From 1cb8291fb9e93810b985ccd52522357ad4bc598b Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 5 Oct 2019 15:47:25 +0200 Subject: [PATCH 001/597] #1933 ignoreByDefault not inherited from config (#1935) --- .../ap/internal/model/source/BeanMapping.java | 2 +- .../ap/test/bugs/_1933/Issue1933Config.java | 35 +++++++++++++++++ .../ap/test/bugs/_1933/Issue1933Mapper.java | 22 +++++++++++ .../ap/test/bugs/_1933/Issue1933Test.java | 39 +++++++++++++++++++ .../builder/ignore/BuilderIgnoringMapper.java | 1 + .../builder/ignore/BuilderIgnoringTest.java | 3 +- .../ignore/inherit/IgnorePropertyTest.java | 4 +- 7 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Config.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java index 5997956ad2..4b291ea71d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java @@ -55,7 +55,7 @@ public static BeanMapping forInheritance(BeanMapping beanMapping) { beanMapping.nullValuePropertyMappingStrategy, beanMapping.nullValueCheckStrategy, beanMapping.reportingPolicy, - false, + beanMapping.ignoreByDefault, beanMapping.ignoreUnmappedSourceProperties, beanMapping.builder, beanMapping.mirror diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Config.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Config.java new file mode 100644 index 0000000000..d971bce47f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Config.java @@ -0,0 +1,35 @@ +/* + * 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._1933; + +import org.mapstruct.BeanMapping; +import org.mapstruct.MapperConfig; +import org.mapstruct.MappingInheritanceStrategy; + +/** + * @author Sjaak Derksen + */ +@MapperConfig(mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG) +public interface Issue1933Config { + + @BeanMapping(ignoreByDefault = true) + Entity updateEntity(Dto dto); + + class Entity { + //CHECKSTYLE:OFF + public String id; + public int updateCount; + //CHECKSTYLE:ON + } + + class Dto { + //CHECKSTYLE:OFF + public String id; + public int updateCount; + //CHECKSTYLE:ON + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Mapper.java new file mode 100644 index 0000000000..fdaa28c1ee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Mapper.java @@ -0,0 +1,22 @@ +/* + * 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._1933; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper(config = Issue1933Config.class) +public interface Issue1933Mapper { + + Issue1933Mapper INSTANCE = Mappers.getMapper( Issue1933Mapper.class ); + + @Mapping(target = "updateCount", source = "updateCount") + Issue1933Config.Entity map(Issue1933Config.Dto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Test.java new file mode 100644 index 0000000000..ae4ecb2e88 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Test.java @@ -0,0 +1,39 @@ +/* + * 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._1933; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Sjaak Derksen + */ +@IssueKey("1933") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue1933Config.class, + Issue1933Mapper.class +}) +public class Issue1933Test { + + @Test + public void shouldIgnoreIdAndMapUpdateCount() { + + Issue1933Config.Dto dto = new Issue1933Config.Dto(); + dto.id = "id"; + dto.updateCount = 5; + + Issue1933Config.Entity entity = Issue1933Mapper.INSTANCE.map( dto ); + + assertThat( entity.id ).isNull(); + assertThat( entity.updateCount ).isEqualTo( 5 ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringMapper.java index 58848b0b8d..9d26b94327 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringMapper.java @@ -20,6 +20,7 @@ public interface BuilderIgnoringMapper { BuilderIgnoringMapper INSTANCE = Mappers.getMapper( BuilderIgnoringMapper.class ); @InheritConfiguration(name = "mapBase") + @Mapping( target = "lastName" ) Person mapWithIgnoringBase(PersonDto source); @BeanMapping(ignoreByDefault = true) diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringTest.java index d179a17ff7..5d28aa3f53 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringTest.java @@ -29,6 +29,7 @@ public class BuilderIgnoringTest { @Test + @IssueKey( "1933" ) public void shouldIgnoreBase() { PersonDto source = new PersonDto(); source.setId( 100L ); @@ -38,7 +39,7 @@ public void shouldIgnoreBase() { Person target = BuilderIgnoringMapper.INSTANCE.mapWithIgnoringBase( source ); assertThat( target.getId() ).isNull(); - assertThat( target.getName() ).isEqualTo( "John" ); + assertThat( target.getName() ).isNull(); assertThat( target.getLastName() ).isEqualTo( "Doe" ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java index d0f829fa4f..7dce466b41 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java @@ -55,7 +55,7 @@ public void shouldIgnoreAllExeptOveriddenInherited() { } @Test - @IssueKey("1392") + @IssueKey("1933") public void shouldIgnoreBase() { WorkBenchDto workBenchDto = new WorkBenchDto(); @@ -67,7 +67,7 @@ public void shouldIgnoreBase() { WorkBenchEntity benchTarget = ToolMapper.INSTANCE.mapBench( workBenchDto ); assertThat( benchTarget ).isNotNull(); - assertThat( benchTarget.getArticleName() ).isEqualTo( "MyBench" ); + assertThat( benchTarget.getArticleName() ).isNull(); assertThat( benchTarget.getDescription() ).isEqualTo( "Beautiful" ); assertThat( benchTarget.getKey() ).isNull(); assertThat( benchTarget.getModificationDate() ).isNull(); From 44ae27d7ca305f4d251914c813fe7084af8adc2f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 6 Oct 2019 13:30:57 +0200 Subject: [PATCH 002/597] #1881 Use ReportingPolicy#IGNORE for unmappedSourcePolicy when mapping references are for forged methods This aligns with the way the ReportingPolicy is handled for unmappedTargetPolicy when mapping references are for forged methods --- .../ap/internal/model/BeanMappingMethod.java | 3 + .../ap/test/bugs/_1881/Issue1881Test.java | 39 ++++++++ .../ap/test/bugs/_1881/VehicleDtoMapper.java | 88 +++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/Issue1881Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/VehicleDtoMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 8a9e4129fe..5089d27177 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -918,6 +918,9 @@ else if ( !unprocessedTargetProperties.isEmpty() && unmappedTargetPolicy.require } private ReportingPolicyPrism getUnmappedSourcePolicy() { + if ( mappingReferences.isForForgedMethods() ) { + return ReportingPolicyPrism.IGNORE; + } MapperConfiguration mapperSettings = MapperConfiguration.getInstanceOn( ctx.getMapperTypeElement() ); return mapperSettings.unmappedSourcePolicy(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/Issue1881Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/Issue1881Test.java new file mode 100644 index 0000000000..6e947a1066 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/Issue1881Test.java @@ -0,0 +1,39 @@ +/* + * 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._1881; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("1881") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + VehicleDtoMapper.class, +}) +public class Issue1881Test { + + @Test + public void shouldCompileCorrectly() { + VehicleDtoMapper.VehicleDto vehicle = VehicleDtoMapper.INSTANCE.map( new VehicleDtoMapper.Vehicle( + "Test", + 100, + "SUV" + ) ); + + assertThat( vehicle.getName() ).isEqualTo( "Test" ); + assertThat( vehicle.getVehicleProperties() ).isNotNull(); + assertThat( vehicle.getVehicleProperties().getSize() ).isEqualTo( 100 ); + assertThat( vehicle.getVehicleProperties().getType() ).isEqualTo( "SUV" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/VehicleDtoMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/VehicleDtoMapper.java new file mode 100644 index 0000000000..34d9f77a75 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/VehicleDtoMapper.java @@ -0,0 +1,88 @@ +/* + * 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._1881; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface VehicleDtoMapper { + + VehicleDtoMapper INSTANCE = Mappers.getMapper( VehicleDtoMapper.class ); + + @Mapping(source = "name", target = "name") + @Mapping(source = "size", target = "vehicleProperties.size") + @Mapping(source = "type", target = "vehicleProperties.type") + VehicleDto map(Vehicle vehicle); + + class VehicleDto { + private String name; + private VehiclePropertiesDto vehicleProperties; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public VehiclePropertiesDto getVehicleProperties() { + return vehicleProperties; + } + + public void setVehicleProperties(VehiclePropertiesDto vehicleProperties) { + this.vehicleProperties = vehicleProperties; + } + } + + class Vehicle { + private final String name; + private final int size; + private final String type; + + public Vehicle(String name, int size, String type) { + this.name = name; + this.size = size; + this.type = type; + } + + public String getName() { + return name; + } + + public int getSize() { + return size; + } + + public String getType() { + return type; + } + } + + class VehiclePropertiesDto { + private int size; + private String type; + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + } +} From 507ec1b3849be3190cad541dd95fc6b9f3304c5d Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Mon, 7 Oct 2019 20:30:20 +0200 Subject: [PATCH 003/597] #1557 & #1936 String-to-enum and enum-to-String (#1938) * #1557 making enum-to-string and string-to-enum * #1936 and not ignored when inverting --- .../java/org/mapstruct/MappingConstants.java | 5 + .../chapter-8-mapping-values.asciidoc | 26 +- .../ap/internal/model/ValueMappingMethod.java | 259 +++++++++++++----- .../ap/internal/model/common/Type.java | 4 + .../model/source/MappingMethodUtils.java | 28 +- .../internal/model/source/ValueMapping.java | 12 +- .../processor/MethodRetrievalProcessor.java | 4 +- .../mapstruct/ap/internal/util/Message.java | 4 +- .../ap/internal/model/EnumMappingMethod.ftl | 44 --- .../ap/internal/model/ValueMappingMethod.ftl | 30 +- .../{ => enum2enum}/DefaultOrderMapper.java | 4 +- .../EnumToEnumMappingTest.java | 14 +- .../ErroneousOrderMapperDuplicateANY.java | 4 +- ...usOrderMapperMappingSameConstantTwice.java | 4 +- ...ppingConstantWithoutMatchInTargetType.java | 4 +- ...sOrderMapperUsingUnknownEnumConstants.java | 4 +- .../test/value/{ => enum2enum}/OrderDto.java | 3 +- .../value/{ => enum2enum}/OrderEntity.java | 3 +- .../value/{ => enum2enum}/OrderMapper.java | 4 +- .../{ => enum2enum}/SpecialOrderMapper.java | 4 +- .../enum2string/EnumToStringMappingTest.java | 60 ++++ ...ErroneousOrderMapperUsingAnyRemaining.java | 29 ++ .../test/value/enum2string/OrderMapper.java | 36 +++ ...erUsingNoAnyRemainingAndNoAnyUnmapped.java | 29 ++ .../test/value/string2enum/OrderMapper.java | 36 +++ .../string2enum/StringToEnumMappingTest.java | 59 ++++ .../DefaultOrderMapperImpl.java | 4 +- .../{ => enum2enum}/OrderMapperImpl.java | 4 +- .../SpecialOrderMapperImpl.java | 4 +- 29 files changed, 572 insertions(+), 153 deletions(-) delete mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/EnumMappingMethod.ftl rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/DefaultOrderMapper.java (81%) rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/EnumToEnumMappingTest.java (97%) rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/ErroneousOrderMapperDuplicateANY.java (86%) rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/ErroneousOrderMapperMappingSameConstantTwice.java (85%) rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java (80%) rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/ErroneousOrderMapperUsingUnknownEnumConstants.java (83%) rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/OrderDto.java (81%) rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/OrderEntity.java (81%) rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/OrderMapper.java (86%) rename processor/src/test/java/org/mapstruct/ap/test/value/{ => enum2enum}/SpecialOrderMapper.java (91%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2string/ErroneousOrderMapperUsingAnyRemaining.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/string2enum/ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/string2enum/OrderMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java rename processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/{ => enum2enum}/DefaultOrderMapperImpl.java (88%) rename processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/{ => enum2enum}/OrderMapperImpl.java (93%) rename processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/{ => enum2enum}/SpecialOrderMapperImpl.java (94%) diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java index 8b0da9a72e..af52d58f26 100644 --- a/core/src/main/java/org/mapstruct/MappingConstants.java +++ b/core/src/main/java/org/mapstruct/MappingConstants.java @@ -23,11 +23,16 @@ private MappingConstants() { /** * In an {@link ValueMapping} this represents any source that is not already mapped by either a defined mapping or * by means of name based mapping. + * + * NOTE: The value is only applicable to {@link ValueMapping#source()} and not to {@link ValueMapping#target()}. */ public static final String ANY_REMAINING = ""; /** * In an {@link ValueMapping} this represents any source that is not already mapped by a defined mapping. + * + * NOTE: The value is only applicable to {@link ValueMapping#source()} and not to {@link ValueMapping#target()}. + * */ public static final String ANY_UNMAPPED = ""; diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index 752c1418a1..7bfd6afa45 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -1,7 +1,7 @@ [[mapping-enum-types]] == Mapping Values -=== Mapping enum types +=== Mapping enum to enum types MapStruct supports the generation of methods which map one Java enum type into another. @@ -67,7 +67,7 @@ public class OrderMapperImpl implements OrderMapper { By default an error will be raised by MapStruct in case a constant of the source enum type does not have a corresponding constant with the same name in the target type and also is not mapped to another constant via `@ValueMapping`. This ensures that all constants are mapped in a safe and predictable manner. The generated mapping method will throw an IllegalStateException if for some reason an unrecognized source value occurs. -MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings. It comes in two flavors: `` and ``. +MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings and only applies to the source. It comes in two flavors: `` and ``. They cannot be used at the same time. In case of source `` MapStruct will continue to map a source enum constant to a target enum constant with the same name. The remainder of the source enum constants will be mapped to the target specified in the `@ValueMapping` with `` source. @@ -80,7 +80,7 @@ MapStruct is able to handle `null` sources and `null` targets by means of the `< Constants for ``, `` and `` are available in the `MappingConstants` class. ==== -Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. +Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. `` and `` will be ignored in that case. .Enum mapping method, and ==== @@ -140,4 +140,22 @@ public class SpecialOrderMapperImpl implements SpecialOrderMapper { [WARNING] ==== The mapping of enum to enum via the `@Mapping` annotation is *DEPRECATED*. It will be removed from future versions of MapStruct. Please adapt existing enum mapping methods to make use of `@ValueMapping` instead. -==== \ No newline at end of file +==== + +=== Mapping enum-to-String or String-to-enum + +MapStruct supports enum to a String mapping along the same lines as is described in <>. There are similarities and differences: + +*enum to `String`* + +1. Similarity: All not explicit defined mappings will result in each source enum constant value being mapped a `String` value with the same constant value. +2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. +3. Difference: `` will result in an error. It acts on the premise that there is name similarity between enum constants in source and target which does not make sense for a String type. +4. Difference: Given 1. and 3. there will never be unmapped values. + +*`String` to enum* + +1. Similarity: All not explicit defined mappings will result in the target enum constant mapped from the `String` value when that maches the target enum constant name. +2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. +3. Similarity: `` will create a mapping for each target enum constant and proceed to the switch/default clause value. +4. Difference: A switch/default value needs to be provided to have a determined outcome (enum has a limited set of values, `String` has unlimited options). Failing to specify `` or ` will result in a warning. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 7c05ac5813..90d5cd38f2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -5,6 +5,9 @@ */ package org.mapstruct.ap.internal.model; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_REMAINING; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_UNMAPPED; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.NULL; import static org.mapstruct.ap.internal.util.Collections.first; import java.util.ArrayList; @@ -16,11 +19,11 @@ import javax.lang.model.util.Types; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.ValueMapping; import org.mapstruct.ap.internal.prism.BeanMappingPrism; -import org.mapstruct.ap.internal.prism.MappingConstantsPrism; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; @@ -42,10 +45,7 @@ public static class Builder { private Method method; private MappingBuilderContext ctx; - private final List trueValueMappings = new ArrayList<>(); - private ValueMapping defaultTargetValue = null; - private ValueMapping nullTargetValue = null; - private boolean applyNamebasedMappings = true; + private ValueMappings valueMappings; public Builder mappingContext(MappingBuilderContext mappingContext) { this.ctx = mappingContext; @@ -58,21 +58,7 @@ public Builder method(Method sourceMethod) { } public Builder valueMappings(List valueMappings) { - for ( ValueMapping valueMapping : valueMappings ) { - if ( MappingConstantsPrism.ANY_REMAINING.equals( valueMapping.getSource() ) ) { - defaultTargetValue = valueMapping; - } - else if ( MappingConstantsPrism.ANY_UNMAPPED.equals( valueMapping.getSource() ) ) { - defaultTargetValue = valueMapping; - applyNamebasedMappings = false; - } - else if ( MappingConstantsPrism.NULL.equals( valueMapping.getSource() ) ) { - nullTargetValue = valueMapping; - } - else { - trueValueMappings.add( valueMapping ); - } - } + this.valueMappings = new ValueMappings( valueMappings ); return this; } @@ -80,27 +66,19 @@ public ValueMappingMethod build( ) { // initialize all relevant parameters List mappingEntries = new ArrayList<>(); - String nullTarget = null; - String defaultTarget = null; - boolean throwIllegalArgumentException = false; - - // for now, we're only dealing with enum mappings, populate relevant parameters based on enum-2-enum - if ( first( method.getSourceParameters() ).getType().isEnumType() && method.getResultType().isEnumType() ) { - mappingEntries.addAll( enumToEnumMapping( method ) ); - if ( (nullTargetValue != null) && !MappingConstantsPrism.NULL.equals( nullTargetValue.getTarget() ) ) { - // absense nulltargetvalue reverts to null. Or it could be a deliberate choice to return null - nullTarget = nullTargetValue.getTarget(); - } - if ( defaultTargetValue != null ) { - // If the default target value is NULL then we should map it to null - defaultTarget = MappingConstantsPrism.NULL.equals( defaultTargetValue.getTarget() ) ? null : - defaultTargetValue.getTarget(); - } - else { - throwIllegalArgumentException = true; - } + Type sourceType = first( method.getSourceParameters() ).getType(); + Type targetType = method.getResultType(); + // enum-to-enum + if ( sourceType.isEnumType() && targetType.isEnumType() ) { + mappingEntries.addAll( enumToEnumMapping( method, sourceType, targetType ) ); + } + else if ( sourceType.isEnumType() && targetType.isString() ) { + mappingEntries.addAll( enumToStringMapping( method, sourceType ) ); + } + else if ( sourceType.isString() && targetType.isEnumType() ) { + mappingEntries.addAll( stringToEnumMapping( method, targetType ) ); } // do before / after lifecycle mappings @@ -112,32 +90,36 @@ public ValueMappingMethod build( ) { LifecycleMethodResolver.afterMappingMethods( method, selectionParameters, ctx, existingVariables ); // finally return a mapping - return new ValueMappingMethod( method, mappingEntries, nullTarget, defaultTarget, - throwIllegalArgumentException, beforeMappingMethods, afterMappingMethods ); + return new ValueMappingMethod( method, + mappingEntries, + valueMappings.nullValueTarget, + valueMappings.defaultTargetValue, + !valueMappings.hasDefaultValue, + beforeMappingMethods, + afterMappingMethods + ); } - private List enumToEnumMapping(Method method) { + private List enumToEnumMapping(Method method, Type sourceType, Type targetType ) { List mappings = new ArrayList<>(); - List unmappedSourceConstants - = new ArrayList<>( first( method.getSourceParameters() ).getType().getEnumConstants() ); - - - if ( !reportErrorIfMappedEnumConstantsDontExist( method ) ) { + List unmappedSourceConstants = new ArrayList<>( sourceType.getEnumConstants() ); + boolean sourceErrorOccurred = !reportErrorIfMappedSourceEnumConstantsDontExist( method, sourceType ); + boolean targetErrorOccurred = !reportErrorIfMappedTargetEnumConstantsDontExist( method, targetType ); + if ( sourceErrorOccurred || targetErrorOccurred ) { return mappings; } - // Start to fill the mappings with the defined valuemappings - for ( ValueMapping valueMapping : trueValueMappings ) { + // Start to fill the mappings with the defined value mappings + for ( ValueMapping valueMapping : valueMappings.regularValueMappings ) { String target = - MappingConstantsPrism.NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); + NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); unmappedSourceConstants.remove( valueMapping.getSource() ); } - // add mappings based on name - if ( applyNamebasedMappings ) { + if ( !valueMappings.hasMapAnyUnmapped ) { // get all target constants List targetConstants = method.getReturnType().getEnumConstants(); @@ -148,7 +130,7 @@ private List enumToEnumMapping(Method method) { } } - if ( defaultTargetValue == null && !unmappedSourceConstants.isEmpty() ) { + if ( valueMappings.defaultTarget == null && !unmappedSourceConstants.isEmpty() ) { String sourceErrorMessage = "source"; String targetErrorMessage = "target"; if ( method instanceof ForgedMethod && ( (ForgedMethod) method ).getHistory() != null ) { @@ -160,7 +142,7 @@ private List enumToEnumMapping(Method method) { // all sources should now be matched, there's no default to fall back to, so if sources remain, // we have an issue. ctx.getMessager().printMessage( method.getExecutable(), - Message.VALUE_MAPPING_UNMAPPED_SOURCES, + Message.VALUEMAPPING_UNMAPPED_SOURCES, sourceErrorMessage, targetErrorMessage, Strings.join( unmappedSourceConstants, ", " ) @@ -171,6 +153,64 @@ private List enumToEnumMapping(Method method) { return mappings; } + private List enumToStringMapping(Method method, Type sourceType ) { + + List mappings = new ArrayList<>(); + List unmappedSourceConstants = new ArrayList<>( sourceType.getEnumConstants() ); + boolean sourceErrorOccurred = !reportErrorIfMappedSourceEnumConstantsDontExist( method, sourceType ); + boolean anyRemainingUsedError = !reportErrorIfSourceEnumConstantsContainsAnyRemaining( method ); + if ( sourceErrorOccurred || anyRemainingUsedError ) { + return mappings; + } + + // Start to fill the mappings with the defined valuemappings + for ( ValueMapping valueMapping : valueMappings.regularValueMappings ) { + String target = + NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); + mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); + unmappedSourceConstants.remove( valueMapping.getSource() ); + } + + // add mappings based on name + if ( !valueMappings.hasMapAnyUnmapped ) { + + // all remaining constants are mapped + for ( String sourceConstant : unmappedSourceConstants ) { + mappings.add( new MappingEntry( sourceConstant, sourceConstant ) ); + } + } + return mappings; + } + + private List stringToEnumMapping(Method method, Type targetType ) { + + List mappings = new ArrayList<>(); + List unmappedSourceConstants = new ArrayList<>( targetType.getEnumConstants() ); + boolean sourceErrorOccurred = !reportErrorIfMappedTargetEnumConstantsDontExist( method, targetType ); + boolean mandatoryMissing = !reportErrorIfAnyRemainingOrAnyUnMappedMissing( method ); + if ( sourceErrorOccurred || mandatoryMissing ) { + return mappings; + } + + // Start to fill the mappings with the defined valuemappings + for ( ValueMapping valueMapping : valueMappings.regularValueMappings ) { + String target = + NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); + mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); + unmappedSourceConstants.remove( valueMapping.getSource() ); + } + + // add mappings based on name + if ( !valueMappings.hasMapAnyUnmapped ) { + + // all remaining constants are mapped + for ( String sourceConstant : unmappedSourceConstants ) { + mappings.add( new MappingEntry( sourceConstant, sourceConstant ) ); + } + } + return mappings; + } + private SelectionParameters getSelectionParameters(Method method, Types typeUtils) { BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( method.getExecutable() ); if ( beanMappingPrism != null ) { @@ -182,13 +222,12 @@ private SelectionParameters getSelectionParameters(Method method, Types typeUtil return null; } - private boolean reportErrorIfMappedEnumConstantsDontExist(Method method) { - List sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants(); - List targetEnumConstants = method.getReturnType().getEnumConstants(); + private boolean reportErrorIfMappedSourceEnumConstantsDontExist(Method method, Type sourceType) { + List sourceEnumConstants = sourceType.getEnumConstants(); boolean foundIncorrectMapping = false; - for ( ValueMapping mappedConstant : trueValueMappings ) { + for ( ValueMapping mappedConstant : valueMappings.regularValueMappings ) { if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -196,11 +235,50 @@ private boolean reportErrorIfMappedEnumConstantsDontExist(Method method) { mappedConstant.getSourceAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, mappedConstant.getSource(), - first( method.getSourceParameters() ).getType() + sourceType ); foundIncorrectMapping = true; } - if ( !MappingConstantsPrism.NULL.equals( mappedConstant.getTarget() ) + } + return !foundIncorrectMapping; + } + + private boolean reportErrorIfSourceEnumConstantsContainsAnyRemaining(Method method) { + boolean foundIncorrectMapping = false; + + if ( valueMappings.hasMapAnyRemaining ) { + ctx.getMessager().printMessage( + method.getExecutable(), + valueMappings.defaultTarget.getMirror(), + valueMappings.defaultTarget.getSourceAnnotationValue(), + Message.VALUEMAPPING_ANY_REMAINING_FOR_NON_ENUM, + method.getResultType() + ); + foundIncorrectMapping = true; + } + return !foundIncorrectMapping; + } + + private boolean reportErrorIfAnyRemainingOrAnyUnMappedMissing(Method method) { + boolean foundIncorrectMapping = false; + + if ( !( valueMappings.hasMapAnyUnmapped || valueMappings.hasMapAnyRemaining ) ) { + ctx.getMessager().printMessage( + method.getExecutable(), + Message.VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING + ); + foundIncorrectMapping = true; + } + return !foundIncorrectMapping; + } + + private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, Type targetType) { + List targetEnumConstants = targetType.getEnumConstants(); + + boolean foundIncorrectMapping = false; + + for ( ValueMapping mappedConstant : valueMappings.regularValueMappings ) { + if ( !NULL.equals( mappedConstant.getTarget() ) && !targetEnumConstants.contains( mappedConstant.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), mappedConstant.getMirror(), @@ -213,25 +291,25 @@ private boolean reportErrorIfMappedEnumConstantsDontExist(Method method) { } } - if ( defaultTargetValue != null && !MappingConstantsPrism.NULL.equals( defaultTargetValue.getTarget() ) - && !targetEnumConstants.contains( defaultTargetValue.getTarget() ) ) { + if ( valueMappings.defaultTarget != null && !NULL.equals( valueMappings.defaultTarget.getTarget() ) + && !targetEnumConstants.contains( valueMappings.defaultTarget.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), - defaultTargetValue.getMirror(), - defaultTargetValue.getTargetAnnotationValue(), + valueMappings.defaultTarget.getMirror(), + valueMappings.defaultTarget.getTargetAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, - defaultTargetValue.getTarget(), + valueMappings.defaultTarget.getTarget(), method.getReturnType() ); foundIncorrectMapping = true; } - if ( nullTargetValue != null && MappingConstantsPrism.NULL.equals( nullTargetValue.getTarget() ) - && !targetEnumConstants.contains( nullTargetValue.getTarget() ) ) { + if ( valueMappings.nullTarget != null && NULL.equals( valueMappings.nullTarget.getTarget() ) + && !targetEnumConstants.contains( valueMappings.nullTarget.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), - nullTargetValue.getMirror(), - nullTargetValue.getTargetAnnotationValue(), + valueMappings.nullTarget.getMirror(), + valueMappings.nullTarget.getTargetAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, - nullTargetValue.getTarget(), + valueMappings.nullTarget.getTarget(), method.getReturnType() ); foundIncorrectMapping = true; @@ -241,6 +319,47 @@ private boolean reportErrorIfMappedEnumConstantsDontExist(Method method) { } } + private static class ValueMappings { + + List regularValueMappings = new ArrayList<>(); + ValueMapping defaultTarget = null; + String defaultTargetValue = null; + ValueMapping nullTarget = null; + String nullValueTarget = null; + boolean hasMapAnyUnmapped = false; + boolean hasMapAnyRemaining = false; + boolean hasDefaultValue = false; + + ValueMappings(List valueMappings) { + + for ( ValueMapping valueMapping : valueMappings ) { + if ( ANY_REMAINING.equals( valueMapping.getSource() ) ) { + defaultTarget = valueMapping; + defaultTargetValue = getValue( defaultTarget ); + hasDefaultValue = true; + hasMapAnyRemaining = true; + } + else if ( ANY_UNMAPPED.equals( valueMapping.getSource() ) ) { + defaultTarget = valueMapping; + defaultTargetValue = getValue( defaultTarget ); + hasDefaultValue = true; + hasMapAnyUnmapped = true; + } + else if ( NULL.equals( valueMapping.getSource() ) ) { + nullTarget = valueMapping; + nullValueTarget = getValue( nullTarget ); + } + else { + regularValueMappings.add( valueMapping ); + } + } + } + + String getValue(ValueMapping valueMapping) { + return NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); + } + } + private ValueMappingMethod(Method method, List enumMappings, String nullTarget, String defaultTarget, boolean throwIllegalArgumentException, List beforeMappingMethods, List afterMappingMethods) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index c21906f68f..da2bfc76fa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -220,6 +220,10 @@ public boolean isAbstract() { return typeElement != null && typeElement.getModifiers().contains( Modifier.ABSTRACT ); } + public boolean isString() { + return String.class.getName().equals( getFullyQualifiedName() ); + } + /** * @return this type's enum constants in case it is an enum, an empty list otherwise. */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java index a471e1c6af..50ea5df6e0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java @@ -5,6 +5,8 @@ */ package org.mapstruct.ap.internal.model.source; +import org.mapstruct.ap.internal.model.common.Type; + import static org.mapstruct.ap.internal.util.Collections.first; /** @@ -18,18 +20,34 @@ public final class MappingMethodUtils { private MappingMethodUtils() { } - /** * Checks if the provided {@code method} is for enum mapping. A Method is an Enum Mapping method when the - * source parameter and result type are enum types. + *
    + *
  1. source parameter type and result type are enum types
  2. + *
  3. source parameter type is a String and result type is an enum type
  4. + *
  5. source parameter type is a enum type and result type is a String
  6. + *
* * @param method to check * * @return {@code true} if the method is for enum mapping, {@code false} otherwise */ public static boolean isEnumMapping(Method method) { - return method.getSourceParameters().size() == 1 - && first( method.getSourceParameters() ).getType().isEnumType() - && method.getResultType().isEnumType(); + if ( method.getSourceParameters().size() != 1 ) { + return false; + } + + Type source = first( method.getSourceParameters() ).getType(); + Type result = method.getResultType(); + if ( source.isEnumType() && result.isEnumType() ) { + return true; + } + if ( source.isString() && result.isEnumType() ) { + return true; + } + if ( source.isEnumType() && result.isString() ) { + return true; + } + return false; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java index 8902d9e8fb..cd0f0a4315 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java @@ -7,17 +7,18 @@ import java.util.List; import java.util.Objects; - import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; -import org.mapstruct.ap.internal.prism.MappingConstantsPrism; import org.mapstruct.ap.internal.prism.ValueMappingPrism; import org.mapstruct.ap.internal.prism.ValueMappingsPrism; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_REMAINING; +import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_UNMAPPED; + /** * Represents the mapping between one value constant and another. * @@ -51,8 +52,8 @@ public static void fromMappingsPrism(ValueMappingsPrism mappingsAnnotation, Exec mappingPrism.source() ); } - if ( MappingConstantsPrism.ANY_REMAINING.equals( mapping.source ) - || MappingConstantsPrism.ANY_UNMAPPED.equals( mapping.source ) ) { + if ( ANY_REMAINING.equals( mapping.source ) + || ANY_UNMAPPED.equals( mapping.source ) ) { if ( anyFound ) { messager.printMessage( method, @@ -111,8 +112,7 @@ public AnnotationValue getTargetAnnotationValue() { public ValueMapping inverse() { ValueMapping result; - if ( !MappingConstantsPrism.ANY_REMAINING.equals( source ) - || !MappingConstantsPrism.ANY_UNMAPPED.equals( source ) ) { + if ( !(ANY_REMAINING.equals( source ) || ANY_UNMAPPED.equals( source ) ) ) { result = new ValueMapping( target, source, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index e7e2db260e..068aa240fb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -461,12 +461,12 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List\" or \"\" can only be used once." ), - VALUE_MAPPING_UNMAPPED_SOURCES( "The following constants from the %s enum have no corresponding constant in the %s enum and must be be mapped via adding additional mappings: %s." ), + VALUEMAPPING_UNMAPPED_SOURCES( "The following constants from the %s enum have no corresponding constant in the %s enum and must be be mapped via adding additional mappings: %s." ), + VALUEMAPPING_ANY_REMAINING_FOR_NON_ENUM( "Source = \"\" can only be used on targets of type enum and not for %s." ), + VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING( "Source = \"\" or \"\" is advisable for mapping of type String to an enum type.", Diagnostic.Kind.WARNING ), VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." ); // CHECKSTYLE:ON diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/EnumMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/EnumMappingMethod.ftl deleted file mode 100644 index 3d0c9e074b..0000000000 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/EnumMappingMethod.ftl +++ /dev/null @@ -1,44 +0,0 @@ -<#-- - - Copyright MapStruct Authors. - - Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - ---> -<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.EnumMappingMethod" --> -@Override -public <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, ) { - <#list beforeMappingReferencesWithoutMappingTarget as callback> - <@includeModel object=callback targetBeanName=resultName targetType=resultType/> - <#if !callback_has_next> - - - - if ( ${sourceParameter.name} == null ) { - return null; - } - - <@includeModel object=resultType/> ${resultName}; - - switch ( ${sourceParameter.name} ) { - <#list enumMappings as enumMapping> - case ${enumMapping.source}: ${resultName} = <@includeModel object=returnType/>.${enumMapping.target}; - break; - - default: throw new IllegalArgumentException( "Unexpected enum constant: " + ${sourceParameter.name} ); - } - <#list beforeMappingReferencesWithMappingTarget as callback> - <#if callback_index = 0> - - - <@includeModel object=callback targetBeanName=resultName targetType=resultType/> - - <#list afterMappingReferences as callback> - <#if callback_index = 0> - - - <@includeModel object=callback targetBeanName=resultName targetType=resultType/> - - - return ${resultName}; -} diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl index 5fa1c0a118..ba16d56a2d 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -15,17 +15,17 @@ if ( ${sourceParameter.name} == null ) { - return <#if nullTarget??><@includeModel object=returnType/>.${nullTarget}<#else>null; + return <@writeTarget target=nullTarget/>; } <@includeModel object=resultType/> ${resultName}; switch ( ${sourceParameter.name} ) { <#list valueMappings as valueMapping> - case ${valueMapping.source}: ${resultName} = <#if valueMapping.target??><@includeModel object=returnType/>.${valueMapping.target}<#else>null; + case <@writeSource source=valueMapping.source/>: ${resultName} = <@writeTarget target=valueMapping.target/>; break; - default: <#if throwIllegalArgumentException>throw new IllegalArgumentException( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <#if defaultTarget??><@includeModel object=returnType/>.${defaultTarget}<#else>null; + default: <#if throwIllegalArgumentException>throw new IllegalArgumentException( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/>; } <#list beforeMappingReferencesWithMappingTarget as callback> <#if callback_index = 0> @@ -40,5 +40,29 @@ <@includeModel object=callback targetBeanName=resultName targetType=resultType/> + <#if !(valueMappings.empty && throwIllegalArgumentException)> return ${resultName}; + } +<#macro writeSource source=""> + <@compress single_line=true> + <#if sourceParameter.type.enumType> + ${source} + <#elseif sourceParameter.type.string> + "${source}" + + + +<#macro writeTarget target=""> + <@compress single_line=true> + <#if target?has_content> + <#if returnType.enumType> + <@includeModel object=returnType/>.${target} + <#elseif returnType.string> + "${target}" + + <#else> + null + + + \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/DefaultOrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapper.java similarity index 81% rename from processor/src/test/java/org/mapstruct/ap/test/value/DefaultOrderMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapper.java index 6228e4b619..4e46a4a020 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/DefaultOrderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapper.java @@ -3,11 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.Mapper; import org.mapstruct.MappingConstants; import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/EnumToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java similarity index 97% rename from processor/src/test/java/org/mapstruct/ap/test/value/EnumToEnumMappingTest.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java index aa1e0ea02c..51f867947a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/EnumToEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import static org.assertj.core.api.Assertions.assertThat; @@ -12,6 +12,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; @@ -214,7 +216,7 @@ public void shouldMapAnyRemainingToNullCorrectly() { diagnostics = { @Diagnostic(type = ErroneousOrderMapperMappingSameConstantTwice.class, kind = Kind.ERROR, - line = 25, + line = 27, messageRegExp = "Source value mapping: \"EXTRA\" cannot be mapped more than once\\.") } ) @@ -228,11 +230,11 @@ public void shouldRaiseErrorIfSameSourceEnumConstantIsMappedTwice() { diagnostics = { @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, kind = Kind.ERROR, - line = 24, + line = 26, messageRegExp = "Constant FOO doesn't exist in enum type org.mapstruct.ap.test.value.OrderType\\."), @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, kind = Kind.ERROR, - line = 25, + line = 27, messageRegExp = "Constant BAR doesn't exist in enum type org.mapstruct.ap.test.value." + "ExternalOrderType\\.") } @@ -247,7 +249,7 @@ public void shouldRaiseErrorIfUnknownEnumConstantsAreSpecifiedInMapping() { diagnostics = { @Diagnostic(type = ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.class, kind = Kind.ERROR, - line = 21, + line = 23, messageRegExp = "The following constants from the source enum have no corresponding constant in the " + "target enum and must be be mapped via adding additional mappings: EXTRA, STANDARD, NORMAL") } @@ -262,7 +264,7 @@ public void shouldRaiseErrorIfSourceConstantWithoutMatchingConstantInTargetTypeI diagnostics = { @Diagnostic(type = ErroneousOrderMapperDuplicateANY.class, kind = Kind.ERROR, - line = 26, + line = 28, messageRegExp = "Source = \"\" or \"\" can only be used once\\." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperDuplicateANY.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperDuplicateANY.java similarity index 86% rename from processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperDuplicateANY.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperDuplicateANY.java index 72989be31a..d6d9076b2f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperDuplicateANY.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperDuplicateANY.java @@ -3,11 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.Mapper; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperMappingSameConstantTwice.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperMappingSameConstantTwice.java similarity index 85% rename from processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperMappingSameConstantTwice.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperMappingSameConstantTwice.java index 62c6bc3341..cc43413942 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperMappingSameConstantTwice.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperMappingSameConstantTwice.java @@ -3,11 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.Mapper; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java similarity index 80% rename from processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java index d39d579098..767522ffd0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java @@ -3,9 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.Mapper; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperUsingUnknownEnumConstants.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperUsingUnknownEnumConstants.java similarity index 83% rename from processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperUsingUnknownEnumConstants.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperUsingUnknownEnumConstants.java index 49569f1fa3..6dacf98c73 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperUsingUnknownEnumConstants.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperUsingUnknownEnumConstants.java @@ -3,11 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.Mapper; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderDto.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderDto.java similarity index 81% rename from processor/src/test/java/org/mapstruct/ap/test/value/OrderDto.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderDto.java index d0eb104a5f..ef756c5def 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/OrderDto.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderDto.java @@ -3,8 +3,9 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; +import org.mapstruct.ap.test.value.ExternalOrderType; /** * @author Gunnar Morling diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderEntity.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderEntity.java similarity index 81% rename from processor/src/test/java/org/mapstruct/ap/test/value/OrderEntity.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderEntity.java index 62a5f41489..247db83d41 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/OrderEntity.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderEntity.java @@ -3,8 +3,9 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; +import org.mapstruct.ap.test.value.OrderType; /** * @author Gunnar Morling diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderMapper.java similarity index 86% rename from processor/src/test/java/org/mapstruct/ap/test/value/OrderMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderMapper.java index 82996f73d6..e30d0cfc78 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/OrderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderMapper.java @@ -3,12 +3,14 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/SpecialOrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java similarity index 91% rename from processor/src/test/java/org/mapstruct/ap/test/value/SpecialOrderMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java index 29767c1a6f..46ffb8b1cf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/SpecialOrderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; @@ -12,6 +12,8 @@ import org.mapstruct.Named; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java new file mode 100644 index 0000000000..2f1be53125 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java @@ -0,0 +1,60 @@ +/* + * 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.value.enum2string; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey( "1557" ) +@WithClasses({ OrderType.class, OrderMapper.class }) +@RunWith(AnnotationProcessorTestRunner.class) +public class EnumToStringMappingTest { + + @Test + public void testTheNormalStuff() { + assertThat( OrderMapper.INSTANCE.mapNormal( null ) ).isNull(); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.EXTRA ) ).isEqualTo( "SPECIAL" ); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.STANDARD ) ).isEqualTo( "DEFAULT" ); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.NORMAL ) ).isEqualTo( "DEFAULT" ); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.B2B ) ).isEqualTo( "B2B" ); + assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.RETAIL ) ).isEqualTo( "RETAIL" ); + } + + @Test + public void testRemainingAndNull() { + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( null ) ).isEqualTo( "DEFAULT" ); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.STANDARD ) ).isNull(); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.NORMAL ) ).isEqualTo( "SPECIAL" ); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.B2B ) ).isEqualTo( "SPECIAL" ); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.RETAIL ) ).isEqualTo( "SPECIAL" ); + assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.EXTRA ) ).isEqualTo( "SPECIAL" ); + } + + @Test + @WithClasses(ErroneousOrderMapperUsingAnyRemaining.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperUsingAnyRemaining.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 26, + messageRegExp = "\"\" can only be used on targets of type enum and not for " + + "java\\.lang\\.String\\.") + } + ) + public void shouldRaiseErrorWhenUsingAnyRemaining() { + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/ErroneousOrderMapperUsingAnyRemaining.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/ErroneousOrderMapperUsingAnyRemaining.java new file mode 100644 index 0000000000..536586529f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/ErroneousOrderMapperUsingAnyRemaining.java @@ -0,0 +1,29 @@ +/* + * 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.value.enum2string; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ErroneousOrderMapperUsingAnyRemaining { + + ErroneousOrderMapperUsingAnyRemaining INSTANCE = Mappers.getMapper( ErroneousOrderMapperUsingAnyRemaining.class ); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ), + @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" ) + }) + String mapWithRemainingAndNull(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java new file mode 100644 index 0000000000..35d9f57f90 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java @@ -0,0 +1,36 @@ +/* + * 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.value.enum2string; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface OrderMapper { + + OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); + + @ValueMappings({ + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = "DEFAULT") + }) + String mapNormal(OrderType orderType); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ), + @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_UNMAPPED, target = "SPECIAL" ) + }) + String withAnyUnmappedAndNull(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.java b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.java new file mode 100644 index 0000000000..06e9c05c96 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.java @@ -0,0 +1,29 @@ +/* + * 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.value.string2enum; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped { + + ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped INSTANCE = + Mappers.getMapper( ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.class ); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "STANDARD" ), + @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ) + }) + OrderType map(String orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/OrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/OrderMapper.java new file mode 100644 index 0000000000..9c43835f47 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/OrderMapper.java @@ -0,0 +1,36 @@ +/* + * 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.value.string2enum; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface OrderMapper { + + OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); + + @ValueMappings({ + @ValueMapping(source = "SPECIAL", target = "EXTRA" ), + @ValueMapping(source = "DEFAULT", target = "STANDARD"), + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "RETAIL" ) + }) + OrderType mapNormal(String orderType); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "STANDARD" ), + @ValueMapping( source = "DEFAULT", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_UNMAPPED, target = "RETAIL" ) + }) + OrderType mapWithAnyUnmapped(String orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java new file mode 100644 index 0000000000..552766d240 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.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.value.string2enum; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey( "1557" ) +@WithClasses({ OrderType.class, OrderMapper.class }) +@RunWith(AnnotationProcessorTestRunner.class) +public class StringToEnumMappingTest { + + @Test + public void testTheNormalStuff() { + assertThat( OrderMapper.INSTANCE.mapNormal( null ) ).isNull(); + assertThat( OrderMapper.INSTANCE.mapNormal( "SPECIAL" ) ).isEqualTo( OrderType.EXTRA ); + assertThat( OrderMapper.INSTANCE.mapNormal( "DEFAULT" ) ).isEqualTo( OrderType.STANDARD ); + assertThat( OrderMapper.INSTANCE.mapNormal( "RETAIL" ) ).isEqualTo( OrderType.RETAIL ); + assertThat( OrderMapper.INSTANCE.mapNormal( "B2B" ) ).isEqualTo( OrderType.B2B ); + assertThat( OrderMapper.INSTANCE.mapNormal( "STANDARD" ) ).isEqualTo( OrderType.STANDARD ); + assertThat( OrderMapper.INSTANCE.mapNormal( "NORMAL" ) ).isEqualTo( OrderType.NORMAL ); + assertThat( OrderMapper.INSTANCE.mapNormal( "blah" ) ).isEqualTo( OrderType.RETAIL ); + } + + @Test + public void testRemainingAndNull() { + assertThat( OrderMapper.INSTANCE.mapWithAnyUnmapped( null ) ).isEqualTo( OrderType.STANDARD ); + assertThat( OrderMapper.INSTANCE.mapWithAnyUnmapped( "DEFAULT" ) ).isNull(); + assertThat( OrderMapper.INSTANCE.mapWithAnyUnmapped( "BLAH" ) ).isEqualTo( OrderType.RETAIL ); + } + + @Test + @WithClasses(ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.class) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 28, + messageRegExp = "Source = \"\" or \"\" is advisable for mapping of " + + "type String to an enum type" ) + } + ) + public void shouldRaiseErrorWhenUsingAnyRemaining() { + } + +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/DefaultOrderMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapperImpl.java similarity index 88% rename from processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/DefaultOrderMapperImpl.java rename to processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapperImpl.java index 3818038de2..fe44bab440 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/DefaultOrderMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/DefaultOrderMapperImpl.java @@ -3,9 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import javax.annotation.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; @Generated( value = "org.mapstruct.ap.MappingProcessor", diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/OrderMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/OrderMapperImpl.java similarity index 93% rename from processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/OrderMapperImpl.java rename to processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/OrderMapperImpl.java index dbcd2cb012..31f6b6989b 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/OrderMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/OrderMapperImpl.java @@ -3,9 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import javax.annotation.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; @Generated( value = "org.mapstruct.ap.MappingProcessor", diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/SpecialOrderMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java similarity index 94% rename from processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/SpecialOrderMapperImpl.java rename to processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java index 8ff732ae2a..f420a63baf 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/SpecialOrderMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java @@ -3,9 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.value; +package org.mapstruct.ap.test.value.enum2enum; import javax.annotation.Generated; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; @Generated( value = "org.mapstruct.ap.MappingProcessor", From 5fbc86d92f93ec08cb3dde56711dcb4414bbc3fb Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Tue, 8 Oct 2019 21:06:47 +0200 Subject: [PATCH 004/597] #1788 allowing enum / not enum by removing checks (#1939) --- .../processor/MethodRetrievalProcessor.java | 10 -------- .../mapstruct/ap/internal/util/Message.java | 2 -- .../ap/test/bugs/_1788/Issue1788Mapper.java | 24 +++++++++++++++++++ .../ap/test/bugs/_1788/Issue1788Test.java | 24 +++++++++++++++++++ 4 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1788/Issue1788Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1788/Issue1788Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 068aa240fb..cc1b0c350b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -461,16 +461,6 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List Date: Sun, 20 Oct 2019 14:13:38 +0300 Subject: [PATCH 005/597] #1944 Remove unused constants in JavaStreamConstants (#1945) --- .../org/mapstruct/ap/internal/util/JavaStreamConstants.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JavaStreamConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JavaStreamConstants.java index f23e89435a..acd63abcd7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/JavaStreamConstants.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JavaStreamConstants.java @@ -13,10 +13,6 @@ public final class JavaStreamConstants { public static final String STREAM_FQN = "java.util.stream.Stream"; - public static final String COLLECTORS_FQN = "java.util.stream.Collectors"; - public static final String STREAM_SUPPORT_FQN = "java.util.stream.StreamSupport"; - public static final String OPTIONAL_FQN = "java.util.Optional"; - public static final String FUNCTION_FQN = "java.util.function.Function"; private JavaStreamConstants() { } From e8a7832d5b6c8ecd776814e30786f3e1f81454f1 Mon Sep 17 00:00:00 2001 From: Andrei Arlou Date: Sun, 20 Oct 2019 14:16:26 +0300 Subject: [PATCH 006/597] #1948 Fix minor warnings with using collection in package model and class util/workarounds/EclipseAsMemberOfWorkaround (#1949) --- .../mapstruct/ap/internal/model/BeanMappingMethod.java | 8 ++------ .../model/LifecycleCallbackMethodReference.java | 10 ++-------- .../ap/internal/model/ObjectFactoryMethodResolver.java | 2 +- .../model/source/ParameterProvidedMethods.java | 2 +- .../ap/internal/model/source/SourceMethod.java | 8 +------- .../util/workarounds/EclipseAsMemberOfWorkaround.java | 3 +-- 6 files changed, 8 insertions(+), 25 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 5089d27177..458804bee1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -8,7 +8,6 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -371,11 +370,8 @@ private void sortPropertyMappingsByDependencies() { ); } else { - Collections.sort( - propertyMappings, - Comparator.comparingInt( propertyMapping -> - graphAnalyzer.getTraversalSequence( propertyMapping.getName() ) ) - ); + propertyMappings.sort( Comparator.comparingInt( propertyMapping -> + graphAnalyzer.getTraversalSequence( propertyMapping.getName() ) ) ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleCallbackMethodReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleCallbackMethodReference.java index 10fb2d9c77..c47fd258c0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleCallbackMethodReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleCallbackMethodReference.java @@ -80,17 +80,11 @@ public String getTargetVariableName() { @Override public Set getImportTypes() { - return declaringType != null ? Collections.asSet( declaringType ) : java.util.Collections. emptySet(); + return declaringType != null ? Collections.asSet( declaringType ) : java.util.Collections.emptySet(); } public boolean hasMappingTargetParameter() { - for ( ParameterBinding param : getParameterBindings() ) { - if ( param.isMappingTarget() ) { - return true; - } - } - - return false; + return getParameterBindings().stream().anyMatch( ParameterBinding::isMappingTarget ); } /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index 5a0f72fb50..b64dd88884 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -76,7 +76,7 @@ public static MethodReference getFactoryMethod( Method method, selectors.getMatchingMethods( method, getAllAvailableMethods( method, ctx.getSourceModel() ), - java.util.Collections. emptyList(), + java.util.Collections.emptyList(), alternativeTarget, SelectionCriteria.forFactoryMethods( selectionParameters ) ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ParameterProvidedMethods.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ParameterProvidedMethods.java index 28817bc43f..6552e43505 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ParameterProvidedMethods.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ParameterProvidedMethods.java @@ -23,7 +23,7 @@ */ public class ParameterProvidedMethods { private static final ParameterProvidedMethods EMPTY = - new ParameterProvidedMethods( Collections.> emptyMap() ); + new ParameterProvidedMethods( Collections.emptyMap() ); private final Map> parameterToProvidedMethods; private final Map methodToProvidingParameter; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 32bd540f10..ae6760ae2d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -454,13 +454,7 @@ public boolean matches(List sourceTypes, Type targetType) { * @return {@code true} if the parameter list contains a parameter annotated with {@code @TargetType} */ public static boolean containsTargetTypeParameter(List parameters) { - for ( Parameter param : parameters ) { - if ( param.isTargetType() ) { - return true; - } - } - - return false; + return parameters.stream().anyMatch( Parameter::isTargetType ); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseAsMemberOfWorkaround.java b/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseAsMemberOfWorkaround.java index 739246a6c6..ab0d681728 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseAsMemberOfWorkaround.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseAsMemberOfWorkaround.java @@ -6,7 +6,6 @@ package org.mapstruct.ap.internal.util.workarounds; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; @@ -73,7 +72,7 @@ static TypeMirror asMemberOf(ProcessingEnvironment environment, DeclaredType con candidatesFromInterfaces ); // there can be multiple matches for the same method name from adjacent interface hierarchies. - Collections.sort( candidatesFromInterfaces, MostSpecificMethodBindingComparator.INSTANCE ); + candidatesFromInterfaces.sort( MostSpecificMethodBindingComparator.INSTANCE ); if ( !candidatesFromInterfaces.isEmpty() ) { // return the most specific match From 74a2e358e8f8ef04061e9ea290a07352c3e7d28e Mon Sep 17 00:00:00 2001 From: Andrei Arlou Date: Sat, 19 Oct 2019 01:35:59 +0300 Subject: [PATCH 007/597] #1946 Simplify conditions in classes: Parameter, SelectionParameters, MethodReference, PropertyMapping --- .../ap/internal/model/MethodReference.java | 20 ++++++------------- .../ap/internal/model/PropertyMapping.java | 8 ++++---- .../ap/internal/model/common/Parameter.java | 5 +++-- .../model/source/SelectionParameters.java | 14 +++---------- 4 files changed, 16 insertions(+), 31 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java index 69b9b81dc1..dc2bf52182 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java @@ -9,6 +9,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -84,7 +85,7 @@ protected MethodReference(Method method, MapperReference declaringMapper, Parame imported.addAll( binding.getImportTypes() ); } - this.importTypes = Collections.unmodifiableSet( imported ); + this.importTypes = Collections.unmodifiableSet( imported ); this.thrownTypes = method.getThrownTypes(); this.isUpdateMethod = method.getMappingTargetParameter() != null; this.definingType = method.getDefiningType(); @@ -304,22 +305,13 @@ public boolean equals(Object obj) { return false; } MethodReference other = (MethodReference) obj; - if ( declaringMapper == null ) { - if ( other.declaringMapper != null ) { - return false; - } - } - else if ( !declaringMapper.equals( other.declaringMapper ) ) { + if ( !Objects.equals( declaringMapper, other.declaringMapper ) ) { return false; } - if ( providingParameter == null ) { - if ( other.providingParameter != null ) { - return false; - } - } - else if ( !providingParameter.equals( other.providingParameter ) ) { + if ( !Objects.equals( providingParameter, other.providingParameter ) ) { return false; } + return true; } @@ -351,7 +343,7 @@ public static MethodReference forMethodCall(String methodName) { @Override public String toString() { - String mapper = declaringMapper != null ? declaringMapper.getType().getName().toString() : ""; + String mapper = declaringMapper != null ? declaringMapper.getType().getName() : ""; String argument = getAssignment() != null ? getAssignment().toString() : ( getSourceReference() != null ? getSourceReference() : "" ); String returnTypeAsString = returnType != null ? returnType.toString() : ""; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 7fc619bac1..3bec3eb461 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -9,6 +9,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; import javax.lang.model.element.AnnotationMirror; @@ -1026,7 +1027,6 @@ public Assignment getDefaultValueAssignment() { } @Override - @SuppressWarnings("unchecked") public Set getImportTypes() { if ( defaultValueAssignment == null ) { return assignment.getImportTypes(); @@ -1062,13 +1062,13 @@ public boolean equals(Object obj) { return false; } final PropertyMapping other = (PropertyMapping) obj; - if ( (this.name == null) ? (other.name != null) : !this.name.equals( other.name ) ) { + if ( !Objects.equals( name, other.name ) ) { return false; } - if ( this.targetType != other.targetType && (this.targetType == null || - !this.targetType.equals( other.targetType )) ) { + if ( !Objects.equals( targetType, other.targetType ) ) { return false; } + return true; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java index f93ae8095a..0f3000913b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.model.common; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import javax.lang.model.element.VariableElement; @@ -105,10 +106,10 @@ public boolean equals(Object o) { Parameter parameter = (Parameter) o; - if ( name != null ? !name.equals( parameter.name ) : parameter.name != null ) { + if ( !Objects.equals( name, parameter.name ) ) { return false; } - return type != null ? type.equals( parameter.type ) : parameter.type == null; + return Objects.equals( type, parameter.type ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java index 34eb0a7d80..ed6851d9a1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.List; +import java.util.Objects; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; @@ -114,26 +115,17 @@ public boolean equals(Object obj) { return false; } - if ( !equals( this.qualifyingNames, other.qualifyingNames ) ) { + if ( !Objects.equals( this.qualifyingNames, other.qualifyingNames ) ) { return false; } - if ( !equals( this.sourceRHS, other.sourceRHS ) ) { + if ( !Objects.equals( this.sourceRHS, other.sourceRHS ) ) { return false; } return equals( this.resultType, other.resultType ); } - private boolean equals(Object object1, Object object2) { - if ( object1 == null ) { - return (object2 == null); - } - else { - return object1.equals( object2 ); - } - } - private boolean equals(List mirrors1, List mirrors2) { if ( mirrors1 == null ) { return (mirrors2 == null); From efea2fb6625592994a851af8436fbf68fc1b62d4 Mon Sep 17 00:00:00 2001 From: Andrei Arlou Date: Sun, 20 Oct 2019 22:11:17 +0300 Subject: [PATCH 008/597] #1951 Use Map.computeIfAbsent in NestedTargetPropertyMappingHolder (#1952) --- .../NestedTargetPropertyMappingHolder.java | 28 +++++++------------ .../ap/internal/model/common/Type.java | 12 ++++---- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index 10b3d47363..3c8b1bec61 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -352,16 +352,12 @@ private GroupedTargetReferences groupByTargetReferences( ) { MappingReference newMapping = mapping.popTargetReference(); if ( newMapping != null ) { // group properties on current name. - if ( !mappingsKeyedByProperty.containsKey( property ) ) { - mappingsKeyedByProperty.put( property, new LinkedHashSet<>() ); - } - mappingsKeyedByProperty.get( property ).add( newMapping ); + mappingsKeyedByProperty.computeIfAbsent( property, propertyEntry -> new LinkedHashSet<>() ) + .add( newMapping ); } else { - if ( !singleTargetReferences.containsKey( property ) ) { - singleTargetReferences.put( property, new LinkedHashSet<>() ); - } - singleTargetReferences.get( property ).add( mapping ); + singleTargetReferences.computeIfAbsent( property, propertyEntry -> new LinkedHashSet<>() ) + .add( mapping ); } } @@ -459,10 +455,8 @@ private GroupedBySourceParameters groupBySourceParameter(Set m for ( MappingReference mapping : mappings ) { if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) { Parameter parameter = mapping.getSourceReference().getParameter(); - if ( !mappingsKeyedByParameter.containsKey( parameter ) ) { - mappingsKeyedByParameter.put( parameter, new LinkedHashSet<>() ); - } - mappingsKeyedByParameter.get( parameter ).add( mapping ); + mappingsKeyedByParameter.computeIfAbsent( parameter, key -> new LinkedHashSet<>() ) + .add( mapping ); } else { appliesToAll.add( mapping ); @@ -530,10 +524,8 @@ private GroupedSourceReferences groupByPoppedSourceReferences( if ( newMapping != null ) { // group properties on current name. PropertyEntry property = first( mapping.getSourceReference().getPropertyEntries() ); - if ( !mappingsKeyedByProperty.containsKey( property ) ) { - mappingsKeyedByProperty.put( property, new LinkedHashSet<>() ); - } - mappingsKeyedByProperty.get( property ).add( newMapping ); + mappingsKeyedByProperty.computeIfAbsent( property, propertyEntry -> new LinkedHashSet<>() ) + .add( newMapping ); } //This is an ignore, or some expression, or a default. We apply these to all else if ( mapping.getSourceReference() == null ) { @@ -666,8 +658,8 @@ private void populateWithSingleTargetReferences(Map for ( MappingReference mapping : singleTargetReferences ) { if ( mapping.getSourceReference() != null && mapping.getSourceReference().isValid() ) { K key = keyExtractor.apply( mapping.getSourceReference() ); - if ( key != null && !map.containsKey( key ) ) { - map.put( key, new LinkedHashSet<>() ); + if ( key != null ) { + map.computeIfAbsent( key, keyValue -> new LinkedHashSet<>() ); } } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index da2bfc76fa..0a448c88ad 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -485,23 +485,21 @@ public Map getPropertyReadAccessors() { // In the DefaultAccessorNamingStrategy, this can only be the case for Booleans: isFoo() and // getFoo(); The latter is preferred. if ( !getter.getSimpleName().toString().startsWith( "is" ) ) { - modifiableGetters.put( getPropertyName( getter ), getter ); + modifiableGetters.put( propertyName, getter ); } } else { - modifiableGetters.put( getPropertyName( getter ), getter ); + modifiableGetters.put( propertyName, getter ); } } List fieldsList = filters.fieldsIn( getAllFields() ); for ( Accessor field : fieldsList ) { String propertyName = getPropertyName( field ); - if ( !modifiableGetters.containsKey( propertyName ) ) { - // If there was no getter or is method for booleans, then resort to the field. - // If a field was already added do not add it again. - modifiableGetters.put( propertyName, field ); - } + // If there was no getter or is method for booleans, then resort to the field. + // If a field was already added do not add it again. + modifiableGetters.putIfAbsent( propertyName, field ); } readAccessors = Collections.unmodifiableMap( modifiableGetters ); } From b26cd4e0cbf38c78685c35daa3f60574ff56c3fe Mon Sep 17 00:00:00 2001 From: Andrei Arlou Date: Sat, 26 Oct 2019 22:24:59 +0300 Subject: [PATCH 009/597] #1953 Fix TODO items for: integrationtest-java8Test, integrationtest-lombokBuilderTest, processor-nullvaluemappingTest (#1955) --- .../ap/test/bugs/_636/SourceTargetBaseMapper.java | 7 ++++++- .../mapstruct/ap/test/bugs/_636/SourceTargetMapper.java | 9 --------- .../src/main/java/org/mapstruct/itest/lombok/Person.java | 4 +--- .../org/mapstruct/itest/lombok/LombokMapperTest.java | 4 ++-- .../mapstruct/ap/test/nullvaluemapping/CarMapper.java | 1 - .../ap/test/nullvaluemapping/NullValueMappingTest.java | 1 + 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetBaseMapper.java b/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetBaseMapper.java index 407a4c3b52..2b09b1e59e 100644 --- a/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetBaseMapper.java +++ b/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetBaseMapper.java @@ -10,6 +10,11 @@ @Mapper public interface SourceTargetBaseMapper { - // TODO.. move default and static interface method here when problem in eclipse processor is fixed. + default Foo fooFromId(long id) { + return new Foo(id); + } + static Bar barFromId(String id) { + return new Bar(id); + } } diff --git a/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetMapper.java b/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetMapper.java index e0e2f791a7..b619771fd3 100644 --- a/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetMapper.java +++ b/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetMapper.java @@ -19,13 +19,4 @@ public interface SourceTargetMapper extends SourceTargetBaseMapper { @Mapping(source = "idBar", target = "bar") }) Target mapSourceToTarget(Source source); - - default Foo fooFromId(long id) { - return new Foo(id); - } - - static Bar barFromId(String id) { - return new Bar(id); - } - } diff --git a/integrationtest/src/test/resources/lombokBuilderTest/src/main/java/org/mapstruct/itest/lombok/Person.java b/integrationtest/src/test/resources/lombokBuilderTest/src/main/java/org/mapstruct/itest/lombok/Person.java index faefc5aa1b..fcfaa6f49f 100644 --- a/integrationtest/src/test/resources/lombokBuilderTest/src/main/java/org/mapstruct/itest/lombok/Person.java +++ b/integrationtest/src/test/resources/lombokBuilderTest/src/main/java/org/mapstruct/itest/lombok/Person.java @@ -8,9 +8,7 @@ import lombok.Builder; import lombok.Getter; -//TODO make MapStruct DefaultBuilderProvider work with custom builder name -//@Builder(builderMethodName = "foo", buildMethodName = "create", builderClassName = "Builder") -@Builder(builderClassName = "Builder") +@Builder(builderMethodName = "foo", buildMethodName = "create", builderClassName = "Builder") @Getter public class Person { private final String name; diff --git a/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java b/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java index 4a58bb72be..6053f294c3 100644 --- a/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java +++ b/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java @@ -19,13 +19,13 @@ public class LombokMapperTest { @Test public void testSimpleImmutableBuilderHappyPath() { - PersonDto personDto = PersonMapper.INSTANCE.toDto( Person.builder() + PersonDto personDto = PersonMapper.INSTANCE.toDto( Person.foo() .age( 33 ) .name( "Bob" ) .address( Address.builder() .addressLine( "Wild Drive" ) .build() ) - .build() ); + .create() ); assertThat( personDto.getAge() ).isEqualTo( 33 ); assertThat( personDto.getName() ).isEqualTo( "Bob" ); assertThat( personDto.getAddress() ).isNotNull(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapper.java index f40e4fbb45..d087d5ef4a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapper.java @@ -39,7 +39,6 @@ public interface CarMapper { @BeanMapping(nullValueMappingStrategy = RETURN_DEFAULT) @Mappings({ @Mapping(target = "seatCount", source = "car.numberOfSeats"), - @Mapping(target = "model", source = "model"), // TODO, should not be needed, must be made based on name only @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") }) CarDto carToCarDto(Car car, String model); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java index 6d893f1375..5c6caf6b1f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java @@ -83,6 +83,7 @@ public void shouldMapExpressionAndConstantRegardlessNullArgSeveralSources() { //then assertThat( carDto1 ).isNotNull(); assertThat( carDto1.getMake() ).isEqualTo( car.getMake() ); + assertThat( carDto1.getModel() ).isEqualTo( "ModelT" ); assertThat( carDto1.getSeatCount() ).isEqualTo( car.getNumberOfSeats() ); assertThat( carDto1.getCatalogId() ).isNotEmpty(); From 071e5dc6b2ba2b03dcee4a85d7768eda6337e543 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sun, 10 Nov 2019 20:14:33 +0100 Subject: [PATCH 010/597] #1966 extra unit test (#1967) --- .../ap/test/bugs/_1966/Issue1966Mapper.java | 55 +++++++++++++++++++ .../ap/test/bugs/_1966/Issue1966Test.java | 36 ++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Test.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Mapper.java new file mode 100644 index 0000000000..13f4d489b9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Mapper.java @@ -0,0 +1,55 @@ +/* + * 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._1966; + +import java.util.Collections; +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper( imports = Collections.class ) +public interface Issue1966Mapper { + + Issue1966Mapper INSTANCE = Mappers.getMapper( Issue1966Mapper.class ); + + @Mapping(target = "previousNames", + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + defaultExpression = "java(Collections.emptyList())") + Animal toAnimal(AnimalRecord record); + + class AnimalRecord { + + private String[] previousNames; + + public String[] getPreviousNames() { + return previousNames; + } + + public void setPreviousNames(String[] previousNames) { + this.previousNames = previousNames; + } + } + + class Animal { + + private List previousNames; + + public List getPreviousNames() { + return previousNames; + } + + public void setPreviousNames(List previousNames) { + this.previousNames = previousNames; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Test.java new file mode 100644 index 0000000000..9ad37958d0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Test.java @@ -0,0 +1,36 @@ +/* + * 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._1966; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Sjaak Derksen + */ +@IssueKey("1966") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue1966Mapper.class +}) +public class Issue1966Test { + + @Test + public void shouldSelectDefaultExpressionEvenWhenSourceInMappingIsNotSpecified() { + + Issue1966Mapper.AnimalRecord dto = new Issue1966Mapper.AnimalRecord(); + + Issue1966Mapper.Animal entity = Issue1966Mapper.INSTANCE.toAnimal( dto ); + + assertThat( entity.getPreviousNames() ).isNotNull(); + assertThat( entity.getPreviousNames() ).isEmpty(); + } +} From ee794d042cecff4a89e30adf42ef6fc07211c453 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 7 Dec 2019 22:20:11 +0100 Subject: [PATCH 011/597] #807 meta annotations and duck typing (#1979) --- core/src/main/java/org/mapstruct/Mapping.java | 2 +- .../src/main/java/org/mapstruct/Mappings.java | 2 +- .../chapter-3-defining-a-mapper.asciidoc | 44 ++++++++++++ .../ap/internal/model/source/Mapping.java | 40 +++++------ .../processor/MethodRetrievalProcessor.java | 58 +++++++++++---- .../ap/test/mappingcomposition/BoxDto.java | 37 ++++++++++ .../ap/test/mappingcomposition/BoxEntity.java | 57 +++++++++++++++ .../mappingcomposition/CompositionTest.java | 71 +++++++++++++++++++ .../ap/test/mappingcomposition/ShelveDto.java | 46 ++++++++++++ .../test/mappingcomposition/ShelveEntity.java | 67 +++++++++++++++++ .../mappingcomposition/StorageMapper.java | 24 +++++++ .../ap/test/mappingcomposition/ToEntity.java | 17 +++++ 12 files changed, 429 insertions(+), 36 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/BoxDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/BoxEntity.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/CompositionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ShelveDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ShelveEntity.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/StorageMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ToEntity.java diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 92bf5742ec..98a309e057 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -142,7 +142,7 @@ @Repeatable(Mappings.class) @Retention(RetentionPolicy.CLASS) -@Target(ElementType.METHOD) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) public @interface Mapping { /** diff --git a/core/src/main/java/org/mapstruct/Mappings.java b/core/src/main/java/org/mapstruct/Mappings.java index 37e33771d7..039ec7b3c7 100644 --- a/core/src/main/java/org/mapstruct/Mappings.java +++ b/core/src/main/java/org/mapstruct/Mappings.java @@ -42,7 +42,7 @@ * @author Gunnar Morling */ @Retention(RetentionPolicy.CLASS) -@Target(ElementType.METHOD) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE }) public @interface Mappings { /** diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 347750ecc8..335bbbf954 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -124,6 +124,50 @@ Collection-typed attributes with the same element type will be copied by creatin MapStruct takes all public properties of the source and target types into account. This includes properties declared on super-types. +[[mapping-composition]] +=== Mapping Composition (experimental) +MapStruct supports the use of meta annotations. The `@Mapping` annotation supports now `@Target` with `ElementType#ANNOTATION_TYPE` in addition to `ElementType#METHOD`. This allows `@Mapping` to be used on other (user defined) annotations for re-use purposes. For example: + +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Retention(RetentionPolicy.CLASS) +@Mapping(target = "id", ignore = true) +@Mapping(target = "creationDate", expression = "java(new java.util.Date())") +@Mapping(target = "name", source = "groupName") +public @interface ToEntity { } +---- +==== + +Can be used to characterise an `Entity` without the need to have a common base type. For instance, `ShelveEntity` and `BoxEntity` do not share a common base type in the `StorageMapper` below. +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface StorageMapper { + + StorageMapper INSTANCE = Mappers.getMapper( StorageMapper.class ); + + @ToEntity + @Mapping( target = "weightLimit", source = "maxWeight") + ShelveEntity map(ShelveDto source); + + @ToEntity + @Mapping( target = "label", source = "designation") + BoxEntity map(BoxDto source); +} +---- +==== + +Still, they do have some properties in common. The `@ToEntity` assumes both target beans `ShelveEntity` and `BoxEntity` have properties: `"id"`, `"creationDate"` and `"name"`. It furthermore assumes that the source beans `ShelveDto` and `BoxDto` always have a property `"groupName"`. This concept is also known as "duck-typing". In other words, if it quacks like duck, walks like a duck its probably a duck. + +This feature is still experimental. Error messages are not mature yet: the method on which the problem occurs is displayed, as well as the concerned values in the `@Mapping` annotation. However, the composition aspect is not visible. The messages are "as if" the `@Mapping` would be present on the concerned method directly. +Therefore, the user should use this feature with care, especially when uncertain when a property is always present. + +A more typesafe (but also more verbose) way would be to define base classes / interfaces on the target bean and the source bean and use `@InheritConfiguration` to achieve the same result (see <>). + [[adding-custom-methods]] === Adding custom methods to mappers diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java index 00e6d9bef0..21ece8e8f0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java @@ -92,36 +92,25 @@ public static Mapping getMappingByTargetName(String targetName, Set map return mappings.stream().filter( mapping -> mapping.targetName.equals( targetName ) ).findAny().orElse( null ); } - public static Set fromMappingsPrism(MappingsPrism mappingsAnnotation, ExecutableElement method, - FormattingMessager messager, Types typeUtils) { - Set mappings = new LinkedHashSet<>(); + public static void addFromMappingsPrism(MappingsPrism mappingsAnnotation, ExecutableElement method, + FormattingMessager messager, Types typeUtils, Set mappings) { for ( MappingPrism mappingPrism : mappingsAnnotation.value() ) { - Mapping mapping = fromMappingPrism( mappingPrism, method, messager, typeUtils ); - if ( mapping != null ) { - if ( mappings.contains( mapping ) ) { - messager.printMessage( method, Message.PROPERTYMAPPING_DUPLICATE_TARGETS, mappingPrism.target() ); - } - else { - mappings.add( mapping ); - } - } + addFromMappingPrism( mappingPrism, method, messager, typeUtils, mappings ); } - - return mappings; } - public static Mapping fromMappingPrism(MappingPrism mappingPrism, ExecutableElement element, - FormattingMessager messager, Types typeUtils) { + public static void addFromMappingPrism(MappingPrism mappingPrism, ExecutableElement method, + FormattingMessager messager, Types typeUtils, Set mappings) { - if (!isConsistent( mappingPrism, element, messager ) ) { - return null; + if (!isConsistent( mappingPrism, method, messager ) ) { + return; } String source = mappingPrism.source().isEmpty() ? null : mappingPrism.source(); String constant = mappingPrism.values.constant() == null ? null : mappingPrism.constant(); - String expression = getExpression( mappingPrism, element, messager ); - String defaultExpression = getDefaultExpression( mappingPrism, element, messager ); + String expression = getExpression( mappingPrism, method, messager ); + String defaultExpression = getDefaultExpression( mappingPrism, method, messager ); String dateFormat = mappingPrism.values.dateFormat() == null ? null : mappingPrism.dateFormat(); String numberFormat = mappingPrism.values.numberFormat() == null ? null : mappingPrism.numberFormat(); String defaultValue = mappingPrism.values.defaultValue() == null ? null : mappingPrism.defaultValue(); @@ -136,7 +125,7 @@ public static Mapping fromMappingPrism(MappingPrism mappingPrism, ExecutableElem numberFormat, mappingPrism.mirror, mappingPrism.values.dateFormat(), - element + method ); SelectionParameters selectionParams = new SelectionParameters( mappingPrism.qualifiedBy(), @@ -155,7 +144,7 @@ public static Mapping fromMappingPrism(MappingPrism mappingPrism, ExecutableElem ? null : NullValuePropertyMappingStrategyPrism.valueOf( mappingPrism.nullValuePropertyMappingStrategy() ); - return new Mapping( + Mapping mapping = new Mapping( source, constant, expression, @@ -174,6 +163,13 @@ public static Mapping fromMappingPrism(MappingPrism mappingPrism, ExecutableElem nullValuePropertyMappingStrategy, null ); + + if ( mappings.contains( mapping ) ) { + messager.printMessage( method, Message.PROPERTYMAPPING_DUPLICATE_TARGETS, mappingPrism.target() ); + } + else { + mappings.add( mapping ); + } } public static Mapping forIgnore( String targetName) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index cc1b0c350b..786ec0a128 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -11,6 +11,9 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; @@ -57,6 +60,11 @@ */ public class MethodRetrievalProcessor implements ModelElementProcessor> { + private static final String JAVA_LANG_ANNOTATION_PGK = "java.lang.annotation"; + private static final String ORG_MAPSTRUCT_PKG = "org.mapstruct"; + private static final String MAPPING_FQN = "org.mapstruct.Mapping"; + private static final String MAPPINGS_FQN = "org.mapstruct.Mappings"; + private FormattingMessager messager; private TypeFactory typeFactory; private AccessorNamingUtils accessorNaming; @@ -243,7 +251,7 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, .setParameters( parameters ) .setReturnType( returnType ) .setExceptionTypes( exceptionTypes ) - .setMappings( getMappings( method ) ) + .setMappings( getMappings( method, method, new LinkedHashSet<>(), new HashSet<>() ) ) .setIterableMapping( IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ), @@ -492,24 +500,50 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List getMappings(ExecutableElement method) { - Set mappings = new LinkedHashSet<>( ); - - MappingPrism mappingAnnotation = MappingPrism.getInstanceOn( method ); - MappingsPrism mappingsAnnotation = MappingsPrism.getInstanceOn( method ); - - if ( mappingAnnotation != null ) { - mappings.add( Mapping.fromMappingPrism( mappingAnnotation, method, messager, typeUtils ) ); + private Set getMappings(ExecutableElement method, Element element, Set mappings, + Set handledElements) { + + for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { + Element lElement = annotationMirror.getAnnotationType().asElement(); + if ( isAnnotation( lElement, MAPPING_FQN ) ) { + // although getInstanceOn does a search on annotation mirrors, the order is preserved + MappingPrism mappingAnnotation = MappingPrism.getInstanceOn( element ); + Mapping.addFromMappingPrism( mappingAnnotation, method, messager, typeUtils, mappings ); + } + else if ( isAnnotation( lElement, MAPPINGS_FQN ) ) { + // although getInstanceOn does a search on annotation mirrors, the order is preserved + MappingsPrism mappingsAnnotation = MappingsPrism.getInstanceOn( element ); + Mapping.addFromMappingsPrism( mappingsAnnotation, method, messager, typeUtils, mappings ); + } + else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) + && !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG ) + && !handledElements.contains( lElement ) + ) { + // recur over annotation mirrors + handledElements.add( lElement ); + getMappings( method, lElement, mappings, handledElements ); + } } + return mappings; + } - if ( mappingsAnnotation != null ) { - mappings.addAll( Mapping.fromMappingsPrism( mappingsAnnotation, method, messager, typeUtils ) ); + private boolean isAnnotationInPackage(Element element, String packageFQN ) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return packageFQN.equals( elementUtils.getPackageOf( element ).getQualifiedName().toString() ); } + return false; + } - return mappings; + private boolean isAnnotation(Element element, String annotationFQN) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return annotationFQN.equals( ( (TypeElement) element ).getQualifiedName().toString() ); + } + return false; } /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/BoxDto.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/BoxDto.java new file mode 100644 index 0000000000..1e984b792e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/BoxDto.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.mappingcomposition; + +public class BoxDto { + + private String groupName; + private String designation; + private ShelveDto shelve; + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getDesignation() { + return designation; + } + + public void setDesignation(String designation) { + this.designation = designation; + } + + public ShelveDto getShelve() { + return shelve; + } + + public void setShelve(ShelveDto shelve) { + this.shelve = shelve; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/BoxEntity.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/BoxEntity.java new file mode 100644 index 0000000000..d0baa7c9d5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/BoxEntity.java @@ -0,0 +1,57 @@ +/* + * 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.mappingcomposition; + +import java.util.Date; + +public class BoxEntity { + + private Date creationDate; + private Long id; + private String name; + private String label; + private ShelveEntity shelve; + + public Date getCreationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public ShelveEntity getShelve() { + return shelve; + } + + public void setShelve(ShelveEntity shelve) { + this.shelve = shelve; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/CompositionTest.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/CompositionTest.java new file mode 100644 index 0000000000..6850c6f4ea --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/CompositionTest.java @@ -0,0 +1,71 @@ +/* + * 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.mappingcomposition; + +import java.util.Date; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * + * @author Sjaak Derksen + */ +@IssueKey("807") +@WithClasses({ + BoxDto.class, + BoxEntity.class, + ShelveDto.class, + ShelveEntity.class, + StorageMapper.class, + ToEntity.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class CompositionTest { + + @Test + public void shouldCompose() { + + Date now = new Date(); + Date anHourAgo = new Date( now.getTime() - 3600 * 1000 ); + Date anHourFromNow = new Date( now.getTime() + 3600 * 1000 ); + + ShelveDto shelve = new ShelveDto(); + shelve.setGroupName( "blue things" ); + shelve.setMaxWeight( 10.0d ); + shelve.setPath( "row5" ); + shelve.setStandNumber( 3 ); + + BoxDto box = new BoxDto(); + box.setDesignation( "blue item" ); + box.setGroupName( "blue stuff" ); + box.setShelve( shelve ); + + BoxEntity boxEntity = StorageMapper.INSTANCE.map( box ); + + // box + assertThat( boxEntity ).isNotNull(); + assertThat( boxEntity.getCreationDate() ).isBetween( anHourAgo, anHourFromNow ); + assertThat( boxEntity.getId() ).isNull(); + assertThat( boxEntity.getName() ).isEqualTo( "blue stuff" ); + assertThat( boxEntity.getLabel() ).isEqualTo( "blue item" ); + + // shelve + ShelveEntity shelveEntity = boxEntity.getShelve(); + assertThat( shelveEntity.getCreationDate() ).isBetween( anHourAgo, anHourFromNow ); + assertThat( shelveEntity.getId() ).isNull(); + assertThat( shelveEntity.getName() ).isEqualTo( "blue things" ); + assertThat( shelveEntity.getPath() ).isEqualTo( "row5" ); + assertThat( shelveEntity.getStandNumber() ).isEqualTo( 3 ); + assertThat( shelveEntity.getWeightLimit() ).isEqualTo( 10.0d ); + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ShelveDto.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ShelveDto.java new file mode 100644 index 0000000000..37cfe79036 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ShelveDto.java @@ -0,0 +1,46 @@ +/* + * 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.mappingcomposition; + +public class ShelveDto { + + private String groupName; + private String path; + private int standNumber; + private double maxWeight; + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public int getStandNumber() { + return standNumber; + } + + public void setStandNumber(int standNumber) { + this.standNumber = standNumber; + } + + public double getMaxWeight() { + return maxWeight; + } + + public void setMaxWeight(double maxWeight) { + this.maxWeight = maxWeight; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ShelveEntity.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ShelveEntity.java new file mode 100644 index 0000000000..39a397c7cb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ShelveEntity.java @@ -0,0 +1,67 @@ +/* + * 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.mappingcomposition; + +import java.util.Date; + +public class ShelveEntity { + + private Date creationDate; + private Long id; + private String name; + + private String path; + private int standNumber; + private double weightLimit; + + public Date getCreationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public int getStandNumber() { + return standNumber; + } + + public void setStandNumber(int standNumber) { + this.standNumber = standNumber; + } + + public double getWeightLimit() { + return weightLimit; + } + + public void setWeightLimit(double weightLimit) { + this.weightLimit = weightLimit; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/StorageMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/StorageMapper.java new file mode 100644 index 0000000000..5136968019 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/StorageMapper.java @@ -0,0 +1,24 @@ +/* + * 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.mappingcomposition; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface StorageMapper { + + StorageMapper INSTANCE = Mappers.getMapper( StorageMapper.class ); + + @ToEntity + @Mapping( target = "weightLimit", source = "maxWeight") + ShelveEntity map(ShelveDto source); + + @ToEntity + @Mapping( target = "label", source = "designation") + BoxEntity map(BoxDto source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ToEntity.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ToEntity.java new file mode 100644 index 0000000000..4cbd94c665 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/ToEntity.java @@ -0,0 +1,17 @@ +/* + * 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.mappingcomposition; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.Mapping; + +@Retention(RetentionPolicy.CLASS) +@Mapping(target = "id", ignore = true) +@Mapping(target = "creationDate", expression = "java(new java.util.Date())") +@Mapping(target = "name", source = "groupName") +public @interface ToEntity { } From 779eb1bd472e5c3743d56215c47f99700d1f8c79 Mon Sep 17 00:00:00 2001 From: ttzn Date: Sun, 8 Dec 2019 19:55:16 +0100 Subject: [PATCH 012/597] #1420 Add support for Gradle incremental annotation processing (#1971) * Add relevant file in META-INF * Add integration test with Gradle test kit dependencies * Test with Gradle 5 and Gradle 6 --- copyright.txt | 1 + integrationtest/pom.xml | 31 ++++ .../GradleIncrementalCompilationTest.java | 159 ++++++++++++++++++ .../build.gradle | 29 ++++ .../settings.gradle | 1 + .../itest/gradle/lib/TestMapper.java | 19 +++ .../itest/gradle/lib/UnrelatedComponent.java | 12 ++ .../mapstruct/itest/gradle/model/Source.java | 18 ++ .../mapstruct/itest/gradle/model/Target.java | 31 ++++ .../gradle/incremental.annotation.processors | 1 + 10 files changed, 302 insertions(+) create mode 100644 integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java create mode 100644 integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle create mode 100644 integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle create mode 100644 integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java create mode 100644 integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/UnrelatedComponent.java create mode 100644 integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/model/Source.java create mode 100644 integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/model/Target.java create mode 100644 processor/src/main/resources/META-INF/gradle/incremental.annotation.processors diff --git a/copyright.txt b/copyright.txt index 2aab0c1f80..2980f611b4 100644 --- a/copyright.txt +++ b/copyright.txt @@ -2,6 +2,7 @@ ============ Alexandr Shalugin - https://github.com/shalugin +Amine Touzani - https://github.com/ttzn Andreas Gudian - https://github.com/agudian Andrei Arlou - https://github.com/Captain1653 Andres Jose Sebastian Rincon Gonzalez - https://github.com/stianrincon diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 257c32730a..8b69ed0a73 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -24,6 +24,19 @@ ${project.version} true + + + + gradle + https://repo.gradle.org/gradle/libs-releases + + true + + + false + + + @@ -42,6 +55,24 @@ maven-verifier test + + org.gradle + gradle-test-kit + 5.6.4 + test + + + org.gradle + gradle-tooling-api + 5.6.4 + test + + + commons-io + commons-io + 2.6 + test + diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java new file mode 100644 index 0000000000..bb0c0f4ce6 --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java @@ -0,0 +1,159 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.tests; + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE; +import static org.hamcrest.core.StringContains.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + *

This is supposed to be run from the mapstruct root project folder. + * Otherwise, use -Dmapstruct_root=path_to_project. + */ +@RunWith( Parameterized.class ) +public class GradleIncrementalCompilationTest { + private static Path rootPath; + private static String projectDir = "integrationtest/src/test/resources/gradleIncrementalCompilationTest"; + private static String compileTaskName = "compileJava"; + + @Rule + public final TemporaryFolder testBuildDir = new TemporaryFolder(); + @Rule + public final TemporaryFolder testProjectDir = new TemporaryFolder(); + + private String gradleVersion; + private GradleRunner runner; + private File sourceDirectory; + private List compileArgs; // Gradle compile task arguments + + public GradleIncrementalCompilationTest(String gradleVersion) { + this.gradleVersion = gradleVersion; + } + + @Parameters( name = "Gradle {0}" ) + public static List gradleVersions() { + return Arrays.asList( "5.0", "6.0" ); + } + + private void replaceInFile(File file, CharSequence target, CharSequence replacement) throws IOException { + String content = FileUtils.readFileToString( file, Charset.defaultCharset() ); + FileUtils.writeStringToFile( file, content.replace( target, replacement ), Charset.defaultCharset() ); + } + + private GradleRunner getRunner(String... additionalArguments) { + List fullArguments = new ArrayList( compileArgs ); + fullArguments.addAll( Arrays.asList( additionalArguments ) ); + return runner.withArguments( fullArguments ); + } + + private void assertCompileOutcome(BuildResult result, TaskOutcome outcome) { + assertEquals( outcome, result.task( ":" + compileTaskName ).getOutcome() ); + } + + private void assertRecompiled(BuildResult result, int recompiledCount) { + assertCompileOutcome( result, recompiledCount > 0 ? SUCCESS : UP_TO_DATE ); + assertThat( + result.getOutput(), + containsString( String.format( "Incremental compilation of %d classes completed", recompiledCount ) ) ); + } + + private List buildCompileArgs() { + // Make Gradle use the temporary build folder by overriding the buildDir property + String buildDirPropertyArg = "-PbuildDir=" + testBuildDir.getRoot().getAbsolutePath(); + + // Inject the path to the folder containing the mapstruct-processor JAR + String jarDirectoryArg = "-PmapstructRootPath=" + rootPath.toString(); + return Arrays.asList( compileTaskName, buildDirPropertyArg, jarDirectoryArg ); + } + + @BeforeClass + public static void setupClass() throws Exception { + rootPath = Paths.get( System.getProperty( "mapstruct_root", "." ) ).toAbsolutePath(); + } + + @Before + public void setup() throws IOException { + // Copy test project files to the temp dir + Path gradleProjectPath = rootPath.resolve( projectDir ); + FileUtils.copyDirectory( gradleProjectPath.toFile(), testProjectDir.getRoot() ); + compileArgs = buildCompileArgs(); + sourceDirectory = new File( testProjectDir.getRoot(), "src/main/java" ); + runner = GradleRunner.create().withGradleVersion( gradleVersion ).withProjectDir( testProjectDir.getRoot() ); + } + + @Test + public void testBuildSucceeds() throws IOException { + // Make sure the test build setup actually compiles + BuildResult buildResult = getRunner().build(); + assertCompileOutcome( buildResult, SUCCESS ); + } + + @Test + public void testUpToDate() throws IOException { + getRunner().build(); + BuildResult secondBuildResult = getRunner().build(); + assertCompileOutcome( secondBuildResult, UP_TO_DATE ); + } + + @Test + public void testChangeConstant() throws IOException { + getRunner().build(); + // Change return value in class Target + File targetFile = new File( sourceDirectory, "org/mapstruct/itest/gradle/model/Target.java" ); + replaceInFile( targetFile, "original", "changed" ); + BuildResult secondBuildResult = getRunner( "--info" ).build(); + + // 3 classes should be recompiled: Target -> TestMapper -> TestMapperImpl + assertRecompiled( secondBuildResult, 3 ); + } + + @Test + public void testChangeTargetField() throws IOException { + getRunner().build(); + // Change target field in mapper interface + File mapperFile = new File( sourceDirectory, "org/mapstruct/itest/gradle/lib/TestMapper.java" ); + replaceInFile( mapperFile, "field", "otherField" ); + BuildResult secondBuildResult = getRunner( "--info" ).build(); + + // 2 classes should be recompiled: TestMapper -> TestMapperImpl + assertRecompiled( secondBuildResult, 2 ); + } + + @Test + public void testChangeUnrelatedFile() throws IOException { + getRunner().build(); + File unrelatedFile = new File( sourceDirectory, "org/mapstruct/itest/gradle/lib/UnrelatedComponent.java" ); + replaceInFile( unrelatedFile, "true", "false" ); + BuildResult secondBuildResult = getRunner( "--info" ).build(); + + // Only the UnrelatedComponent class should be recompiled + assertRecompiled( secondBuildResult, 1 ); + } +} diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle new file mode 100644 index 0000000000..e62a8d087f --- /dev/null +++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle @@ -0,0 +1,29 @@ +plugins { + id 'java' +} + +if (!project.hasProperty('mapstructRootPath')) + throw new IllegalArgumentException("Missing required property: mapstructRootPath") + +repositories { + jcenter() + mavenLocal() + flatDir { + dirs "${mapstructRootPath}/processor/target" + dirs "${mapstructRootPath}/core/target" + } +} + +// Extract version and artifactId values +def apPom = new XmlParser().parse(file("${mapstructRootPath}/processor/pom.xml")) +ext.apArtifactId = apPom.artifactId[0].text() +ext.apVersion = apPom.parent[0].version[0].text() + +def libPom = new XmlParser().parse(file("${mapstructRootPath}/core/pom.xml")) +ext.libArtifactId = libPom.artifactId[0].text() +ext.libVersion = libPom.parent[0].version[0].text() + +dependencies { + annotationProcessor name: "${apArtifactId}-${apVersion}" + implementation name: "${libArtifactId}-${libVersion}" +} diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle new file mode 100644 index 0000000000..f62a77ab7b --- /dev/null +++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'gradle-incremental-compilation-test' \ No newline at end of file diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java new file mode 100644 index 0000000000..13ae2ef5fc --- /dev/null +++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java @@ -0,0 +1,19 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.gradle.lib; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +import org.mapstruct.itest.gradle.model.Target; +import org.mapstruct.itest.gradle.model.Source; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface TestMapper { + @Mapping(source = "value", target = "field") + public Target toTarget(Source source); +} diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/UnrelatedComponent.java b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/UnrelatedComponent.java new file mode 100644 index 0000000000..9f1095850c --- /dev/null +++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/UnrelatedComponent.java @@ -0,0 +1,12 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.gradle.lib; + +public class UnrelatedComponent { + public boolean unrelatedMethod() { + return true; + } +} diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/model/Source.java b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/model/Source.java new file mode 100644 index 0000000000..c7103aaceb --- /dev/null +++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/model/Source.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.gradle.model; + +public class Source { + private int value; + + public void setValue(int value) { + this.value = value; + } + + public int getValue() { + return value; + } +} diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/model/Target.java b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/model/Target.java new file mode 100644 index 0000000000..4b5a171109 --- /dev/null +++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/model/Target.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.gradle.model; + +public class Target { + private String field = getDefaultValue(); + private String otherField; + + public void setField(String field) { + this.field = field; + } + + public String getField() { + return field; + } + + public void setOtherField(String otherField) { + this.otherField = otherField; + } + + public String getOtherField() { + return otherField; + } + + public String getDefaultValue() { + return "original"; + } +} diff --git a/processor/src/main/resources/META-INF/gradle/incremental.annotation.processors b/processor/src/main/resources/META-INF/gradle/incremental.annotation.processors new file mode 100644 index 0000000000..172d1bb777 --- /dev/null +++ b/processor/src/main/resources/META-INF/gradle/incremental.annotation.processors @@ -0,0 +1 @@ +org.mapstruct.ap.MappingProcessor,isolating From 84062bf78fe17af331f31b4f35a9133ff82d7f36 Mon Sep 17 00:00:00 2001 From: fml2 Date: Mon, 9 Dec 2019 13:49:57 +0100 Subject: [PATCH 013/597] docs: Fix typo in the builder section --- .../src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 335bbbf954..5255ac6f1b 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -422,7 +422,7 @@ The default implementation of the `BuilderProvider` assumes the following: * The type has a parameterless public static builder creation method that returns a builder. So for example `Person` has a public static method that returns `PersonBuilder`. -* The builder type has a parameterless public method (build method) that returns the type being build +* The builder type has a parameterless public method (build method) that returns the type being built. In our example `PersonBuilder` has a method returning `Person`. * In case there are multiple build methods, MapStruct will look for a method called `build`, if such method exists then this would be used, otherwise a compilation error would be created. @@ -527,4 +527,4 @@ Otherwise, you would need to write a custom `BuilderProvider` [TIP] ==== In case you want to disable using builders then you can use the `NoOpBuilderProvider` by creating a `org.mapstruct.ap.spi.BuilderProvider` file in the `META-INF/services` directory with `org.mapstruct.ap.spi.NoOpBuilderProvider` as it's content. -==== \ No newline at end of file +==== From c153c8bf5af9a20a2d9c1d7ef3a9a631212159bb Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Mon, 23 Dec 2019 20:41:38 +0100 Subject: [PATCH 014/597] #1983 javadoc and doc on @ignore in @InheritInverseConfituration (#1986) --- .../org/mapstruct/InheritConfiguration.java | 4 ++++ .../InheritInverseConfiguration.java | 19 ++++++++++++++++++- ...11-reusing-mapping-configurations.asciidoc | 6 ++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/mapstruct/InheritConfiguration.java b/core/src/main/java/org/mapstruct/InheritConfiguration.java index d39ded7b0d..0d1bf0b38f 100644 --- a/core/src/main/java/org/mapstruct/InheritConfiguration.java +++ b/core/src/main/java/org/mapstruct/InheritConfiguration.java @@ -18,6 +18,10 @@ * If no method can be identified unambiguously as configuration source (i.e. several candidate methods with matching * source and target type exist), the name of the method to inherit from must be specified via {@link #name()}. *

+ * {@link Mapping#expression()}, {@link Mapping#constant()}, {@link Mapping#defaultExpression()} and + * {@link Mapping#defaultValue()} are not inverse inherited + * + *

* A typical use case is annotating an update method so it inherits all mappings from a corresponding "standard" mapping * method: * diff --git a/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java b/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java index b1eec47f34..3063ca07c9 100644 --- a/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java +++ b/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java @@ -21,9 +21,12 @@ *

* If more than one matching inverse method exists, the name of the method to inherit the configuration from must be * specified via {@link #name()} + *

+ * {@link Mapping#expression()}, {@link Mapping#constant()}, {@link Mapping#defaultExpression()} and + * {@link Mapping#defaultValue()} are not inverse inherited * *

- * Example + * Examples *

*

  * @Mapper
@@ -56,7 +59,21 @@
  *      }
  * }
  * 
+ *

+ *


+ * @Mapper
+ * public interface CarMapper {
  *
+ * @Mapping( target = "seatCount", source = "numberOfSeats")
+ * @Mapping( target = "enginePower", source = "engineClass", ignore=true) // NOTE: source specified as well
+ * CarDto carToDto(Car car);
+ *
+ * @InheritInverseConfiguration
+ * @Mapping(target = "numberOfSeats", ignore = true)
+ * // no need to specify a mapping with ignore for "engineClass": specifying source above will assume
+ * Car carDtoToCar(CarDto carDto);
+ * }
+ * 
* @author Sjaak Derksen */ @Target(ElementType.METHOD) diff --git a/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc b/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc index 2b47579ec8..104b0a9b2f 100644 --- a/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc +++ b/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc @@ -78,13 +78,15 @@ If multiple methods qualify, the method from which to inherit the configuration Configurations are inherited transitively. So if method `C` defines a mapping `@Mapping( target = "x", ignore = true)`, `B` defines a mapping `@Mapping( target = "y", ignore = true)`, then if `A` inherits from `B` inherits from `C`, `A` will inherit mappings for both property `x` and `y`. -Expressions and constants are excluded (silently ignored) in `@InheritInverseConfiguration`. +`@Mapping#expression`, `@Mapping#defaultExpression`, `@Mapping#defaultValue` and `@Mapping#constant` are excluded (silently ignored) in `@InheritInverseConfiguration`. + +`@Mapping#ignore` is only applied when `@Mapping#source` is also present in `@InheritInverseConfiguration`. Reverse mapping of nested source properties is experimental as of the 1.1.0.Beta2 release. Reverse mapping will take place automatically when the source property name and target property name are identical. Otherwise, `@Mapping` should specify both the target name and source name. In all cases, a suitable mapping method needs to be in place for the reverse mapping. [NOTE] ==== -`@InheritConfiguration` or `@InheritInverseConfiguration` cannot refer to methods in a used mapper. +`@InheritInverseConfiguration` cannot refer to methods in a used mapper. ==== [[shared-configurations]] From 57cfd35afc194aec5b4130acd3e57f273aa5b68a Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Thu, 9 Jan 2020 20:59:36 +0100 Subject: [PATCH 015/597] #1995 and mapstruct/mapstruct.org#101 nesting calling custom method (#1999) Co-authored-by: coffman21 --- .../chapter-5-data-type-conversions.asciidoc | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 45c97b3dab..592897ac6e 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -253,6 +253,62 @@ This means that it is possible for MapStruct not to report unmapped target prope ==== +[[invoking-custom-mapping-method]] +=== Invoking custom mapping method + +Sometimes mappings are not straightforward and some fields require custom logic. + +The example below demonstrates how the properties `length`, `width` and `height` in `FishTank` can be mapped to the `VolumeDto` bean, which is a member of `FishTankWithVolumeDto`. `VolumeDto` contains the properties `volume` and `description`. Custom logic is achieved by defining a method which takes `FishTank` instance as a parameter and returns a `VolumeDto`. MapStruct will take the entire parameter `source` and generate code to call the custom method `mapVolume` in order to map the `FishTank` object to the target property `volume`. + +The remainder of the fields could be mapped the regular way: using mappings defined defined by means of `@Mapping` annotations. + +.Manually implemented mapping method +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public class FishTank { + Fish fish; + String material; + Quality quality; + int length; + int width; + int height; +} + +public class FishTankWithVolumeDto { + FishDto fish; + MaterialDto material; + QualityDto quality; + VolumeDto volume; +} + +public class VolumeDto { + int volume; + String description; +} + +@Mapper +public abstract class FishTankMapperWithVolume { + + @Mapping(target = "fish.kind", source = "source.fish.type") + @Mapping(target = "material.materialType", source = "source.material") + @Mapping(target = "quality.document", source = "source.quality.report") + @Mapping(target = "volume", source = "source") + abstract FishTankWithVolumeDto map(FishTank source); + + VolumeDto mapVolume(FishTank source) { + int volume = source.length * source.width * source.height; + String desc = volume < 100 ? "Small" : "Large"; + return new VolumeDto(volume, desc); + } +} +---- +==== + +Note the `@Mapping` annotation where `source` field is equal to `"source"`, indicating the parameter name `source` itself in the method `map(FishTank source)` in stead of a (target) property in `FishTank`. + + [[invoking-other-mappers]] === Invoking other mappers @@ -293,7 +349,7 @@ In the `@Mapper` annotation at the `CarMapper` interface reference the `DateMapp [subs="verbatim,attributes"] ---- @Mapper(uses=DateMapper.class) -public class CarMapper { +public interface CarMapper { CarDto carToCarDto(Car car); } @@ -620,4 +676,4 @@ public interface MovieMapper { [WARNING] ==== Although the used mechanism is the same, the user has to be a bit more careful. Refactoring the name of a defined qualifier in an IDE will neatly refactor all other occurrences as well. This is obviously not the case for changing a name. -==== \ No newline at end of file +==== From 076f3ba839f0872b4d431e5f471a2bc83023dcae Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Fri, 10 Jan 2020 20:16:25 +0100 Subject: [PATCH 016/597] #1995 follow up, small typo --- .../src/main/asciidoc/chapter-5-data-type-conversions.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 592897ac6e..2a446dee5e 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -306,7 +306,7 @@ public abstract class FishTankMapperWithVolume { ---- ==== -Note the `@Mapping` annotation where `source` field is equal to `"source"`, indicating the parameter name `source` itself in the method `map(FishTank source)` in stead of a (target) property in `FishTank`. +Note the `@Mapping` annotation where `source` field is equal to `"source"`, indicating the parameter name `source` itself in the method `map(FishTank source)` instead of a (target) property in `FishTank`. [[invoking-other-mappers]] From 327730127b3024ddaa9169c72885badb459fdbd8 Mon Sep 17 00:00:00 2001 From: Marcel Overdijk Date: Sun, 27 Oct 2019 12:50:06 +0100 Subject: [PATCH 017/597] Updated docs to add compilerArgs instead of replacing them. When configuring MapStruct compilerArgs *add* them, no *replace* --- documentation/src/main/asciidoc/chapter-2-set-up.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 614be18346..16183b0001 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -210,7 +210,7 @@ When invoking javac directly, these options are passed to the compiler in the fo ---- ... compileJava { - options.compilerArgs = [ + options.compilerArgs += [ '-Amapstruct.suppressGeneratorTimestamp=true', '-Amapstruct.suppressGeneratorVersionInfoComment=true', '-Amapstruct.verbose=true' @@ -296,4 +296,4 @@ If the `@Generated` annotation is not available, MapStruct will detect this situ ===== In Java 9 `java.annotation.processing.Generated` was added (part of the `java.compiler` module), if this annotation is available then it will be used. -===== \ No newline at end of file +===== From d0565702672aae22ee59121ecb3178a12a7fe420 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 15 Jan 2020 21:33:11 +0100 Subject: [PATCH 018/597] #2001 Avoid NPE when checking whether import type element is nested When the typeToAdd is an array then TypeElement is null and ComponentType is the one that would be imported --- .../ap/internal/model/GeneratedType.java | 11 +++++-- .../mapstruct/ap/test/bugs/_2001/Entity.java | 25 +++++++++++++++ .../ap/test/bugs/_2001/EntityExtra.java | 23 +++++++++++++ .../mapstruct/ap/test/bugs/_2001/Form.java | 23 +++++++++++++ .../ap/test/bugs/_2001/FormExtra.java | 23 +++++++++++++ .../ap/test/bugs/_2001/Issue2001Mapper.java | 17 ++++++++++ .../ap/test/bugs/_2001/Issue2001Test.java | 32 +++++++++++++++++++ 7 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Entity.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/EntityExtra.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Form.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/FormExtra.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java index 81ccc779a6..a03f9af9a0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java @@ -288,8 +288,15 @@ private boolean needsImportDeclaration(Type typeToAdd) { } if ( typeToAdd.getPackageName().equals( packageName ) ) { - if ( !typeToAdd.getTypeElement().getNestingKind().isNested() ) { - return false; + if ( typeToAdd.getTypeElement() != null ) { + if ( !typeToAdd.getTypeElement().getNestingKind().isNested() ) { + return false; + } + } + else if ( typeToAdd.getComponentType() != null ) { + if ( !typeToAdd.getComponentType().getTypeElement().getNestingKind().isNested() ) { + return false; + } } } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Entity.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Entity.java new file mode 100644 index 0000000000..38ec42ee7d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Entity.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.ap.test.bugs._2001; + +import java.util.Set; + +/** + * @author Filip Hrisafov + */ +public +class Entity { + + private Set extras; + + public Set getExtras() { + return extras; + } + + public void setExtras(Set extras) { + this.extras = extras; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/EntityExtra.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/EntityExtra.java new file mode 100644 index 0000000000..ecd0dbb1f5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/EntityExtra.java @@ -0,0 +1,23 @@ +/* + * 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._2001; + +/** + * @author Filip Hrisafov + */ +public +class EntityExtra { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Form.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Form.java new file mode 100644 index 0000000000..a3656acecf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Form.java @@ -0,0 +1,23 @@ +/* + * 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._2001; + +/** + * @author Filip Hrisafov + */ +public +class Form { + + private FormExtra[] extras; + + public FormExtra[] getExtras() { + return extras; + } + + public void setExtras(FormExtra[] extras) { + this.extras = extras; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/FormExtra.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/FormExtra.java new file mode 100644 index 0000000000..2ff150f4ff --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/FormExtra.java @@ -0,0 +1,23 @@ +/* + * 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._2001; + +/** + * @author Filip Hrisafov + */ +public +class FormExtra { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Mapper.java new file mode 100644 index 0000000000..272d23bb5d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Mapper.java @@ -0,0 +1,17 @@ +/* + * 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._2001; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2001Mapper { + + Form map(Entity entity); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Test.java new file mode 100644 index 0000000000..7cb89f79e9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Test.java @@ -0,0 +1,32 @@ +/* + * 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._2001; + +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; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2001") +@RunWith( AnnotationProcessorTestRunner.class ) +@WithClasses( { + Entity.class, + EntityExtra.class, + Form.class, + FormExtra.class, + Issue2001Mapper.class +} ) +public class Issue2001Test { + + @Test + public void shouldCompile() { + + } +} From 2b36c220a3ec1adbe855fd03abdc899c2c589fd7 Mon Sep 17 00:00:00 2001 From: sjaakd Date: Sat, 25 Jan 2020 08:06:06 +0100 Subject: [PATCH 019/597] #1998 preparation move classes to new names 1 --- .../conversion/CreateDecimalFormat.java | 6 ++-- .../internal/model/AbstractBaseBuilder.java | 4 +-- .../model/AbstractMappingMethodBuilder.java | 4 +-- .../ap/internal/model/BeanMappingMethod.java | 14 ++++---- .../model/BuilderFinisherMethodResolver.java | 4 +-- .../ap/internal/model/ForgedMethod.java | 8 ++--- .../ap/internal/model/HelperMethod.java | 4 +-- .../ap/internal/model/PropertyMapping.java | 10 +++--- .../model/beanmapping/TargetReference.java | 6 ++-- ...anMapping.java => BeanMappingOptions.java} | 20 +++++------ ...pping.java => IterableMappingOptions.java} | 12 +++---- ...MapMapping.java => MapMappingOptions.java} | 14 ++++---- .../source/MapperOptions.java} | 10 +++--- ...Options.java => MappingMethodOptions.java} | 34 +++++++++---------- .../ap/internal/model/source/Method.java | 6 ++-- .../internal/model/source/SourceMethod.java | 34 +++++++++---------- .../model/source/builtin/BuiltInMethod.java | 10 +++--- ...nnotationBasedComponentModelProcessor.java | 4 +-- .../processor/MapperCreationProcessor.java | 32 ++++++++--------- .../processor/MapperServiceProcessor.java | 4 +-- .../processor/MethodRetrievalProcessor.java | 26 +++++++------- 21 files changed, 133 insertions(+), 133 deletions(-) rename processor/src/main/java/org/mapstruct/ap/internal/model/source/{BeanMapping.java => BeanMappingOptions.java} (91%) rename processor/src/main/java/org/mapstruct/ap/internal/model/source/{IterableMapping.java => IterableMappingOptions.java} (85%) rename processor/src/main/java/org/mapstruct/ap/internal/model/source/{MapMapping.java => MapMappingOptions.java} (88%) rename processor/src/main/java/org/mapstruct/ap/internal/{util/MapperConfiguration.java => model/source/MapperOptions.java} (97%) rename processor/src/main/java/org/mapstruct/ap/internal/model/source/{MappingOptions.java => MappingMethodOptions.java} (85%) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java index 9fe0c5b332..59f85d6b2c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java @@ -14,7 +14,7 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.model.source.MappingOptions; +import org.mapstruct.ap.internal.model.source.MappingMethodOptions; /** * HelperMethod that creates a {@link java.text.DecimalFormat} @@ -52,7 +52,7 @@ public Type getReturnType() { } @Override - public MappingOptions getMappingOptions() { - return MappingOptions.empty(); + public MappingMethodOptions getMappingOptions() { + return MappingMethodOptions.empty(); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java index b030f41c3f..b2d13dede4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java @@ -13,7 +13,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.MappingMethodUtils; import org.mapstruct.ap.internal.model.source.Method; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; /** @@ -60,7 +60,7 @@ boolean canGenerateAutoSubMappingBetween(Type sourceType, Type targetType) { } private boolean isDisableSubMappingMethodsGeneration() { - MapperConfiguration configuration = MapperConfiguration.getInstanceOn( ctx.getMapperTypeElement() ); + MapperOptions configuration = MapperOptions.getInstanceOn( ctx.getMapperTypeElement() ); return configuration.isDisableSubMappingMethodsGeneration(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index 69dad8e0c4..f9c53891c7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -8,7 +8,7 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.source.BeanMapping; +import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping; @@ -58,7 +58,7 @@ Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) { return createForgedAssignment( sourceRHS, ctx.getTypeFactory() - .builderTypeFor( targetType, BeanMapping.builderPrismFor( method ) ), + .builderTypeFor( targetType, BeanMappingOptions.builderPrismFor( method ) ), forgedMethod ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 458804bee1..c7783c4bb7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -35,16 +35,16 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder; -import org.mapstruct.ap.internal.model.source.BeanMapping; +import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.Mapping; -import org.mapstruct.ap.internal.model.source.MappingOptions; +import org.mapstruct.ap.internal.model.source.MappingMethodOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; @@ -121,7 +121,7 @@ public Builder forgedMethod(ForgedMethod forgedMethod) { public BeanMappingMethod build() { - BeanMapping beanMapping = method.getMappingOptions().getBeanMapping(); + BeanMappingOptions beanMapping = method.getMappingOptions().getBeanMapping(); SelectionParameters selectionParameters = beanMapping != null ? beanMapping.getSelectionParameters() : null; /* the return type that needs to be constructed (new or factorized), so for instance: */ @@ -828,13 +828,13 @@ private ReportingPolicyPrism getUnmappedTargetPolicy() { if ( mappingReferences.isForForgedMethods() ) { return ReportingPolicyPrism.IGNORE; } - MappingOptions mappingOptions = method.getMappingOptions(); + MappingMethodOptions mappingOptions = method.getMappingOptions(); if ( mappingOptions.getBeanMapping() != null && mappingOptions.getBeanMapping().getReportingPolicy() != null ) { return mappingOptions.getBeanMapping().getReportingPolicy(); } - MapperConfiguration mapperSettings = MapperConfiguration.getInstanceOn( ctx.getMapperTypeElement() ); + MapperOptions mapperSettings = MapperOptions.getInstanceOn( ctx.getMapperTypeElement() ); return mapperSettings.unmappedTargetPolicy( ctx.getOptions() ); } @@ -917,7 +917,7 @@ private ReportingPolicyPrism getUnmappedSourcePolicy() { if ( mappingReferences.isForForgedMethods() ) { return ReportingPolicyPrism.IGNORE; } - MapperConfiguration mapperSettings = MapperConfiguration.getInstanceOn( ctx.getMapperTypeElement() ); + MapperOptions mapperSettings = MapperOptions.getInstanceOn( ctx.getMapperTypeElement() ); return mapperSettings.unmappedSourcePolicy(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java index 90487d69c1..b3b63390ef 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java @@ -9,7 +9,7 @@ import javax.lang.model.element.ExecutableElement; import org.mapstruct.ap.internal.model.common.BuilderType; -import org.mapstruct.ap.internal.model.source.BeanMapping; +import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.prism.BuilderPrism; import org.mapstruct.ap.internal.util.Message; @@ -35,7 +35,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp return null; } - BuilderPrism builderMapping = BeanMapping.builderPrismFor( method ); + BuilderPrism builderMapping = BeanMappingOptions.builderPrismFor( method ); if ( builderMapping == null && buildMethods.size() == 1 ) { return MethodReference.forMethodCall( first( buildMethods ).getSimpleName().toString() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java index b385c127b5..b5ad9274b0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java @@ -15,11 +15,11 @@ import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.source.MappingOptions; +import org.mapstruct.ap.internal.model.source.MappingMethodOptions; import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Strings; /** @@ -323,7 +323,7 @@ public Type getDefiningType() { } @Override - public MapperConfiguration getMapperConfiguration() { + public MapperOptions getMapperConfiguration() { return basedOn.getMapperConfiguration(); } @@ -343,7 +343,7 @@ public boolean isObjectFactory() { } @Override - public MappingOptions getMappingOptions() { + public MappingMethodOptions getMappingOptions() { return basedOn.getMappingOptions(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java index 149e713337..52da765bdf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java @@ -18,7 +18,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Strings; /** @@ -238,7 +238,7 @@ public Type getDefiningType() { } @Override - public MapperConfiguration getMapperConfiguration() { + public MapperOptions getMapperConfiguration() { return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 3bec3eb461..7aec07d46d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -27,7 +27,7 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.source.BeanMapping; +import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry; @@ -38,7 +38,7 @@ import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.NativeTypes; import org.mapstruct.ap.internal.util.Strings; @@ -111,7 +111,7 @@ public T targetReadAccessor(Accessor targetReadAccessor) { public T targetWriteAccessor(Accessor targetWriteAccessor) { this.targetWriteAccessor = targetWriteAccessor; this.targetType = ctx.getTypeFactory().getType( targetWriteAccessor.getAccessedType() ); - BuilderPrism builderPrism = BeanMapping.builderPrismFor( method ); + BuilderPrism builderPrism = BeanMappingOptions.builderPrismFor( method ); this.targetBuilderType = ctx.getTypeFactory().builderTypeFor( this.targetType, builderPrism ); this.targetWriteAccessorType = targetWriteAccessor.getAccessorType(); return (T) this; @@ -230,8 +230,8 @@ public PropertyMappingBuilder nullValuePropertyMappingStrategy( NullValuePropert public PropertyMapping build() { - MapperConfiguration mapperConfiguration = method.getMapperConfiguration(); - BeanMapping beanMapping = method.getMappingOptions().getBeanMapping(); + MapperOptions mapperConfiguration = method.getMapperConfiguration(); + BeanMappingOptions beanMapping = method.getMappingOptions().getBeanMapping(); // null value check strategy (determine true value based on hierarchy) NullValueCheckStrategyPrism nvcsBean = beanMapping != null ? beanMapping.getNullValueCheckStrategy() : null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java index 2ca8b9ca37..55f43f33dd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java @@ -18,7 +18,7 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.model.source.BeanMapping; +import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.Mapping; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.prism.BuilderPrism; @@ -203,7 +203,7 @@ private List getTargetEntries(Type type, String[] entryNames) { ); } else { - BuilderPrism builderPrism = BeanMapping.builderPrismFor( method ); + BuilderPrism builderPrism = BeanMappingOptions.builderPrismFor( method ); builderType = typeFactory.builderTypeFor( nextType, builderPrism ); propertyEntry = PropertyEntry.forTargetReference( fullName, targetReadAccessor, @@ -271,7 +271,7 @@ private Type typeBasedOnMethod(Type type) { return type; } else { - BuilderPrism builderPrism = BeanMapping.builderPrismFor( method ); + BuilderPrism builderPrism = BeanMappingOptions.builderPrismFor( method ); return typeFactory.effectiveResultTypeFor( type, builderPrism ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java similarity index 91% rename from processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index 4b291ea71d..7a192c6f1f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -28,7 +28,7 @@ * * @author Sjaak Derksen */ -public class BeanMapping { +public class BeanMappingOptions { private final SelectionParameters selectionParameters; private final NullValueMappingStrategyPrism nullValueMappingStrategy; @@ -48,8 +48,8 @@ public class BeanMapping { * * @return new mapping */ - public static BeanMapping forInheritance(BeanMapping beanMapping) { - return new BeanMapping( + public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) { + return new BeanMappingOptions( SelectionParameters.forInheritance( beanMapping.selectionParameters ), beanMapping.nullValueMappingStrategy, beanMapping.nullValuePropertyMappingStrategy, @@ -95,7 +95,7 @@ public Builder typeFactory(TypeFactory typeFactory) { return this; } - public BeanMapping build() { + public BeanMappingOptions build() { if ( prism == null ) { return null; @@ -148,7 +148,7 @@ public BeanMapping build() { ); //TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support? - return new BeanMapping( + return new BeanMappingOptions( cmp, nullValueMappingStrategy, nullValuePropertyMappingStrategy, @@ -162,11 +162,11 @@ public BeanMapping build() { } } - private BeanMapping(SelectionParameters selectionParameters, NullValueMappingStrategyPrism nvms, - NullValuePropertyMappingStrategyPrism nvpms, NullValueCheckStrategyPrism nvcs, - ReportingPolicyPrism reportingPolicy, boolean ignoreByDefault, - List ignoreUnmappedSourceProperties, BuilderPrism builder, - AnnotationMirror mirror) { + private BeanMappingOptions(SelectionParameters selectionParameters, NullValueMappingStrategyPrism nvms, + NullValuePropertyMappingStrategyPrism nvpms, NullValueCheckStrategyPrism nvcs, + ReportingPolicyPrism reportingPolicy, boolean ignoreByDefault, + List ignoreUnmappedSourceProperties, BuilderPrism builder, + AnnotationMirror mirror) { this.selectionParameters = selectionParameters; this.nullValueMappingStrategy = nvms; this.nullValuePropertyMappingStrategy = nvpms; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java similarity index 85% rename from processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMapping.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java index 0f000ec74c..75fcf84e84 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java @@ -21,15 +21,15 @@ * * @author Gunnar Morling */ -public class IterableMapping { +public class IterableMappingOptions { private final SelectionParameters selectionParameters; private final FormattingParameters formattingParameters; private final AnnotationMirror mirror; private final NullValueMappingStrategyPrism nullValueMappingStrategy; - public static IterableMapping fromPrism(IterableMappingPrism iterableMapping, ExecutableElement method, - FormattingMessager messager, Types typeUtils) { + public static IterableMappingOptions fromPrism(IterableMappingPrism iterableMapping, ExecutableElement method, + FormattingMessager messager, Types typeUtils) { if ( iterableMapping == null ) { return null; } @@ -66,15 +66,15 @@ public static IterableMapping fromPrism(IterableMappingPrism iterableMapping, Ex method ); - return new IterableMapping( formatting, + return new IterableMappingOptions( formatting, selection, iterableMapping.mirror, nullValueMappingStrategy ); } - private IterableMapping(FormattingParameters formattingParameters, SelectionParameters selectionParameters, - AnnotationMirror mirror, NullValueMappingStrategyPrism nvms) { + private IterableMappingOptions(FormattingParameters formattingParameters, SelectionParameters selectionParameters, + AnnotationMirror mirror, NullValueMappingStrategyPrism nvms) { this.formattingParameters = formattingParameters; this.selectionParameters = selectionParameters; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java similarity index 88% rename from processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMapping.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java index 5bbc704d30..62c827b5a9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java @@ -21,7 +21,7 @@ * * @author Gunnar Morling */ -public class MapMapping { +public class MapMappingOptions { private final SelectionParameters keySelectionParameters; private final SelectionParameters valueSelectionParameters; @@ -30,8 +30,8 @@ public class MapMapping { private final AnnotationMirror mirror; private final NullValueMappingStrategyPrism nullValueMappingStrategy; - public static MapMapping fromPrism(MapMappingPrism mapMapping, ExecutableElement method, - FormattingMessager messager, Types typeUtils) { + public static MapMappingOptions fromPrism(MapMappingPrism mapMapping, ExecutableElement method, + FormattingMessager messager, Types typeUtils) { if ( mapMapping == null ) { return null; } @@ -89,7 +89,7 @@ public static MapMapping fromPrism(MapMappingPrism mapMapping, ExecutableElement method ); - return new MapMapping( + return new MapMappingOptions( keyFormatting, keySelection, valueFormatting, @@ -99,9 +99,9 @@ public static MapMapping fromPrism(MapMappingPrism mapMapping, ExecutableElement ); } - private MapMapping(FormattingParameters keyFormatting, SelectionParameters keySelectionParameters, - FormattingParameters valueFormatting, SelectionParameters valueSelectionParameters, AnnotationMirror mirror, - NullValueMappingStrategyPrism nvms ) { + private MapMappingOptions(FormattingParameters keyFormatting, SelectionParameters keySelectionParameters, + FormattingParameters valueFormatting, SelectionParameters valueSelectionParameters, AnnotationMirror mirror, + NullValueMappingStrategyPrism nvms ) { this.keyFormattingParameters = keyFormatting; this.keySelectionParameters = keySelectionParameters; this.valueFormattingParameters = valueFormatting; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java similarity index 97% rename from processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index 5eb5427581..1039e412f5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.util; +package org.mapstruct.ap.internal.model.source; import java.util.ArrayList; import java.util.LinkedHashSet; @@ -36,17 +36,17 @@ * * @author Sjaak Derksen */ -public class MapperConfiguration { +public class MapperOptions { private final MapperPrism mapperPrism; private final MapperConfigPrism mapperConfigPrism; private final DeclaredType config; - public static MapperConfiguration getInstanceOn(Element e) { - return new MapperConfiguration( MapperPrism.getInstanceOn( e ) ); + public static MapperOptions getInstanceOn(Element e) { + return new MapperOptions( MapperPrism.getInstanceOn( e ) ); } - private MapperConfiguration(MapperPrism mapperPrism) { + private MapperOptions(MapperPrism mapperPrism) { this.mapperPrism = mapperPrism; if ( mapperPrism.values.config() != null ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java similarity index 85% rename from processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 30f4060823..2786e221b1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -24,8 +24,8 @@ * * @author Andreas Gudian */ -public class MappingOptions { - private static final MappingOptions EMPTY = new MappingOptions( Collections.emptySet(), +public class MappingMethodOptions { + private static final MappingMethodOptions EMPTY = new MappingMethodOptions( Collections.emptySet(), null, null, null, @@ -33,14 +33,14 @@ public class MappingOptions { ); private Set mappings; - private IterableMapping iterableMapping; - private MapMapping mapMapping; - private BeanMapping beanMapping; + private IterableMappingOptions iterableMapping; + private MapMappingOptions mapMapping; + private BeanMappingOptions beanMapping; private List valueMappings; private boolean fullyInitialized; - public MappingOptions(Set mappings, IterableMapping iterableMapping, MapMapping mapMapping, - BeanMapping beanMapping, List valueMappings ) { + public MappingMethodOptions(Set mappings, IterableMappingOptions iterableMapping, MapMappingOptions mapMapping, + BeanMappingOptions beanMapping, List valueMappings ) { this.mappings = mappings; this.iterableMapping = iterableMapping; this.mapMapping = mapMapping; @@ -53,7 +53,7 @@ public MappingOptions(Set mappings, IterableMapping iterableMapping, Ma * * @return empty mapping options */ - public static MappingOptions empty() { + public static MappingMethodOptions empty() { return EMPTY; } @@ -65,15 +65,15 @@ public Set getMappings() { return mappings; } - public IterableMapping getIterableMapping() { + public IterableMappingOptions getIterableMapping() { return iterableMapping; } - public MapMapping getMapMapping() { + public MapMappingOptions getMapMapping() { return mapMapping; } - public BeanMapping getBeanMapping() { + public BeanMappingOptions getBeanMapping() { return beanMapping; } @@ -81,15 +81,15 @@ public List getValueMappings() { return valueMappings; } - public void setIterableMapping(IterableMapping iterableMapping) { + public void setIterableMapping(IterableMappingOptions iterableMapping) { this.iterableMapping = iterableMapping; } - public void setMapMapping(MapMapping mapMapping) { + public void setMapMapping(MapMappingOptions mapMapping) { this.mapMapping = mapMapping; } - public void setBeanMapping(BeanMapping beanMapping) { + public void setBeanMapping(BeanMappingOptions beanMapping) { this.beanMapping = beanMapping; } @@ -117,7 +117,7 @@ public void markAsFullyInitialized() { * @param method the source method */ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse, SourceMethod method ) { - MappingOptions inherited = templateMethod.getMappingOptions(); + MappingMethodOptions inherited = templateMethod.getMappingOptions(); if ( null != inherited ) { if ( getIterableMapping() == null && inherited.getIterableMapping() != null) { setIterableMapping( inherited.getIterableMapping() ); @@ -128,7 +128,7 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } if ( getBeanMapping() == null && inherited.getBeanMapping() != null ) { - setBeanMapping( BeanMapping.forInheritance( inherited.getBeanMapping() ) ); + setBeanMapping( BeanMappingOptions.forInheritance( inherited.getBeanMapping() ) ); } if ( getValueMappings() == null ) { @@ -179,7 +179,7 @@ public void applyIgnoreAll(SourceMethod method, TypeFactory typeFactory ) { if ( !method.isUpdateMethod() ) { writeType = typeFactory.effectiveResultTypeFor( writeType, - BeanMapping.builderPrismFor( method ) + BeanMappingOptions.builderPrismFor( method ) ); } Map writeAccessors = writeType.getPropertyWriteAccessors( cms ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index 5ed6f485d9..cf122fe2d8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -13,7 +13,7 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; /** * This interface makes available common method properties and a matching method There are 2 known implementors: @@ -168,7 +168,7 @@ public interface Method { * * @return the mapper config when this method needs to be implemented */ - MapperConfiguration getMapperConfiguration(); + MapperOptions getMapperConfiguration(); /** * @return {@code true}, if the method represents a mapping lifecycle callback (Before/After mapping method) @@ -185,7 +185,7 @@ public interface Method { * * @return the mapping options for this method */ - MappingOptions getMappingOptions(); + MappingMethodOptions getMappingOptions(); /** * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index ae6760ae2d..0de68bd514 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -20,7 +20,7 @@ import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.prism.ObjectFactoryPrism; import org.mapstruct.ap.internal.util.Executables; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.model.source.MappingMethodUtils.isEnumMapping; @@ -31,7 +31,7 @@ * type. *

* A method can either be configured by itself or by another method for the inverse mapping direction (the appropriate - * setter on {@link MappingOptions} will be called in this case). + * setter on {@link MappingMethodOptions} will be called in this case). * * @author Gunnar Morling */ @@ -49,8 +49,8 @@ public class SourceMethod implements Method { private final Type returnType; private final Accessibility accessibility; private final List exceptionTypes; - private final MapperConfiguration config; - private final MappingOptions mappingOptions; + private final MapperOptions config; + private final MappingMethodOptions mappingOptions; private final List prototypeMethods; private final Type mapperToImplement; @@ -78,12 +78,12 @@ public static class Builder { private Type returnType = null; private List exceptionTypes; private Set mappings; - private IterableMapping iterableMapping = null; - private MapMapping mapMapping = null; - private BeanMapping beanMapping = null; + private IterableMappingOptions iterableMapping = null; + private MapMappingOptions mapMapping = null; + private BeanMappingOptions beanMapping = null; private Types typeUtils; private TypeFactory typeFactory = null; - private MapperConfiguration mapperConfig = null; + private MapperOptions mapperConfig = null; private List prototypeMethods = Collections.emptyList(); private List valueMappings; private ParameterProvidedMethods contextProvidedMethods; @@ -118,17 +118,17 @@ public Builder setMappings(Set mappings) { return this; } - public Builder setIterableMapping(IterableMapping iterableMapping) { + public Builder setIterableMapping(IterableMappingOptions iterableMapping) { this.iterableMapping = iterableMapping; return this; } - public Builder setMapMapping(MapMapping mapMapping) { + public Builder setMapMapping(MapMappingOptions mapMapping) { this.mapMapping = mapMapping; return this; } - public Builder setBeanMapping(BeanMapping beanMapping) { + public Builder setBeanMapping(BeanMappingOptions beanMapping) { this.beanMapping = beanMapping; return this; } @@ -148,7 +148,7 @@ public Builder setTypeFactory(TypeFactory typeFactory) { return this; } - public Builder setMapperConfiguration(MapperConfiguration mapperConfig) { + public Builder setMapperConfiguration(MapperOptions mapperConfig) { this.mapperConfig = mapperConfig; return this; } @@ -174,14 +174,14 @@ public SourceMethod build() { mappings = Collections.emptySet(); } - MappingOptions mappingOptions = - new MappingOptions( mappings, iterableMapping, mapMapping, beanMapping, valueMappings ); + MappingMethodOptions mappingOptions = + new MappingMethodOptions( mappings, iterableMapping, mapMapping, beanMapping, valueMappings ); return new SourceMethod( this, mappingOptions ); } } - private SourceMethod(Builder builder, MappingOptions mappingOptions) { + private SourceMethod(Builder builder, MappingMethodOptions mappingOptions) { this.declaringMapper = builder.declaringMapper; this.executable = builder.executable; this.parameters = builder.parameters; @@ -463,7 +463,7 @@ public List getThrownTypes() { } @Override - public MappingOptions getMappingOptions() { + public MappingMethodOptions getMappingOptions() { return mappingOptions; } @@ -483,7 +483,7 @@ public Type getDefiningType() { } @Override - public MapperConfiguration getMapperConfiguration() { + public MapperOptions getMapperConfiguration() { return config; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java index bbfafcbfb6..3fdfd0eeae 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java @@ -16,10 +16,10 @@ import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.source.MappingOptions; +import org.mapstruct.ap.internal.model.source.MappingMethodOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.util.Collections.first; @@ -255,7 +255,7 @@ public Type getDefiningType() { } @Override - public MapperConfiguration getMapperConfiguration() { + public MapperOptions getMapperConfiguration() { return null; } @@ -270,8 +270,8 @@ public boolean isUpdateMethod() { } @Override - public MappingOptions getMappingOptions() { - return MappingOptions.empty(); + public MappingMethodOptions getMappingOptions() { + return MappingMethodOptions.empty(); } public BuiltInFieldReference getFieldReference() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java index 8cd9a21c00..17707d446c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java @@ -24,7 +24,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; /** * An {@link ModelElementProcessor} which converts the given {@link Mapper} object into an annotation based component @@ -42,7 +42,7 @@ public abstract class AnnotationBasedComponentModelProcessor implements ModelEle public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, Mapper mapper) { this.typeFactory = context.getTypeFactory(); - MapperConfiguration mapperConfiguration = MapperConfiguration.getInstanceOn( mapperTypeElement ); + MapperOptions mapperConfiguration = MapperOptions.getInstanceOn( mapperTypeElement ); String componentModel = mapperConfiguration.componentModel( context.getOptions() ); InjectionStrategyPrism injectionStrategy = mapperConfiguration.getInjectionStrategy( context.getOptions() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index f5d924002f..c67780de43 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -40,8 +40,8 @@ import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.model.source.BeanMapping; -import org.mapstruct.ap.internal.model.source.MappingOptions; +import org.mapstruct.ap.internal.model.source.BeanMappingOptions; +import org.mapstruct.ap.internal.model.source.MappingMethodOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; @@ -56,7 +56,7 @@ import org.mapstruct.ap.internal.processor.creation.MappingResolverImpl; import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.FormattingMessager; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.version.VersionInformation; @@ -93,7 +93,7 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, L this.typeFactory = context.getTypeFactory(); this.accessorNaming = context.getAccessorNaming(); - MapperConfiguration mapperConfig = MapperConfiguration.getInstanceOn( mapperTypeElement ); + MapperOptions mapperConfig = MapperOptions.getInstanceOn( mapperTypeElement ); List mapperReferences = initReferencedMappers( mapperTypeElement, mapperConfig ); MappingBuilderContext ctx = new MappingBuilderContext( @@ -126,7 +126,7 @@ public int getPriority() { return 1000; } - private List initReferencedMappers(TypeElement element, MapperConfiguration mapperConfig) { + private List initReferencedMappers(TypeElement element, MapperOptions mapperConfig) { List result = new LinkedList<>(); List variableNames = new LinkedList<>(); @@ -145,7 +145,7 @@ private List initReferencedMappers(TypeElement element, MapperC return result; } - private Mapper getMapper(TypeElement element, MapperConfiguration mapperConfig, List methods) { + private Mapper getMapper(TypeElement element, MapperOptions mapperConfig, List methods) { List mappingMethods = getMappingMethods( mapperConfig, methods ); mappingMethods.addAll( mappingContext.getUsedSupportedMappings() ); @@ -257,7 +257,7 @@ else if ( constructor.getParameters().size() == 1 ) { private SortedSet getExtraImports(TypeElement element) { SortedSet extraImports = new TreeSet<>(); - MapperConfiguration mapperConfiguration = MapperConfiguration.getInstanceOn( element ); + MapperOptions mapperConfiguration = MapperOptions.getInstanceOn( element ); for ( TypeMirror extraImport : mapperConfiguration.imports() ) { Type type = typeFactory.getType( extraImport ); @@ -272,7 +272,7 @@ private SortedSet getExtraImports(TypeElement element) { return extraImports; } - private List getMappingMethods(MapperConfiguration mapperConfig, List methods) { + private List getMappingMethods(MapperOptions mapperConfig, List methods) { List mappingMethods = new ArrayList<>(); for ( SourceMethod method : methods ) { @@ -282,7 +282,7 @@ private List getMappingMethods(MapperConfiguration mapperConfig, mergeInheritedOptions( method, mapperConfig, methods, new ArrayList<>() ); - MappingOptions mappingOptions = method.getMappingOptions(); + MappingMethodOptions mappingOptions = method.getMappingOptions(); boolean hasFactoryMethod = false; @@ -360,7 +360,7 @@ else if ( method.isStreamMapping() ) { } else { this.messager.note( 1, Message.BEANMAPPING_CREATE_NOTE, method ); - BuilderPrism builderPrism = BeanMapping.builderPrismFor( method ); + BuilderPrism builderPrism = BeanMappingOptions.builderPrismFor( method ); BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder(); BeanMappingMethod beanMappingMethod = builder .mappingContext( mappingContext ) @@ -387,7 +387,7 @@ else if ( method.isStreamMapping() ) { } private M createWithElementMappingMethod(SourceMethod method, - MappingOptions mappingOptions, ContainerMappingMethodBuilder builder) { + MappingMethodOptions mappingOptions, ContainerMappingMethodBuilder builder) { FormattingParameters formattingParameters = null; SelectionParameters selectionParameters = null; @@ -408,7 +408,7 @@ private M createWithElementMappingMethod(Sour .build(); } - private void mergeInheritedOptions(SourceMethod method, MapperConfiguration mapperConfig, + private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConfig, List availableMethods, List initializingMethods) { if ( initializingMethods.contains( method ) ) { // cycle detected @@ -424,7 +424,7 @@ private void mergeInheritedOptions(SourceMethod method, MapperConfiguration mapp initializingMethods.add( method ); - MappingOptions mappingOptions = method.getMappingOptions(); + MappingMethodOptions mappingOptions = method.getMappingOptions(); List applicableReversePrototypeMethods = method.getApplicableReversePrototypeMethods(); SourceMethod inverseTemplateMethod = @@ -512,7 +512,7 @@ private void reportErrorIfNoImplementationTypeIsRegisteredForInterfaceReturnType */ private SourceMethod getInverseTemplateMethod(List rawMethods, SourceMethod method, List initializingMethods, - MapperConfiguration mapperConfig) { + MapperOptions mapperConfig) { SourceMethod resultMethod = null; InheritInverseConfigurationPrism inversePrism = InheritInverseConfigurationPrism.getInstanceOn( method.getExecutable() @@ -568,7 +568,7 @@ else if ( nameFilteredcandidates.size() > 1 ) { private SourceMethod extractInitializedOptions(SourceMethod resultMethod, List rawMethods, - MapperConfiguration mapperConfig, + MapperOptions mapperConfig, List initializingMethods) { if ( resultMethod != null ) { if ( !resultMethod.getMappingOptions().isFullyInitialized() ) { @@ -589,7 +589,7 @@ private SourceMethod extractInitializedOptions(SourceMethod resultMethod, */ private SourceMethod getForwardTemplateMethod(List rawMethods, SourceMethod method, List initializingMethods, - MapperConfiguration mapperConfig) { + MapperOptions mapperConfig) { SourceMethod resultMethod = null; InheritConfigurationPrism forwardPrism = InheritConfigurationPrism.getInstanceOn( method.getExecutable() diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java index a2b7cf747e..967d13f5f6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java @@ -15,7 +15,7 @@ import org.mapstruct.ap.internal.model.GeneratedType; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.ServicesEntry; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.writer.ModelWriter; /** @@ -37,7 +37,7 @@ public Void process(ProcessorContext context, TypeElement mapperTypeElement, Map } else { String componentModel = - MapperConfiguration.getInstanceOn( mapperTypeElement ).componentModel( context.getOptions() ); + MapperOptions.getInstanceOn( mapperTypeElement ).componentModel( context.getOptions() ); spiGenerationNeeded = "default".equals( componentModel ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 786ec0a128..dac5bf49d1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -26,9 +26,9 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.model.source.BeanMapping; -import org.mapstruct.ap.internal.model.source.IterableMapping; -import org.mapstruct.ap.internal.model.source.MapMapping; +import org.mapstruct.ap.internal.model.source.BeanMappingOptions; +import org.mapstruct.ap.internal.model.source.IterableMappingOptions; +import org.mapstruct.ap.internal.model.source.MapMappingOptions; import org.mapstruct.ap.internal.model.source.Mapping; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SourceMethod; @@ -45,7 +45,7 @@ import org.mapstruct.ap.internal.util.AnnotationProcessingException; import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.FormattingMessager; -import org.mapstruct.ap.internal.util.MapperConfiguration; +import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; import static org.mapstruct.ap.internal.util.Executables.getAllEnclosedExecutableElements; @@ -81,7 +81,7 @@ public List process(ProcessorContext context, TypeElement mapperTy this.messager.note( 0, Message.PROCESSING_NOTE, mapperTypeElement ); - MapperConfiguration mapperConfig = MapperConfiguration.getInstanceOn( mapperTypeElement ); + MapperOptions mapperConfig = MapperOptions.getInstanceOn( mapperTypeElement ); if ( mapperConfig != null ) { this.messager.note( 0, Message.CONFIG_NOTE, mapperConfig.getClass().getName() ); @@ -104,7 +104,7 @@ public int getPriority() { } private List retrievePrototypeMethods(TypeElement mapperTypeElement, - MapperConfiguration mapperConfig) { + MapperOptions mapperConfig) { if ( mapperConfig.config() == null ) { return Collections.emptyList(); } @@ -149,7 +149,7 @@ private List retrievePrototypeMethods(TypeElement mapperTypeElemen * @return All mapping methods declared by the given type */ private List retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement, - MapperConfiguration mapperConfig, List prototypeMethods) { + MapperOptions mapperConfig, List prototypeMethods) { List methods = new ArrayList<>(); for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, usedMapper ) ) { @@ -186,7 +186,7 @@ private TypeElement asTypeElement(DeclaredType type) { private SourceMethod getMethod(TypeElement usedMapper, ExecutableElement method, TypeElement mapperToImplement, - MapperConfiguration mapperConfig, + MapperOptions mapperConfig, List prototypeMethods) { ExecutableType methodType = typeFactory.getMethodType( (DeclaredType) usedMapper.asType(), method ); @@ -219,7 +219,7 @@ else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( method, private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, ExecutableElement method, List parameters, boolean containsTargetTypeParameter, - MapperConfiguration mapperConfig, + MapperOptions mapperConfig, List prototypeMethods, TypeElement mapperToImplement) { Type returnType = typeFactory.getReturnType( methodType ); @@ -253,16 +253,16 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, .setExceptionTypes( exceptionTypes ) .setMappings( getMappings( method, method, new LinkedHashSet<>(), new HashSet<>() ) ) .setIterableMapping( - IterableMapping.fromPrism( + IterableMappingOptions.fromPrism( IterableMappingPrism.getInstanceOn( method ), method, messager, typeUtils ) ) .setMapMapping( - MapMapping.fromPrism( MapMappingPrism.getInstanceOn( method ), method, messager, typeUtils ) ) + MapMappingOptions.fromPrism( MapMappingPrism.getInstanceOn( method ), method, messager, typeUtils ) ) .setBeanMapping( - new BeanMapping.Builder() + new BeanMappingOptions.Builder() .beanMappingPrism( BeanMappingPrism.getInstanceOn( method ) ) .messager( messager ) .method( method ) @@ -280,7 +280,7 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, } private ParameterProvidedMethods retrieveContextProvidedMethods( - List contextParameters, TypeElement mapperToImplement, MapperConfiguration mapperConfig) { + List contextParameters, TypeElement mapperToImplement, MapperOptions mapperConfig) { ParameterProvidedMethods.Builder builder = ParameterProvidedMethods.builder(); for ( Parameter contextParam : contextParameters ) { From b84526b24abeddc1b2d113c4780498a0c4b62c03 Mon Sep 17 00:00:00 2001 From: sjaakd Date: Sat, 25 Jan 2020 08:08:49 +0100 Subject: [PATCH 020/597] #1998 preparation move classes to new names 2 --- .../ap/internal/model/BeanMappingMethod.java | 4 +- .../NestedTargetPropertyMappingHolder.java | 6 +-- .../model/beanmapping/MappingReference.java | 10 ++--- .../model/beanmapping/MappingReferences.java | 6 +-- .../model/beanmapping/SourceReference.java | 4 +- .../model/beanmapping/TargetReference.java | 4 +- .../model/source/MappingMethodOptions.java | 24 +++++----- .../{Mapping.java => MappingOptions.java} | 44 +++++++++---------- .../internal/model/source/SourceMethod.java | 4 +- .../processor/MethodRetrievalProcessor.java | 10 ++--- 10 files changed, 58 insertions(+), 58 deletions(-) rename processor/src/main/java/org/mapstruct/ap/internal/model/source/{Mapping.java => MappingOptions.java} (91%) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index c7783c4bb7..4e350bcb2c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -36,7 +36,7 @@ import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder; import org.mapstruct.ap.internal.model.source.BeanMappingOptions; -import org.mapstruct.ap.internal.model.source.Mapping; +import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.MappingMethodOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; @@ -536,7 +536,7 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Set ha PropertyMapping propertyMapping = null; TargetReference targetRef = mappingRef.getTargetReference(); - Mapping mapping = mappingRef.getMapping(); + MappingOptions mapping = mappingRef.getMapping(); PropertyEntry targetProperty = first( targetRef.getPropertyEntries() ); String targetPropertyName = targetProperty.getName(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index 3c8b1bec61..46bf0b3d3d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -21,7 +21,7 @@ import org.mapstruct.ap.internal.model.beanmapping.SourceReference; import org.mapstruct.ap.internal.model.beanmapping.TargetReference; import org.mapstruct.ap.internal.model.common.Parameter; -import org.mapstruct.ap.internal.model.source.Mapping; +import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; import static org.mapstruct.ap.internal.util.Collections.first; @@ -294,7 +294,7 @@ private void handleSourceParameterMappings(Set sourceParameter } /** - * The target references are popped. The {@code List<}{@link Mapping}{@code >} are keyed on the unique first + * The target references are popped. The {@code List<}{@link MappingOptions}{@code >} are keyed on the unique first * entries of the target references. * *

@@ -503,7 +503,7 @@ private GroupedBySourceParameters groupBySourceParameter(Set m * * * - * @param entryByParam the entry of a {@link Parameter} and it's associated {@link Mapping}(s) that need to + * @param entryByParam the entry of a {@link Parameter} and it's associated {@link MappingOptions}(s) that need to * be used for grouping on popped source references * @param singleTargetReferences the single target references that match the source mappings * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java index 1ea5ff500e..2675e29007 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java @@ -7,26 +7,26 @@ import java.util.Objects; -import org.mapstruct.ap.internal.model.source.Mapping; +import org.mapstruct.ap.internal.model.source.MappingOptions; /** - * Represents the intermediate (nesting) state of the {@link Mapping} in this class. + * Represents the intermediate (nesting) state of the {@link MappingOptions} in this class. */ public class MappingReference { - private Mapping mapping; + private MappingOptions mapping; private TargetReference targetReference; private SourceReference sourceReference; - public MappingReference(Mapping mapping, TargetReference targetReference, SourceReference sourceReference) { + public MappingReference(MappingOptions mapping, TargetReference targetReference, SourceReference sourceReference) { this.mapping = mapping; this.targetReference = targetReference; this.sourceReference = sourceReference; } - public Mapping getMapping() { + public MappingOptions getMapping() { return mapping; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java index 424cf99b62..ef6ae343e8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java @@ -12,7 +12,7 @@ import java.util.Set; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.model.source.Mapping; +import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.util.FormattingMessager; @@ -35,7 +35,7 @@ public static MappingReferences forSourceMethod(SourceMethod sourceMethod, Forma Set references = new LinkedHashSet<>(); List targetThisReferences = new ArrayList<>( ); - for ( Mapping mapping : sourceMethod.getMappingOptions().getMappings() ) { + for ( MappingOptions mapping : sourceMethod.getMappingOptions().getMappings() ) { // handle source reference SourceReference sourceReference = new SourceReference.BuilderFromMapping().mapping( mapping ) @@ -139,7 +139,7 @@ public List getTargetThisReferences() { * @return */ private static boolean isValidWhenInversed(MappingReference mappingRef) { - Mapping mapping = mappingRef.getMapping(); + MappingOptions mapping = mappingRef.getMapping(); if ( mapping.getInheritContext() != null && mapping.getInheritContext().isReversed() ) { return mappingRef.getTargetReference().isValid() && ( mappingRef.getSourceReference() == null || mappingRef.getSourceReference().isValid() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index c06d27c540..a0bae0c919 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -18,7 +18,7 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.model.source.Mapping; +import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; @@ -72,7 +72,7 @@ public BuilderFromMapping messager(FormattingMessager messager) { return this; } - public BuilderFromMapping mapping(Mapping mapping) { + public BuilderFromMapping mapping(MappingOptions mapping) { this.sourceName = mapping.getSourceName(); this.annotationMirror = mapping.getMirror(); this.sourceAnnotationValue = mapping.getSourceAnnotationValue(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java index 55f43f33dd..7937fbef51 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java @@ -19,7 +19,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.BeanMappingOptions; -import org.mapstruct.ap.internal.model.source.Mapping; +import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.prism.BuilderPrism; import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; @@ -84,7 +84,7 @@ public Builder messager(FormattingMessager messager) { return this; } - public Builder mapping(Mapping mapping) { + public Builder mapping(MappingOptions mapping) { if ( mapping.getInheritContext() != null ) { this.isReversed = mapping.getInheritContext().isReversed(); this.templateMethod = mapping.getInheritContext().getInheritedFromMethod(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 2786e221b1..75e44c2ed5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -17,7 +17,7 @@ import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.internal.util.accessor.Accessor; -import static org.mapstruct.ap.internal.model.source.Mapping.getMappingTargetNamesBy; +import static org.mapstruct.ap.internal.model.source.MappingOptions.getMappingTargetNamesBy; /** * Encapsulates all options specifiable on a mapping method @@ -32,14 +32,14 @@ public class MappingMethodOptions { Collections.emptyList() ); - private Set mappings; + private Set mappings; private IterableMappingOptions iterableMapping; private MapMappingOptions mapMapping; private BeanMappingOptions beanMapping; private List valueMappings; private boolean fullyInitialized; - public MappingMethodOptions(Set mappings, IterableMappingOptions iterableMapping, MapMappingOptions mapMapping, + public MappingMethodOptions(Set mappings, IterableMappingOptions iterableMapping, MapMappingOptions mapMapping, BeanMappingOptions beanMapping, List valueMappings ) { this.mappings = mappings; this.iterableMapping = iterableMapping; @@ -58,10 +58,10 @@ public static MappingMethodOptions empty() { } /** - * @return the {@link Mapping}s configured for this method, keyed by target property name. Only for enum mapping + * @return the {@link MappingOptions}s configured for this method, keyed by target property name. Only for enum mapping * methods a target will be mapped by several sources. TODO. Remove the value list when 2.0 */ - public Set getMappings() { + public Set getMappings() { return mappings; } @@ -153,8 +153,8 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } } - Set newMappings = new LinkedHashSet<>(); - for ( Mapping mapping : inherited.getMappings() ) { + Set newMappings = new LinkedHashSet<>(); + for ( MappingOptions mapping : inherited.getMappings() ) { if ( isInverse ) { if ( mapping.canInverse() ) { newMappings.add( mapping.copyForInverseInheritance( templateMethod ) ); @@ -191,25 +191,25 @@ public void applyIgnoreAll(SourceMethod method, TypeFactory typeFactory ) { for ( String targetPropertyName : writeAccessors.keySet() ) { if ( !mappedPropertyNames.contains( targetPropertyName ) ) { - Mapping mapping = Mapping.forIgnore( targetPropertyName ); + MappingOptions mapping = MappingOptions.forIgnore( targetPropertyName ); mappings.add( mapping ); } } } - private void filterNestedTargetIgnores( Set mappings) { + private void filterNestedTargetIgnores( Set mappings) { // collect all properties to ignore, and safe their target name ( == same name as first ref target property) - Set ignored = getMappingTargetNamesBy( Mapping::isIgnored, mappings ); + Set ignored = getMappingTargetNamesBy( MappingOptions::isIgnored, mappings ); mappings.removeIf( m -> isToBeIgnored( ignored, m ) ); } - private boolean isToBeIgnored(Set ignored, Mapping mapping) { + private boolean isToBeIgnored(Set ignored, MappingOptions mapping) { String[] propertyEntries = getPropertyEntries( mapping ); return propertyEntries.length > 1 && ignored.contains( propertyEntries[ 0 ] ); } - private String[] getPropertyEntries( Mapping mapping ) { + private String[] getPropertyEntries( MappingOptions mapping ) { return mapping.getTargetName().split( "\\." ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java similarity index 91% rename from processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 21ece8e8f0..74a9825721 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -31,7 +31,7 @@ * * @author Gunnar Morling */ -public class Mapping { +public class MappingOptions { private static final Pattern JAVA_EXPRESSION = Pattern.compile( "^java\\((.*)\\)$" ); @@ -81,19 +81,19 @@ public Method getInheritedFromMethod() { } } - public static Set getMappingTargetNamesBy(Predicate predicate, Set mappings) { + public static Set getMappingTargetNamesBy(Predicate predicate, Set mappings) { return mappings.stream() .filter( predicate ) - .map( Mapping::getTargetName ) + .map( MappingOptions::getTargetName ) .collect( Collectors.toCollection( LinkedHashSet::new ) ); } - public static Mapping getMappingByTargetName(String targetName, Set mappings) { + public static MappingOptions getMappingByTargetName(String targetName, Set mappings) { return mappings.stream().filter( mapping -> mapping.targetName.equals( targetName ) ).findAny().orElse( null ); } public static void addFromMappingsPrism(MappingsPrism mappingsAnnotation, ExecutableElement method, - FormattingMessager messager, Types typeUtils, Set mappings) { + FormattingMessager messager, Types typeUtils, Set mappings) { for ( MappingPrism mappingPrism : mappingsAnnotation.value() ) { addFromMappingPrism( mappingPrism, method, messager, typeUtils, mappings ); @@ -101,7 +101,7 @@ public static void addFromMappingsPrism(MappingsPrism mappingsAnnotation, Execut } public static void addFromMappingPrism(MappingPrism mappingPrism, ExecutableElement method, - FormattingMessager messager, Types typeUtils, Set mappings) { + FormattingMessager messager, Types typeUtils, Set mappings) { if (!isConsistent( mappingPrism, method, messager ) ) { return; @@ -144,7 +144,7 @@ public static void addFromMappingPrism(MappingPrism mappingPrism, ExecutableElem ? null : NullValuePropertyMappingStrategyPrism.valueOf( mappingPrism.nullValuePropertyMappingStrategy() ); - Mapping mapping = new Mapping( + MappingOptions mapping = new MappingOptions( source, constant, expression, @@ -172,8 +172,8 @@ public static void addFromMappingPrism(MappingPrism mappingPrism, ExecutableElem } } - public static Mapping forIgnore( String targetName) { - return new Mapping( + public static MappingOptions forIgnore(String targetName) { + return new MappingOptions( null, null, null, @@ -267,14 +267,14 @@ else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null } @SuppressWarnings("checkstyle:parameternumber") - private Mapping(String sourceName, String constant, String javaExpression, String defaultJavaExpression, - String targetName, String defaultValue, boolean isIgnored, AnnotationMirror mirror, - AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue, - FormattingParameters formattingParameters, SelectionParameters selectionParameters, - AnnotationValue dependsOnAnnotationValue, Set dependsOn, - NullValueCheckStrategyPrism nullValueCheckStrategy, - NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy, - InheritContext inheritContext) { + private MappingOptions(String sourceName, String constant, String javaExpression, String defaultJavaExpression, + String targetName, String defaultValue, boolean isIgnored, AnnotationMirror mirror, + AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue, + FormattingParameters formattingParameters, SelectionParameters selectionParameters, + AnnotationValue dependsOnAnnotationValue, Set dependsOn, + NullValueCheckStrategyPrism nullValueCheckStrategy, + NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy, + InheritContext inheritContext) { this.sourceName = sourceName; this.constant = constant; this.javaExpression = javaExpression; @@ -421,9 +421,9 @@ public boolean canInverse() { return constant == null && javaExpression == null && !( isIgnored && sourceName == null ); } - public Mapping copyForInverseInheritance(SourceMethod method ) { + public MappingOptions copyForInverseInheritance(SourceMethod method ) { - return new Mapping( + return new MappingOptions( sourceName != null ? targetName : null, null, // constant null, // expression @@ -450,8 +450,8 @@ public Mapping copyForInverseInheritance(SourceMethod method ) { * * @return the copy */ - public Mapping copyForForwardInheritance( SourceMethod method ) { - return new Mapping( + public MappingOptions copyForForwardInheritance(SourceMethod method ) { + return new MappingOptions( sourceName, constant, javaExpression, @@ -485,7 +485,7 @@ public boolean equals(Object o) { if ( o == null || getClass() != o.getClass() ) { return false; } - Mapping mapping = (Mapping) o; + MappingOptions mapping = (MappingOptions) o; return targetName.equals( mapping.targetName ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 0de68bd514..3048b86add 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -77,7 +77,7 @@ public static class Builder { private List parameters; private Type returnType = null; private List exceptionTypes; - private Set mappings; + private Set mappings; private IterableMappingOptions iterableMapping = null; private MapMappingOptions mapMapping = null; private BeanMappingOptions beanMapping = null; @@ -113,7 +113,7 @@ public Builder setExceptionTypes(List exceptionTypes) { return this; } - public Builder setMappings(Set mappings) { + public Builder setMappings(Set mappings) { this.mappings = mappings; return this; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index dac5bf49d1..fcc365b3eb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -29,7 +29,7 @@ import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.IterableMappingOptions; import org.mapstruct.ap.internal.model.source.MapMappingOptions; -import org.mapstruct.ap.internal.model.source.Mapping; +import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.ValueMapping; @@ -505,20 +505,20 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List getMappings(ExecutableElement method, Element element, Set mappings, - Set handledElements) { + private Set getMappings(ExecutableElement method, Element element, Set mappings, + Set handledElements) { for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { Element lElement = annotationMirror.getAnnotationType().asElement(); if ( isAnnotation( lElement, MAPPING_FQN ) ) { // although getInstanceOn does a search on annotation mirrors, the order is preserved MappingPrism mappingAnnotation = MappingPrism.getInstanceOn( element ); - Mapping.addFromMappingPrism( mappingAnnotation, method, messager, typeUtils, mappings ); + MappingOptions.addFromMappingPrism( mappingAnnotation, method, messager, typeUtils, mappings ); } else if ( isAnnotation( lElement, MAPPINGS_FQN ) ) { // although getInstanceOn does a search on annotation mirrors, the order is preserved MappingsPrism mappingsAnnotation = MappingsPrism.getInstanceOn( element ); - Mapping.addFromMappingsPrism( mappingsAnnotation, method, messager, typeUtils, mappings ); + MappingOptions.addFromMappingsPrism( mappingsAnnotation, method, messager, typeUtils, mappings ); } else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) && !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG ) From 6b49c838ebdd12a3934c44f0e5c9bf925f346e64 Mon Sep 17 00:00:00 2001 From: sjaakd Date: Tue, 24 Dec 2019 13:18:03 +0100 Subject: [PATCH 021/597] #1998 simplify usage of inheritance of annotation fields --- .../conversion/CreateDecimalFormat.java | 2 +- .../internal/model/AbstractBaseBuilder.java | 7 +- .../model/AbstractMappingMethodBuilder.java | 10 +- .../ap/internal/model/BeanMappingMethod.java | 39 +-- .../model/BuilderFinisherMethodResolver.java | 3 +- .../model/CollectionAssignmentBuilder.java | 2 +- .../model/ContainerMappingMethodBuilder.java | 19 +- .../ap/internal/model/ForgedMethod.java | 12 +- .../ap/internal/model/HelperMethod.java | 6 - .../ap/internal/model/MapMappingMethod.java | 6 +- .../NestedTargetPropertyMappingHolder.java | 12 +- .../ap/internal/model/PropertyMapping.java | 46 +-- .../ap/internal/model/ValueMappingMethod.java | 28 +- .../model/beanmapping/MappingReferences.java | 2 +- .../model/beanmapping/SourceReference.java | 2 +- .../model/beanmapping/TargetReference.java | 9 +- .../model/source/BeanMappingOptions.java | 231 +++++-------- .../internal/model/source/DefaultOptions.java | 128 +++++++ .../model/source/DelegatingOptions.java | 117 +++++++ .../model/source/IterableMappingOptions.java | 84 ++--- .../model/source/MapMappingOptions.java | 108 +++--- .../model/source/MapperConfigOptions.java | 131 +++++++ .../internal/model/source/MapperOptions.java | 326 ++++++------------ .../model/source/MappingMethodOptions.java | 65 ++-- .../internal/model/source/MappingOptions.java | 299 ++++++++-------- .../ap/internal/model/source/Method.java | 10 +- .../internal/model/source/SourceMethod.java | 44 +-- ...eMapping.java => ValueMappingOptions.java} | 22 +- .../model/source/builtin/BuiltInMethod.java | 8 +- .../prism/NullValueMappingStrategyPrism.java | 14 +- ...nnotationBasedComponentModelProcessor.java | 8 +- .../processor/MapperCreationProcessor.java | 63 ++-- .../processor/MapperServiceProcessor.java | 5 +- .../processor/MethodRetrievalProcessor.java | 122 ++++--- .../creation/MappingResolverImpl.java | 2 +- .../ap/test/verbose/CreateBeanMapping.java | 2 +- .../test/verbose/CreateBeanMappingConfig.java | 12 + .../ap/test/verbose/VerboseTest.java | 12 +- 38 files changed, 1082 insertions(+), 936 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java rename processor/src/main/java/org/mapstruct/ap/internal/model/source/{ValueMapping.java => ValueMappingOptions.java} (83%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/verbose/CreateBeanMappingConfig.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java index 59f85d6b2c..9005907d09 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java @@ -52,7 +52,7 @@ public Type getReturnType() { } @Override - public MappingMethodOptions getMappingOptions() { + public MappingMethodOptions getOptions() { return MappingMethodOptions.empty(); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java index b2d13dede4..7bf720de71 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.model; import javax.lang.model.element.AnnotationMirror; + import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.BuilderType; import org.mapstruct.ap.internal.model.common.ParameterBinding; @@ -13,7 +14,6 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.MappingMethodUtils; import org.mapstruct.ap.internal.model.source.Method; -import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; /** @@ -60,8 +60,7 @@ boolean canGenerateAutoSubMappingBetween(Type sourceType, Type targetType) { } private boolean isDisableSubMappingMethodsGeneration() { - MapperOptions configuration = MapperOptions.getInstanceOn( ctx.getMapperTypeElement() ); - return configuration.isDisableSubMappingMethodsGeneration(); + return method.getOptions().getMapper().isDisableSubMappingMethodsGeneration(); } /** @@ -86,7 +85,7 @@ Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, if ( MappingMethodUtils.isEnumMapping( forgedMethod ) ) { forgedMappingMethod = new ValueMappingMethod.Builder() .method( forgedMethod ) - .valueMappings( forgedMethod.getMappingOptions().getValueMappings() ) + .valueMappings( forgedMethod.getOptions().getValueMappings() ) .mappingContext( ctx ) .build(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index f9c53891c7..8f0092578c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -8,7 +8,7 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.source.BeanMappingOptions; +import org.mapstruct.ap.internal.prism.BuilderPrism; import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping; @@ -54,12 +54,12 @@ Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) { sourceRHS.getSourceErrorMessagePart() ); ForgedMethod forgedMethod = forElementMapping( name, sourceType, targetType, method, forgedHistory, true ); + BuilderPrism builderPrism = method.getOptions().getBeanMapping().getBuilderPrism(); return createForgedAssignment( - sourceRHS, - ctx.getTypeFactory() - .builderTypeFor( targetType, BeanMappingOptions.builderPrismFor( method ) ), - forgedMethod + sourceRHS, + ctx.getTypeFactory().builderTypeFor( targetType, builderPrism ), + forgedMethod ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 4e350bcb2c..e193b19ffa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -37,14 +37,11 @@ import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder; import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.MappingOptions; -import org.mapstruct.ap.internal.model.source.MappingMethodOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; -import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; @@ -121,7 +118,7 @@ public Builder forgedMethod(ForgedMethod forgedMethod) { public BeanMappingMethod build() { - BeanMappingOptions beanMapping = method.getMappingOptions().getBeanMapping(); + BeanMappingOptions beanMapping = method.getOptions().getBeanMapping(); SelectionParameters selectionParameters = beanMapping != null ? beanMapping.getSelectionParameters() : null; /* the return type that needs to be constructed (new or factorized), so for instance: */ @@ -161,7 +158,7 @@ else if ( !method.isUpdateMethod() ) { /* the type that needs to be used in the mapping process as target */ Type resultTypeToMap = returnTypeToConstruct == null ? method.getResultType() : returnTypeToConstruct; - CollectionMappingStrategyPrism cms = this.method.getMapperConfiguration().getCollectionMappingStrategy(); + CollectionMappingStrategyPrism cms = this.method.getOptions().getMapper().getCollectionMappingStrategy(); // determine accessors Map accessors = resultTypeToMap.getPropertyWriteAccessors( cms ); @@ -216,9 +213,10 @@ else if ( !method.isUpdateMethod() ) { reportErrorForUnmappedSourcePropertiesIfRequired(); // mapNullToDefault - NullValueMappingStrategyPrism nullValueMappingStrategy = - beanMapping != null ? beanMapping.getNullValueMappingStrategy() : null; - boolean mapNullToDefault = method.getMapperConfiguration().isMapToDefault( nullValueMappingStrategy ); + boolean mapNullToDefault = method.getOptions() + .getBeanMapping() + .getNullValueMappingStrategy() + .isReturnDefault(); // sort sortPropertyMappingsByDependencies(); @@ -388,7 +386,7 @@ private boolean canResultTypeFromBeanMappingBeConstructed(Type resultType) { if ( resultType.isAbstract() ) { ctx.getMessager().printMessage( method.getExecutable(), - method.getMappingOptions().getBeanMapping().getMirror(), + method.getOptions().getBeanMapping().getMirror(), BEANMAPPING_ABSTRACT, resultType, method.getResultType() @@ -398,7 +396,7 @@ private boolean canResultTypeFromBeanMappingBeConstructed(Type resultType) { else if ( !resultType.isAssignableTo( method.getResultType() ) ) { ctx.getMessager().printMessage( method.getExecutable(), - method.getMappingOptions().getBeanMapping().getMirror(), + method.getOptions().getBeanMapping().getMirror(), BEANMAPPING_NOT_ASSIGNABLE, resultType, method.getResultType() @@ -408,7 +406,7 @@ else if ( !resultType.isAssignableTo( method.getResultType() ) ) { else if ( !resultType.hasEmptyAccessibleConstructor() ) { ctx.getMessager().printMessage( method.getExecutable(), - method.getMappingOptions().getBeanMapping().getMirror(), + method.getOptions().getBeanMapping().getMirror(), Message.GENERAL_NO_SUITABLE_CONSTRUCTOR, resultType ); @@ -630,8 +628,7 @@ else if ( mapping.getJavaExpression() != null ) { .defaultValue( mapping.getDefaultValue() ) .defaultJavaExpression( mapping.getDefaultJavaExpression() ) .mirror( mapping.getMirror() ) - .nullValueCheckStrategy( mapping.getNullValueCheckStrategy() ) - .nullValuePropertyMappingStrategy( mapping.getNullValuePropertyMappingStrategy() ) + .options( mapping ) .build(); handledTargets.add( targetPropertyName ); unprocessedSourceParameters.remove( sourceRef.getParameter() ); @@ -732,6 +729,7 @@ private void applyPropertyNameBasedMapping(List sourceReference .sourceReference( sourceRef ) .existingVariableNames( existingVariableNames ) .forgeMethodWithMappingReferences( mappingRefs ) + .options( method.getOptions().getBeanMapping() ) .build(); unprocessedSourceParameters.remove( sourceRef.getParameter() ); @@ -777,6 +775,7 @@ private void applyParameterNameBasedMapping() { .sourceReference( sourceRef ) .existingVariableNames( existingVariableNames ) .forgeMethodWithMappingReferences( mappingRefs ) + .options( method.getOptions().getBeanMapping() ) .build(); propertyMappings.add( propertyMapping ); @@ -828,15 +827,7 @@ private ReportingPolicyPrism getUnmappedTargetPolicy() { if ( mappingReferences.isForForgedMethods() ) { return ReportingPolicyPrism.IGNORE; } - MappingMethodOptions mappingOptions = method.getMappingOptions(); - if ( mappingOptions.getBeanMapping() != null && - mappingOptions.getBeanMapping().getReportingPolicy() != null ) { - return mappingOptions.getBeanMapping().getReportingPolicy(); - } - - MapperOptions mapperSettings = MapperOptions.getInstanceOn( ctx.getMapperTypeElement() ); - - return mapperSettings.unmappedTargetPolicy( ctx.getOptions() ); + return method.getOptions().getMapper().unmappedTargetPolicy(); } private void reportErrorForUnmappedTargetPropertiesIfRequired() { @@ -917,9 +908,7 @@ private ReportingPolicyPrism getUnmappedSourcePolicy() { if ( mappingReferences.isForForgedMethods() ) { return ReportingPolicyPrism.IGNORE; } - MapperOptions mapperSettings = MapperOptions.getInstanceOn( ctx.getMapperTypeElement() ); - - return mapperSettings.unmappedSourcePolicy(); + return method.getOptions().getMapper().unmappedSourcePolicy(); } private void reportErrorForUnmappedSourcePropertiesIfRequired() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java index b3b63390ef..e1a586c522 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java @@ -9,7 +9,6 @@ import javax.lang.model.element.ExecutableElement; import org.mapstruct.ap.internal.model.common.BuilderType; -import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.prism.BuilderPrism; import org.mapstruct.ap.internal.util.Message; @@ -35,7 +34,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp return null; } - BuilderPrism builderMapping = BeanMappingOptions.builderPrismFor( method ); + BuilderPrism builderMapping = method.getOptions().getBeanMapping().getBuilderPrism(); if ( builderMapping == null && buildMethods.size() == 1 ) { return MethodReference.forMethodCall( first( buildMethods ).getSimpleName().toString() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index 6422a354d4..1acacad461 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -127,7 +127,7 @@ public CollectionAssignmentBuilder nullValuePropertyMappingStrategy( NullValuePr public Assignment build() { Assignment result = assignment; - CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy(); + CollectionMappingStrategyPrism cms = method.getOptions().getMapper().getCollectionMappingStrategy(); boolean targetImmutable = cms == CollectionMappingStrategyPrism.TARGET_IMMUTABLE || targetReadAccessor == null; if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java index f5ad077378..e669e69aad 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.internal.model; -import static org.mapstruct.ap.internal.util.Collections.first; - import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -19,10 +17,11 @@ import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; +import static org.mapstruct.ap.internal.util.Collections.first; + /** * Builder that can be used to build {@link ContainerMappingMethod}(s). * @@ -36,7 +35,6 @@ public abstract class ContainerMappingMethodBuilder sourceParameter boolean forceUpdateMethod) { if ( !sourceParameterMappings.isEmpty() ) { // The source parameter mappings have no mappings, the source name is actually the parameter itself - MappingReferences nonNestedRefs = new MappingReferences( Collections.emptySet(), false, true ); + MappingReferences nonNestedRefs = + new MappingReferences( Collections.emptySet(), false, true ); SourceReference reference = new SourceReference.BuilderFromProperty() .sourceParameter( sourceParameter ) .name( targetProperty.getName() ) @@ -294,8 +295,8 @@ private void handleSourceParameterMappings(Set sourceParameter } /** - * The target references are popped. The {@code List<}{@link MappingOptions}{@code >} are keyed on the unique first - * entries of the target references. + * The target references are popped. The {@code List<}{@link MappingOptions}{@code >} are keyed on the unique + * first entries of the target references. * *

*

@@ -503,8 +504,8 @@ private GroupedBySourceParameters groupBySourceParameter(Set m * * * - * @param entryByParam the entry of a {@link Parameter} and it's associated {@link MappingOptions}(s) that need to - * be used for grouping on popped source references + * @param entryByParam the entry of a {@link Parameter} and it's associated {@link MappingOptions}(s) that need + * to be used for grouping on popped source references * @param singleTargetReferences the single target references that match the source mappings * * @return the Grouped Source References @@ -638,6 +639,7 @@ private PropertyMapping createPropertyMappingForNestedTarget(MappingReferences m .forgeMethodWithMappingReferences( mappingReferences ) .forceUpdateMethod( forceUpdateMethod ) .forgedNamedBased( false ) + .options( method.getOptions().getBeanMapping() ) .build(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 7aec07d46d..f125338528 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -20,6 +20,9 @@ import org.mapstruct.ap.internal.model.assignment.SetterWrapper; import org.mapstruct.ap.internal.model.assignment.StreamAdderWrapper; import org.mapstruct.ap.internal.model.assignment.UpdateWrapper; +import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; +import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry; +import org.mapstruct.ap.internal.model.beanmapping.SourceReference; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.BuilderType; import org.mapstruct.ap.internal.model.common.FormattingParameters; @@ -27,18 +30,13 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.source.BeanMappingOptions; -import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; +import org.mapstruct.ap.internal.model.source.DelegatingOptions; import org.mapstruct.ap.internal.model.source.Method; -import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry; import org.mapstruct.ap.internal.model.source.SelectionParameters; -import org.mapstruct.ap.internal.model.beanmapping.SourceReference; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; import org.mapstruct.ap.internal.prism.BuilderPrism; import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; -import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.NativeTypes; import org.mapstruct.ap.internal.util.Strings; @@ -46,10 +44,10 @@ import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; -import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT; -import static org.mapstruct.ap.internal.model.ForgedMethod.forParameterMapping; import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping; +import static org.mapstruct.ap.internal.model.ForgedMethod.forParameterMapping; import static org.mapstruct.ap.internal.model.ForgedMethod.forPropertyMapping; +import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT; import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT; import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL; @@ -111,7 +109,7 @@ public T targetReadAccessor(Accessor targetReadAccessor) { public T targetWriteAccessor(Accessor targetWriteAccessor) { this.targetWriteAccessor = targetWriteAccessor; this.targetType = ctx.getTypeFactory().getType( targetWriteAccessor.getAccessedType() ); - BuilderPrism builderPrism = BeanMappingOptions.builderPrismFor( method ); + BuilderPrism builderPrism = method.getOptions().getBeanMapping().getBuilderPrism(); this.targetBuilderType = ctx.getTypeFactory().builderTypeFor( this.targetType, builderPrism ); this.targetWriteAccessorType = targetWriteAccessor.getAccessorType(); return (T) this; @@ -160,7 +158,6 @@ public static class PropertyMappingBuilder extends MappingBuilderBase valueMappings) { + public Builder valueMappings(List valueMappings) { this.valueMappings = new ValueMappings( valueMappings ); return this; } @@ -111,7 +111,7 @@ private List enumToEnumMapping(Method method, Type sourceType, Typ } // Start to fill the mappings with the defined value mappings - for ( ValueMapping valueMapping : valueMappings.regularValueMappings ) { + for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) { String target = NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); @@ -164,7 +164,7 @@ private List enumToStringMapping(Method method, Type sourceType ) } // Start to fill the mappings with the defined valuemappings - for ( ValueMapping valueMapping : valueMappings.regularValueMappings ) { + for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) { String target = NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); @@ -193,7 +193,7 @@ private List stringToEnumMapping(Method method, Type targetType ) } // Start to fill the mappings with the defined valuemappings - for ( ValueMapping valueMapping : valueMappings.regularValueMappings ) { + for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) { String target = NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); @@ -227,7 +227,7 @@ private boolean reportErrorIfMappedSourceEnumConstantsDontExist(Method method, T boolean foundIncorrectMapping = false; - for ( ValueMapping mappedConstant : valueMappings.regularValueMappings ) { + for ( ValueMappingOptions mappedConstant : valueMappings.regularValueMappings ) { if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -277,7 +277,7 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T boolean foundIncorrectMapping = false; - for ( ValueMapping mappedConstant : valueMappings.regularValueMappings ) { + for ( ValueMappingOptions mappedConstant : valueMappings.regularValueMappings ) { if ( !NULL.equals( mappedConstant.getTarget() ) && !targetEnumConstants.contains( mappedConstant.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -321,18 +321,18 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T private static class ValueMappings { - List regularValueMappings = new ArrayList<>(); - ValueMapping defaultTarget = null; + List regularValueMappings = new ArrayList<>(); + ValueMappingOptions defaultTarget = null; String defaultTargetValue = null; - ValueMapping nullTarget = null; + ValueMappingOptions nullTarget = null; String nullValueTarget = null; boolean hasMapAnyUnmapped = false; boolean hasMapAnyRemaining = false; boolean hasDefaultValue = false; - ValueMappings(List valueMappings) { + ValueMappings(List valueMappings) { - for ( ValueMapping valueMapping : valueMappings ) { + for ( ValueMappingOptions valueMapping : valueMappings ) { if ( ANY_REMAINING.equals( valueMapping.getSource() ) ) { defaultTarget = valueMapping; defaultTargetValue = getValue( defaultTarget ); @@ -355,7 +355,7 @@ else if ( NULL.equals( valueMapping.getSource() ) ) { } } - String getValue(ValueMapping valueMapping) { + String getValue(ValueMappingOptions valueMapping) { return NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java index ef6ae343e8..63af65f2e2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java @@ -35,7 +35,7 @@ public static MappingReferences forSourceMethod(SourceMethod sourceMethod, Forma Set references = new LinkedHashSet<>(); List targetThisReferences = new ArrayList<>( ); - for ( MappingOptions mapping : sourceMethod.getMappingOptions().getMappings() ) { + for ( MappingOptions mapping : sourceMethod.getOptions().getMappings() ) { // handle source reference SourceReference sourceReference = new SourceReference.BuilderFromMapping().mapping( mapping ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index a0bae0c919..cf52c96a2d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -78,7 +78,7 @@ public BuilderFromMapping mapping(MappingOptions mapping) { this.sourceAnnotationValue = mapping.getSourceAnnotationValue(); if ( mapping.getInheritContext() != null ) { isForwarded = mapping.getInheritContext().isForwarded(); - templateMethod = mapping.getInheritContext().getInheritedFromMethod(); + templateMethod = mapping.getInheritContext().getTemplateMethod(); } return this; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java index 7937fbef51..6041453cf8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java @@ -18,7 +18,6 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.prism.BuilderPrism; @@ -87,7 +86,7 @@ public Builder messager(FormattingMessager messager) { public Builder mapping(MappingOptions mapping) { if ( mapping.getInheritContext() != null ) { this.isReversed = mapping.getInheritContext().isReversed(); - this.templateMethod = mapping.getInheritContext().getInheritedFromMethod(); + this.templateMethod = mapping.getInheritContext().getTemplateMethod(); } this.isIgnored = mapping.isIgnored(); this.targetName = mapping.getTargetName(); @@ -162,7 +161,7 @@ && matchesSourceOrTargetParameter( segments[0], parameter, isReversed ) ) { private List getTargetEntries(Type type, String[] entryNames) { // initialize - CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy(); + CollectionMappingStrategyPrism cms = method.getOptions().getMapper().getCollectionMappingStrategy(); List targetEntries = new ArrayList<>(); Type nextType = type; @@ -203,7 +202,7 @@ private List getTargetEntries(Type type, String[] entryNames) { ); } else { - BuilderPrism builderPrism = BeanMappingOptions.builderPrismFor( method ); + BuilderPrism builderPrism = method.getOptions().getBeanMapping().getBuilderPrism(); builderType = typeFactory.builderTypeFor( nextType, builderPrism ); propertyEntry = PropertyEntry.forTargetReference( fullName, targetReadAccessor, @@ -271,7 +270,7 @@ private Type typeBasedOnMethod(Type type) { return type; } else { - BuilderPrism builderPrism = BeanMappingOptions.builderPrismFor( method ); + BuilderPrism builderPrism = method.getOptions().getBeanMapping().getBuilderPrism(); return typeFactory.effectiveResultTypeFor( type, builderPrism ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index 7a192c6f1f..44aa02c134 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -5,9 +5,9 @@ */ package org.mapstruct.ap.internal.model.source; +import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.TypeKind; @@ -19,7 +19,6 @@ import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; @@ -28,197 +27,125 @@ * * @author Sjaak Derksen */ -public class BeanMappingOptions { +public class BeanMappingOptions extends DelegatingOptions { private final SelectionParameters selectionParameters; - private final NullValueMappingStrategyPrism nullValueMappingStrategy; - private final NullValueCheckStrategyPrism nullValueCheckStrategy; - private final ReportingPolicyPrism reportingPolicy; - private final boolean ignoreByDefault; - private final List ignoreUnmappedSourceProperties; - private final BuilderPrism builder; - private final NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy; - - private final AnnotationMirror mirror; + private final BeanMappingPrism prism; /** * creates a mapping for inheritance. Will set - * - ignoreByDefault to false. - * - resultType to null * * @return new mapping */ public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) { - return new BeanMappingOptions( + BeanMappingOptions options = new BeanMappingOptions( SelectionParameters.forInheritance( beanMapping.selectionParameters ), - beanMapping.nullValueMappingStrategy, - beanMapping.nullValuePropertyMappingStrategy, - beanMapping.nullValueCheckStrategy, - beanMapping.reportingPolicy, - beanMapping.ignoreByDefault, - beanMapping.ignoreUnmappedSourceProperties, - beanMapping.builder, - beanMapping.mirror + beanMapping.prism, + beanMapping ); + return options; } - public static class Builder { - - private BeanMappingPrism prism; - private ExecutableElement method; - private FormattingMessager messager; - private Types typeUtils; - private TypeFactory typeFactory; - - public Builder beanMappingPrism(BeanMappingPrism beanMappingPrism) { - this.prism = beanMappingPrism; - return this; - } - - public Builder method(ExecutableElement method) { - this.method = method; - return this; - } - - public Builder messager(FormattingMessager messager) { - this.messager = messager; - return this; + public static BeanMappingOptions getInstanceOn(BeanMappingPrism prism, MapperOptions mapperOptions, + ExecutableElement method, FormattingMessager messager, + Types typeUtils, TypeFactory typeFactory + ) { + if ( prism == null || !isConsistent( prism, method, messager ) ) { + BeanMappingOptions options = new BeanMappingOptions( null, null, mapperOptions ); + return options; } - public Builder typeUtils(Types typeUtils) { - this.typeUtils = typeUtils; - return this; - } + Objects.requireNonNull( method ); + Objects.requireNonNull( messager ); + Objects.requireNonNull( method ); + Objects.requireNonNull( typeUtils ); + Objects.requireNonNull( typeFactory ); + + SelectionParameters selectionParameters = new SelectionParameters( + prism.qualifiedBy(), + prism.qualifiedByName(), + TypeKind.VOID != prism.resultType().getKind() ? prism.resultType() : null, + typeUtils + ); - public Builder typeFactory(TypeFactory typeFactory) { - this.typeFactory = typeFactory; - return this; - } + //TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support? + BeanMappingOptions options = new BeanMappingOptions( selectionParameters, prism, mapperOptions ); + return options; + } - public BeanMappingOptions build() { - - if ( prism == null ) { - return null; - } - - Objects.requireNonNull( method ); - Objects.requireNonNull( messager ); - Objects.requireNonNull( method ); - Objects.requireNonNull( typeUtils ); - Objects.requireNonNull( typeFactory ); - - boolean resultTypeIsDefined = !TypeKind.VOID.equals( prism.resultType().getKind() ); - - NullValueMappingStrategyPrism nullValueMappingStrategy = - null == prism.values.nullValueMappingStrategy() - ? null - : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); - - NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy = - null == prism.values.nullValuePropertyMappingStrategy() - ? null - : - NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); - - NullValueCheckStrategyPrism nullValueCheckStrategy = - null == prism.values.nullValueCheckStrategy() - ? null - : NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); - - boolean ignoreByDefault = prism.ignoreByDefault(); - BuilderPrism builderMapping = null; - if ( prism.values.builder() != null ) { - builderMapping = prism.builder(); - } - - if ( !resultTypeIsDefined && prism.qualifiedBy().isEmpty() && - prism.qualifiedByName().isEmpty() - && prism.ignoreUnmappedSourceProperties().isEmpty() - && ( nullValueMappingStrategy == null ) && ( nullValuePropertyMappingStrategy == null ) - && ( nullValueCheckStrategy == null ) && !ignoreByDefault && builderMapping == null ) { - - messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS ); - } - - SelectionParameters cmp = new SelectionParameters( - prism.qualifiedBy(), - prism.qualifiedByName(), - resultTypeIsDefined ? prism.resultType() : null, - typeUtils - ); - - //TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support? - return new BeanMappingOptions( - cmp, - nullValueMappingStrategy, - nullValuePropertyMappingStrategy, - nullValueCheckStrategy, - null, - ignoreByDefault, - prism.ignoreUnmappedSourceProperties(), - builderMapping, - prism.mirror - ); + private static boolean isConsistent(BeanMappingPrism prism, ExecutableElement method, + FormattingMessager messager) { + if ( TypeKind.VOID == prism.resultType().getKind() + && prism.qualifiedBy().isEmpty() + && prism.qualifiedByName().isEmpty() + && prism.ignoreUnmappedSourceProperties().isEmpty() + && null == prism.values.nullValueCheckStrategy() + && null == prism.values.nullValuePropertyMappingStrategy() + && null == prism.values.nullValueMappingStrategy() + && null == prism.values.ignoreByDefault() + && null == prism.values.builder() ) { + + messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS ); + return false; } + return true; } - private BeanMappingOptions(SelectionParameters selectionParameters, NullValueMappingStrategyPrism nvms, - NullValuePropertyMappingStrategyPrism nvpms, NullValueCheckStrategyPrism nvcs, - ReportingPolicyPrism reportingPolicy, boolean ignoreByDefault, - List ignoreUnmappedSourceProperties, BuilderPrism builder, - AnnotationMirror mirror) { + private BeanMappingOptions(SelectionParameters selectionParameters, BeanMappingPrism prism, + DelegatingOptions next) { + super( next ); this.selectionParameters = selectionParameters; - this.nullValueMappingStrategy = nvms; - this.nullValuePropertyMappingStrategy = nvpms; - this.nullValueCheckStrategy = nvcs; - this.reportingPolicy = reportingPolicy; - this.ignoreByDefault = ignoreByDefault; - this.ignoreUnmappedSourceProperties = ignoreUnmappedSourceProperties; - this.builder = builder; - this.mirror = mirror; + this.prism = prism; } - public SelectionParameters getSelectionParameters() { - return selectionParameters; - } + // @Mapping, @BeanMapping - public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - return nullValueMappingStrategy; + @Override + public NullValueCheckStrategyPrism getNullValueCheckStrategy() { + return null == prism || null == prism.values.nullValueCheckStrategy() ? + next().getNullValueCheckStrategy() + : NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); } + @Override public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { - return nullValuePropertyMappingStrategy; + return null == prism || null == prism.values.nullValuePropertyMappingStrategy() ? + next().getNullValuePropertyMappingStrategy() + : NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); } - public NullValueCheckStrategyPrism getNullValueCheckStrategy() { - return nullValueCheckStrategy; + @Override + public NullValueMappingStrategyPrism getNullValueMappingStrategy() { + return null == prism || null == prism.values.nullValueMappingStrategy() ? + next().getNullValueMappingStrategy() + : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + } + + @Override + public BuilderPrism getBuilderPrism() { + return null == prism || null == prism.values.builder() ? next().getBuilderPrism() : prism.builder(); } - public ReportingPolicyPrism getReportingPolicy() { - return reportingPolicy; + // @BeanMapping specific + + public SelectionParameters getSelectionParameters() { + return selectionParameters; } public boolean isignoreByDefault() { - return ignoreByDefault; + return null == prism ? false : prism.ignoreByDefault(); } public List getIgnoreUnmappedSourceProperties() { - return ignoreUnmappedSourceProperties; + return null == prism ? Collections.emptyList() : prism.ignoreUnmappedSourceProperties(); } public AnnotationMirror getMirror() { - return mirror; + return null == prism ? null : prism.mirror; } - /** - * derives the builder prism given the options and configuration - * @param method containing mandatory configuration and the mapping options (optionally containing a beanmapping) - * @return null if BuilderPrism not exist - */ - public static BuilderPrism builderPrismFor(Method method) { - return method.getMapperConfiguration() - .getBuilderPrism( Optional.ofNullable( method.getMappingOptions().getBeanMapping() ) - .map( b -> b.builder ) - .orElse( null ) ); + @Override + public boolean hasAnnotation() { + return prism != null; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java new file mode 100644 index 0000000000..4b7fdcbc50 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -0,0 +1,128 @@ +/* + * 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.internal.model.source; + +import java.util.Collections; +import java.util.Set; +import javax.lang.model.type.DeclaredType; + +import org.mapstruct.ap.internal.option.Options; +import org.mapstruct.ap.internal.prism.BuilderPrism; +import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; +import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; +import org.mapstruct.ap.internal.prism.MapperPrism; +import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; +import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; +import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; +import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; +import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; + +public class DefaultOptions extends DelegatingOptions { + + private final MapperPrism prism; + private final Options options; + + DefaultOptions(MapperPrism prism, Options options) { + super( null ); + this.prism = prism; + this.options = options; + } + + @Override + public String implementationName() { + return prism.implementationName(); + } + + @Override + public String implementationPackage() { + return prism.implementationPackage(); + } + + @Override + public Set uses() { + return Collections.emptySet(); + } + + @Override + public Set imports() { + return Collections.emptySet(); + } + + @Override + public ReportingPolicyPrism unmappedTargetPolicy() { + if ( options.getUnmappedTargetPolicy() != null ) { + return options.getUnmappedTargetPolicy(); + } + return ReportingPolicyPrism.valueOf( prism.unmappedTargetPolicy() ); + } + + @Override + public ReportingPolicyPrism unmappedSourcePolicy() { + return ReportingPolicyPrism.valueOf( prism.unmappedSourcePolicy() ); + } + + @Override + public ReportingPolicyPrism typeConversionPolicy() { + return ReportingPolicyPrism.valueOf( prism.typeConversionPolicy() ); + } + + @Override + public String componentModel() { + if ( options.getDefaultComponentModel() != null ) { + return options.getDefaultComponentModel(); + } + return prism.componentModel(); + } + + @Override + public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() { + return MappingInheritanceStrategyPrism.valueOf( prism.mappingInheritanceStrategy() ); + } + + @Override + public InjectionStrategyPrism getInjectionStrategy() { + if ( options.getDefaultInjectionStrategy() != null ) { + return InjectionStrategyPrism.valueOf( options.getDefaultInjectionStrategy().toUpperCase() ); + } + return InjectionStrategyPrism.valueOf( prism.injectionStrategy() ); + } + + @Override + public Boolean isDisableSubMappingMethodsGeneration() { + return prism.disableSubMappingMethodsGeneration(); + } + + // BeanMapping and Mapping + + public CollectionMappingStrategyPrism getCollectionMappingStrategy() { + return CollectionMappingStrategyPrism.valueOf( prism.collectionMappingStrategy() ); + } + + public NullValueCheckStrategyPrism getNullValueCheckStrategy() { + return NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); + } + + public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { + return NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); + } + + public NullValueMappingStrategyPrism getNullValueMappingStrategy() { + return NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + } + + public BuilderPrism getBuilderPrism() { + // TODO: I realized this is not correct, however it needs to be null in order to keep downward compatibility + // but assuming a default @Builder will make testcases fail. Not having a default means that you need to + // specify this mandatory on @MappingConfig and @Mapper. + return null; + } + + @Override + public boolean hasAnnotation() { + return false; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java new file mode 100644 index 0000000000..1d249e7775 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -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.internal.model.source; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.internal.prism.BuilderPrism; +import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; +import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; +import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; +import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; +import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; +import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; +import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; + +/** + * Chain Of Responsibility Pattern. + */ +public abstract class DelegatingOptions { + + private final DelegatingOptions next; + + public DelegatingOptions(DelegatingOptions next) { + this.next = next; + } + + // @Mapper and @MapperConfig + + public String implementationName() { + return next.implementationName(); + } + + public String implementationPackage() { + return next.implementationPackage(); + } + + public Set uses() { + return next.uses(); + } + + public Set imports() { + return next.imports(); + } + + public ReportingPolicyPrism unmappedTargetPolicy() { + return next.unmappedTargetPolicy(); + } + + public ReportingPolicyPrism unmappedSourcePolicy() { + return next.unmappedSourcePolicy(); + } + + public ReportingPolicyPrism typeConversionPolicy() { + return next.typeConversionPolicy(); + } + + public String componentModel() { + return next.componentModel(); + } + + public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() { + return next.getMappingInheritanceStrategy(); + } + + public InjectionStrategyPrism getInjectionStrategy() { + return next.getInjectionStrategy(); + } + + public Boolean isDisableSubMappingMethodsGeneration() { + return next.isDisableSubMappingMethodsGeneration(); + } + + // BeanMapping and Mapping + + public CollectionMappingStrategyPrism getCollectionMappingStrategy() { + return next.getCollectionMappingStrategy(); + } + + public NullValueCheckStrategyPrism getNullValueCheckStrategy() { + return next.getNullValueCheckStrategy(); + } + + public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { + return next.getNullValuePropertyMappingStrategy(); + } + + public NullValueMappingStrategyPrism getNullValueMappingStrategy() { + return next.getNullValueMappingStrategy(); + } + + public BuilderPrism getBuilderPrism() { + return next.getBuilderPrism(); + } + + DelegatingOptions next() { + return next; + } + + protected Set toDeclaredTypes(List in, Set next) { + Set result = in.stream() + .map( DeclaredType.class::cast ) + .collect( Collectors.toCollection( LinkedHashSet::new ) ); + result.addAll( next ); + return result; + } + + public abstract boolean hasAnnotation(); + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java index 75fcf84e84..077883c311 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java @@ -21,65 +21,60 @@ * * @author Gunnar Morling */ -public class IterableMappingOptions { +public class IterableMappingOptions extends DelegatingOptions { private final SelectionParameters selectionParameters; private final FormattingParameters formattingParameters; - private final AnnotationMirror mirror; - private final NullValueMappingStrategyPrism nullValueMappingStrategy; + private final IterableMappingPrism prism; - public static IterableMappingOptions fromPrism(IterableMappingPrism iterableMapping, ExecutableElement method, + public static IterableMappingOptions fromPrism(IterableMappingPrism prism, + MapperOptions mappperOptions, ExecutableElement method, FormattingMessager messager, Types typeUtils) { - if ( iterableMapping == null ) { - return null; - } - - boolean elementTargetTypeIsDefined = !TypeKind.VOID.equals( iterableMapping.elementTargetType().getKind() ); - - NullValueMappingStrategyPrism nullValueMappingStrategy = - iterableMapping.values.nullValueMappingStrategy() == null - ? null - : NullValueMappingStrategyPrism.valueOf( iterableMapping.nullValueMappingStrategy() ); - - if ( !elementTargetTypeIsDefined - && iterableMapping.dateFormat().isEmpty() - && iterableMapping.numberFormat().isEmpty() - && iterableMapping.qualifiedBy().isEmpty() - && iterableMapping.qualifiedByName().isEmpty() - && ( nullValueMappingStrategy == null ) ) { - messager.printMessage( method, Message.ITERABLEMAPPING_NO_ELEMENTS ); + if ( prism == null || !isConsistent( prism, method, messager ) ) { + IterableMappingOptions options = new IterableMappingOptions( null, null, null, mappperOptions ); + return options; } SelectionParameters selection = new SelectionParameters( - iterableMapping.qualifiedBy(), - iterableMapping.qualifiedByName(), - elementTargetTypeIsDefined ? iterableMapping.elementTargetType() : null, + prism.qualifiedBy(), + prism.qualifiedByName(), + TypeKind.VOID != prism.elementTargetType().getKind() ? prism.elementTargetType() : null, typeUtils ); FormattingParameters formatting = new FormattingParameters( - iterableMapping.dateFormat(), - iterableMapping.numberFormat(), - iterableMapping.mirror, - iterableMapping.values.dateFormat(), + prism.dateFormat(), + prism.numberFormat(), + prism.mirror, + prism.values.dateFormat(), method ); - return new IterableMappingOptions( formatting, - selection, - iterableMapping.mirror, - nullValueMappingStrategy - ); + IterableMappingOptions options = new IterableMappingOptions( formatting, selection, prism, mappperOptions ); + return options; } - private IterableMappingOptions(FormattingParameters formattingParameters, SelectionParameters selectionParameters, - AnnotationMirror mirror, NullValueMappingStrategyPrism nvms) { + private static boolean isConsistent(IterableMappingPrism prism, ExecutableElement method, + FormattingMessager messager) { + if ( prism.dateFormat().isEmpty() + && prism.numberFormat().isEmpty() + && prism.qualifiedBy().isEmpty() + && prism.qualifiedByName().isEmpty() + && TypeKind.VOID == prism.elementTargetType().getKind() + && null == prism.values.nullValueMappingStrategy() ) { + messager.printMessage( method, Message.ITERABLEMAPPING_NO_ELEMENTS ); + return false; + } + return true; + } + private IterableMappingOptions(FormattingParameters formattingParameters, SelectionParameters selectionParameters, + IterableMappingPrism prism, DelegatingOptions next ) { + super( next ); this.formattingParameters = formattingParameters; this.selectionParameters = selectionParameters; - this.mirror = mirror; - this.nullValueMappingStrategy = nvms; + this.prism = prism; } public SelectionParameters getSelectionParameters() { @@ -91,10 +86,19 @@ public FormattingParameters getFormattingParameters() { } public AnnotationMirror getMirror() { - return mirror; + return null == prism ? null : prism.mirror; } + @Override public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - return nullValueMappingStrategy; + return null == prism || null == prism.values.nullValueMappingStrategy() ? + next().getNullValueMappingStrategy() + : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); } + + @Override + public boolean hasAnnotation() { + return prism != null; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java index 62c827b5a9..83d8bec5ce 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java @@ -21,93 +21,91 @@ * * @author Gunnar Morling */ -public class MapMappingOptions { +public class MapMappingOptions extends DelegatingOptions { private final SelectionParameters keySelectionParameters; private final SelectionParameters valueSelectionParameters; private final FormattingParameters keyFormattingParameters; private final FormattingParameters valueFormattingParameters; - private final AnnotationMirror mirror; - private final NullValueMappingStrategyPrism nullValueMappingStrategy; + private final MapMappingPrism prism; - public static MapMappingOptions fromPrism(MapMappingPrism mapMapping, ExecutableElement method, + public static MapMappingOptions fromPrism(MapMappingPrism prism, MapperOptions mapperOptions, + ExecutableElement method, FormattingMessager messager, Types typeUtils) { - if ( mapMapping == null ) { - return null; - } - - NullValueMappingStrategyPrism nullValueMappingStrategy = - mapMapping.values.nullValueMappingStrategy() == null - ? null - : NullValueMappingStrategyPrism.valueOf( mapMapping.nullValueMappingStrategy() ); - - - boolean keyTargetTypeIsDefined = !TypeKind.VOID.equals( mapMapping.keyTargetType().getKind() ); - boolean valueTargetTypeIsDefined = !TypeKind.VOID.equals( mapMapping.valueTargetType().getKind() ); - if ( mapMapping.keyDateFormat().isEmpty() - && mapMapping.keyNumberFormat().isEmpty() - && mapMapping.keyQualifiedBy().isEmpty() - && mapMapping.keyQualifiedByName().isEmpty() - && mapMapping.valueDateFormat().isEmpty() - && mapMapping.valueNumberFormat().isEmpty() - && mapMapping.valueQualifiedBy().isEmpty() - && mapMapping.valueQualifiedByName().isEmpty() - && !keyTargetTypeIsDefined - && !valueTargetTypeIsDefined - && ( nullValueMappingStrategy == null ) ) { - messager.printMessage( method, Message.MAPMAPPING_NO_ELEMENTS ); + if ( prism == null || !isConsistent( prism, method, messager ) ) { + MapMappingOptions options = new MapMappingOptions( null, null, null, null, null, mapperOptions ); + return options; } SelectionParameters keySelection = new SelectionParameters( - mapMapping.keyQualifiedBy(), - mapMapping.keyQualifiedByName(), - keyTargetTypeIsDefined ? mapMapping.keyTargetType() : null, + prism.keyQualifiedBy(), + prism.keyQualifiedByName(), + TypeKind.VOID != prism.keyTargetType().getKind() ? prism.keyTargetType() : null, typeUtils ); SelectionParameters valueSelection = new SelectionParameters( - mapMapping.valueQualifiedBy(), - mapMapping.valueQualifiedByName(), - valueTargetTypeIsDefined ? mapMapping.valueTargetType() : null, + prism.valueQualifiedBy(), + prism.valueQualifiedByName(), + TypeKind.VOID != prism.valueTargetType().getKind() ? prism.valueTargetType() : null, typeUtils ); FormattingParameters keyFormatting = new FormattingParameters( - mapMapping.keyDateFormat(), - mapMapping.keyNumberFormat(), - mapMapping.mirror, - mapMapping.values.keyDateFormat(), + prism.keyDateFormat(), + prism.keyNumberFormat(), + prism.mirror, + prism.values.keyDateFormat(), method ); FormattingParameters valueFormatting = new FormattingParameters( - mapMapping.valueDateFormat(), - mapMapping.valueNumberFormat(), - mapMapping.mirror, - mapMapping.values.valueDateFormat(), + prism.valueDateFormat(), + prism.valueNumberFormat(), + prism.mirror, + prism.values.valueDateFormat(), method ); - return new MapMappingOptions( + MapMappingOptions options = new MapMappingOptions( keyFormatting, keySelection, valueFormatting, valueSelection, - mapMapping.mirror, - nullValueMappingStrategy + prism, + mapperOptions ); + return options; + } + + private static boolean isConsistent(MapMappingPrism prism, ExecutableElement method, FormattingMessager messager) { + if ( prism.keyDateFormat().isEmpty() + && prism.keyNumberFormat().isEmpty() + && prism.keyQualifiedBy().isEmpty() + && prism.keyQualifiedByName().isEmpty() + && prism.valueDateFormat().isEmpty() + && prism.valueNumberFormat().isEmpty() + && prism.valueQualifiedBy().isEmpty() + && prism.valueQualifiedByName().isEmpty() + && TypeKind.VOID == prism.keyTargetType().getKind() + && TypeKind.VOID == prism.valueTargetType().getKind() + && null == prism.values.nullValueMappingStrategy() ) { + messager.printMessage( method, Message.MAPMAPPING_NO_ELEMENTS ); + return false; + } + return true; } private MapMappingOptions(FormattingParameters keyFormatting, SelectionParameters keySelectionParameters, - FormattingParameters valueFormatting, SelectionParameters valueSelectionParameters, AnnotationMirror mirror, - NullValueMappingStrategyPrism nvms ) { + FormattingParameters valueFormatting, SelectionParameters valueSelectionParameters, + MapMappingPrism prism, DelegatingOptions next ) { + super( next ); this.keyFormattingParameters = keyFormatting; this.keySelectionParameters = keySelectionParameters; this.valueFormattingParameters = valueFormatting; this.valueSelectionParameters = valueSelectionParameters; - this.mirror = mirror; - this.nullValueMappingStrategy = nvms; + this.prism = prism; } public FormattingParameters getKeyFormattingParameters() { @@ -127,11 +125,19 @@ public SelectionParameters getValueSelectionParameters() { } public AnnotationMirror getMirror() { - return mirror; + return null == prism ? null : prism.mirror; } + @Override public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - return nullValueMappingStrategy; + return null == prism || null == prism.values.nullValueMappingStrategy() ? + next().getNullValueMappingStrategy() + : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + } + + @Override + public boolean hasAnnotation() { + return prism != null; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java new file mode 100644 index 0000000000..b83bbd2f90 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -0,0 +1,131 @@ +/* + * 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.internal.model.source; + +import java.util.Set; +import javax.lang.model.type.DeclaredType; + +import org.mapstruct.ap.internal.prism.BuilderPrism; +import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; +import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; +import org.mapstruct.ap.internal.prism.MapperConfigPrism; +import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; +import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; +import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; +import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; +import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; + +public class MapperConfigOptions extends DelegatingOptions { + + private final MapperConfigPrism prism; + + MapperConfigOptions(MapperConfigPrism prism, DelegatingOptions next ) { + super( next ); + this.prism = prism; + } + + @Override + public String implementationName() { + return null == prism.values.implementationName() ? next().implementationName() : + prism.implementationName(); + } + + @Override + public String implementationPackage() { + return null == prism.values.implementationPackage() ? next().implementationPackage() : + prism.implementationPackage(); + } + + @Override + public Set uses() { + return toDeclaredTypes( prism.uses(), next().uses() ); + } + + @Override + public Set imports() { + return toDeclaredTypes( prism.imports(), next().imports() ); + } + + @Override + public ReportingPolicyPrism unmappedTargetPolicy() { + return null == prism.values.unmappedTargetPolicy() ? next().unmappedTargetPolicy() : + ReportingPolicyPrism.valueOf( prism.unmappedTargetPolicy() ); + } + + @Override + public ReportingPolicyPrism unmappedSourcePolicy() { + return null == prism.values.unmappedSourcePolicy() ? next().unmappedSourcePolicy() : + ReportingPolicyPrism.valueOf( prism.unmappedSourcePolicy() ); + } + + @Override + public ReportingPolicyPrism typeConversionPolicy() { + return null == prism.values.typeConversionPolicy() ? next().typeConversionPolicy() : + ReportingPolicyPrism.valueOf( prism.typeConversionPolicy() ); + } + + @Override + public String componentModel() { + return null == prism.values.componentModel() ? next().componentModel() : prism.componentModel(); + } + + @Override + public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() { + return null == prism.values.mappingInheritanceStrategy() ? next().getMappingInheritanceStrategy() : + MappingInheritanceStrategyPrism.valueOf( prism.mappingInheritanceStrategy() ); + } + + @Override + public InjectionStrategyPrism getInjectionStrategy() { + return null == prism.values.injectionStrategy() ? next().getInjectionStrategy() : + InjectionStrategyPrism.valueOf( prism.injectionStrategy() ); + } + + @Override + public Boolean isDisableSubMappingMethodsGeneration() { + return null == prism.values.disableSubMappingMethodsGeneration() ? + next().isDisableSubMappingMethodsGeneration() : prism.disableSubMappingMethodsGeneration(); + } + + @Override + public CollectionMappingStrategyPrism getCollectionMappingStrategy() { + return null == prism.values.collectionMappingStrategy() ? + next().getCollectionMappingStrategy() + : CollectionMappingStrategyPrism.valueOf( prism.collectionMappingStrategy() ); + } + + @Override + public NullValueCheckStrategyPrism getNullValueCheckStrategy() { + return null == prism.values.nullValueCheckStrategy() ? + next().getNullValueCheckStrategy() + : NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); + } + + @Override + public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { + return null == prism.values.nullValuePropertyMappingStrategy() ? + next().getNullValuePropertyMappingStrategy() + : NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); + } + + @Override + public NullValueMappingStrategyPrism getNullValueMappingStrategy() { + return null == prism.values.nullValueMappingStrategy() ? + next().getNullValueMappingStrategy() + : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + } + + @Override + public BuilderPrism getBuilderPrism() { + return null == prism.values.builder() ? next().getBuilderPrism() : prism.builder(); + } + + @Override + public boolean hasAnnotation() { + return prism != null; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index 1039e412f5..b1397154d1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.internal.model.source; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeKind; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.prism.BuilderPrism; @@ -26,282 +24,158 @@ import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; -/** - * Provides an aggregated view to the settings given via {@link org.mapstruct.Mapper} and - * {@link org.mapstruct.MapperConfig} for a specific mapper class. - *

- * Settings given via {@code Mapper} will generally take precedence over settings inherited from a referenced config - * class. The lists of referenced mappers given via {@link org.mapstruct.Mapper#uses()} and - * {@link org.mapstruct.MapperConfig#uses() } will be merged. - * - * @author Sjaak Derksen - */ -public class MapperOptions { - - private final MapperPrism mapperPrism; - private final MapperConfigPrism mapperConfigPrism; - private final DeclaredType config; +public class MapperOptions extends DelegatingOptions { - public static MapperOptions getInstanceOn(Element e) { - return new MapperOptions( MapperPrism.getInstanceOn( e ) ); - } - - private MapperOptions(MapperPrism mapperPrism) { - this.mapperPrism = mapperPrism; + private final MapperPrism prism; + private final DeclaredType mapperConfigType; - if ( mapperPrism.values.config() != null ) { - // TODO #737 Only a declared type makes sense here; Validate and raise graceful error; - // Also validate that @MapperConfig is present - this.config = (DeclaredType) mapperPrism.config(); - this.mapperConfigPrism = MapperConfigPrism.getInstanceOn( config.asElement() ); + public static MapperOptions getInstanceOn(TypeElement typeElement, Options options) { + MapperPrism prism = MapperPrism.getInstanceOn( typeElement ); + MapperOptions mapperAnnotation; + DelegatingOptions defaults = new DefaultOptions( prism, options ); + DeclaredType mapperConfigType; + if ( prism.values.config() != null && prism.config().getKind() == TypeKind.DECLARED ) { + mapperConfigType = (DeclaredType) prism.config(); } else { - this.config = null; - this.mapperConfigPrism = null; + mapperConfigType = null; } - } - - public String implementationName() { - if ( mapperConfigPrism != null && mapperPrism.values.implementationName() == null ) { - return mapperConfigPrism.implementationName(); + if ( mapperConfigType != null ) { + Element mapperConfigElement = mapperConfigType.asElement(); + MapperConfigPrism configPrism = MapperConfigPrism.getInstanceOn( mapperConfigElement ); + MapperConfigOptions mapperConfigAnnotation = new MapperConfigOptions( configPrism, defaults ); + mapperAnnotation = new MapperOptions( prism, mapperConfigType, mapperConfigAnnotation ); } else { - return mapperPrism.implementationName(); + mapperAnnotation = new MapperOptions( prism, null, defaults ); } + return mapperAnnotation; } - public String implementationPackage() { - if ( mapperConfigPrism != null && mapperPrism.values.implementationPackage() == null ) { - return mapperConfigPrism.implementationPackage(); - } - else { - return mapperPrism.implementationPackage(); - } + private MapperOptions(MapperPrism prism, DeclaredType mapperConfigType, DelegatingOptions next) { + super( next ); + this.prism = prism; + this.mapperConfigType = mapperConfigType; } - public Set uses() { - Set uses = new LinkedHashSet<>(); - - for ( TypeMirror usedMapperType : mapperPrism.uses() ) { - // TODO #737 Only declared type make sense here; Validate and raise graceful error; - uses.add( (DeclaredType) usedMapperType ); - } - - if ( mapperConfigPrism != null ) { - for ( TypeMirror usedMapperType : mapperConfigPrism.uses() ) { - // TODO #737 Only declared type make sense here; Validate and raise graceful error; - uses.add( (DeclaredType) usedMapperType ); - } - } - - return uses; + @Override + public String implementationName() { + return null == prism.values.implementationName() ? next().implementationName() : prism.implementationName(); } - public List imports() { - List imports = new ArrayList<>( mapperPrism.imports() ); - if ( mapperConfigPrism != null ) { - imports.addAll( mapperConfigPrism.imports() ); - } - return imports; + @Override + public String implementationPackage() { + return null == prism.values.implementationPackage() ? next().implementationPackage() : + prism.implementationPackage(); } - public ReportingPolicyPrism unmappedTargetPolicy(Options options) { - if ( mapperPrism.values.unmappedTargetPolicy() != null ) { - return ReportingPolicyPrism.valueOf( mapperPrism.unmappedTargetPolicy() ); - } - - if ( mapperConfigPrism != null && mapperConfigPrism.values.unmappedTargetPolicy() != null ) { - return ReportingPolicyPrism.valueOf( mapperConfigPrism.unmappedTargetPolicy() ); - } + @Override + public Set uses() { + return toDeclaredTypes( prism.uses(), next().uses() ); + } - if ( options.getUnmappedTargetPolicy() != null ) { - return options.getUnmappedTargetPolicy(); - } + @Override + public Set imports() { + return toDeclaredTypes( prism.imports(), next().imports() ); + } - // fall back to default defined in the annotation - return ReportingPolicyPrism.valueOf( mapperPrism.unmappedTargetPolicy() ); + @Override + public ReportingPolicyPrism unmappedTargetPolicy() { + return null == prism.values.unmappedTargetPolicy() ? next().unmappedTargetPolicy() : + ReportingPolicyPrism.valueOf( prism.unmappedTargetPolicy() ); } + @Override public ReportingPolicyPrism unmappedSourcePolicy() { - if ( mapperPrism.values.unmappedSourcePolicy() != null ) { - return ReportingPolicyPrism.valueOf( mapperPrism.unmappedSourcePolicy() ); - } - - if ( mapperConfigPrism != null && mapperConfigPrism.values.unmappedSourcePolicy() != null ) { - return ReportingPolicyPrism.valueOf( mapperConfigPrism.unmappedSourcePolicy() ); - } - - // fall back to default defined in the annotation - return ReportingPolicyPrism.valueOf( mapperPrism.unmappedSourcePolicy() ); + return null == prism.values.unmappedSourcePolicy() ? next().unmappedSourcePolicy() : + ReportingPolicyPrism.valueOf( prism.unmappedSourcePolicy() ); } + @Override public ReportingPolicyPrism typeConversionPolicy() { - if ( mapperPrism.values.typeConversionPolicy() != null ) { - return ReportingPolicyPrism.valueOf( mapperPrism.typeConversionPolicy() ); - } - - if ( mapperConfigPrism != null && mapperConfigPrism.values.typeConversionPolicy() != null ) { - return ReportingPolicyPrism.valueOf( mapperConfigPrism.typeConversionPolicy() ); - } - - // fall back to default defined in the annotation - return ReportingPolicyPrism.valueOf( mapperPrism.typeConversionPolicy() ); + return null == prism.values.typeConversionPolicy() ? next().typeConversionPolicy() : + ReportingPolicyPrism.valueOf( prism.typeConversionPolicy() ); } - public CollectionMappingStrategyPrism getCollectionMappingStrategy() { - if ( mapperConfigPrism != null && mapperPrism.values.collectionMappingStrategy() == null ) { - return CollectionMappingStrategyPrism.valueOf( mapperConfigPrism.collectionMappingStrategy() ); - } - else { - return CollectionMappingStrategyPrism.valueOf( mapperPrism.collectionMappingStrategy() ); - } + @Override + public String componentModel() { + return null == prism.values.componentModel() ? next().componentModel() : prism.componentModel(); } + @Override public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() { - if ( mapperConfigPrism != null && mapperPrism.values.mappingInheritanceStrategy() == null ) { - return MappingInheritanceStrategyPrism.valueOf( mapperConfigPrism.mappingInheritanceStrategy() ); - } - else { - return MappingInheritanceStrategyPrism.valueOf( mapperPrism.mappingInheritanceStrategy() ); - } + return null == prism.values.mappingInheritanceStrategy() ? next().getMappingInheritanceStrategy() : + MappingInheritanceStrategyPrism.valueOf( prism.mappingInheritanceStrategy() ); } - public NullValueCheckStrategyPrism getNullValueCheckStrategy(NullValueCheckStrategyPrism beanPrism, - NullValueCheckStrategyPrism mappingPrism) { - if ( mappingPrism != null ) { - return mappingPrism; - } - else if ( beanPrism != null ) { - return beanPrism; - } - else if ( mapperConfigPrism != null && mapperPrism.values.nullValueCheckStrategy() == null ) { - return NullValueCheckStrategyPrism.valueOf( mapperConfigPrism.nullValueCheckStrategy() ); - } - else { - return NullValueCheckStrategyPrism.valueOf( mapperPrism.nullValueCheckStrategy() ); - } + @Override + public InjectionStrategyPrism getInjectionStrategy() { + return null == prism.values.injectionStrategy() ? next().getInjectionStrategy() : + InjectionStrategyPrism.valueOf( prism.injectionStrategy() ); } - public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy( - NullValuePropertyMappingStrategyPrism beanPrism, - NullValuePropertyMappingStrategyPrism mappingPrism) { - if ( mappingPrism != null ) { - return mappingPrism; - } - else if ( beanPrism != null ) { - return beanPrism; - } - else if ( mapperConfigPrism != null && mapperPrism.values.nullValuePropertyMappingStrategy() == null ) { - return NullValuePropertyMappingStrategyPrism.valueOf( - mapperConfigPrism.nullValuePropertyMappingStrategy() - ); - } - else { - return NullValuePropertyMappingStrategyPrism.valueOf( mapperPrism.nullValuePropertyMappingStrategy() ); - } + @Override + public Boolean isDisableSubMappingMethodsGeneration() { + return null == prism.values.disableSubMappingMethodsGeneration() ? + next().isDisableSubMappingMethodsGeneration() : prism.disableSubMappingMethodsGeneration(); } - public InjectionStrategyPrism getInjectionStrategy(Options options) { - if ( mapperPrism.values.injectionStrategy() != null ) { - return InjectionStrategyPrism.valueOf( mapperPrism.injectionStrategy() ); - } - - if ( mapperConfigPrism != null && mapperConfigPrism.values.injectionStrategy() != null ) { - return InjectionStrategyPrism.valueOf( mapperConfigPrism.injectionStrategy() ); - } - - if ( options.getDefaultInjectionStrategy() != null ) { - return InjectionStrategyPrism.valueOf( options.getDefaultInjectionStrategy().toUpperCase() ); - } + // @Mapping, @BeanMapping - // fall back to default defined in the annotation - return InjectionStrategyPrism.valueOf( mapperPrism.injectionStrategy() ); + @Override + public CollectionMappingStrategyPrism getCollectionMappingStrategy() { + return null == prism.values.collectionMappingStrategy() ? + next().getCollectionMappingStrategy() + : CollectionMappingStrategyPrism.valueOf( prism.collectionMappingStrategy() ); } - public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - if ( mapperConfigPrism != null && mapperPrism.values.nullValueMappingStrategy() == null ) { - return NullValueMappingStrategyPrism.valueOf( mapperConfigPrism.nullValueMappingStrategy() ); - } - else { - return NullValueMappingStrategyPrism.valueOf( mapperPrism.nullValueMappingStrategy() ); - } + @Override + public NullValueCheckStrategyPrism getNullValueCheckStrategy() { + return null == prism.values.nullValueCheckStrategy() ? + next().getNullValueCheckStrategy() + : NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); } - public boolean isMapToDefault(NullValueMappingStrategyPrism mapNullToDefault) { - - // check on method level - if ( mapNullToDefault != null ) { - return mapNullToDefault == NullValueMappingStrategyPrism.RETURN_DEFAULT; - } - - return isMapToDefaultOnMapperAndMappingConfigLevel(); - + @Override + public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { + return null == prism.values.nullValuePropertyMappingStrategy() ? + next().getNullValuePropertyMappingStrategy() + : NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); } - private boolean isMapToDefaultOnMapperAndMappingConfigLevel() { - final NullValueMappingStrategyPrism strategy; - if ( mapperConfigPrism != null && mapperPrism.values.nullValueMappingStrategy() == null ) { - strategy = NullValueMappingStrategyPrism.valueOf( mapperConfigPrism.nullValueMappingStrategy() ); - } - else { - strategy = NullValueMappingStrategyPrism.valueOf( mapperPrism.nullValueMappingStrategy() ); - } - - return NullValueMappingStrategyPrism.RETURN_DEFAULT == strategy; + @Override + public NullValueMappingStrategyPrism getNullValueMappingStrategy() { + return null == prism.values.nullValueMappingStrategy() ? + next().getNullValueMappingStrategy() + : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); } - public String componentModel(Options options) { - if ( mapperPrism.values.componentModel() != null ) { - return mapperPrism.componentModel(); - } - - if ( mapperConfigPrism != null && mapperConfigPrism.values.componentModel() != null ) { - return mapperConfigPrism.componentModel(); - } - - if ( options.getDefaultComponentModel() != null ) { - return options.getDefaultComponentModel(); - } - - return mapperPrism.componentModel(); // fall back to default defined in the annotation + @Override + public BuilderPrism getBuilderPrism() { + return null == prism.values.builder() ? next().getBuilderPrism() : prism.builder(); } - public boolean isDisableSubMappingMethodsGeneration() { - if ( mapperPrism.disableSubMappingMethodsGeneration() ) { - return true; - } - - if ( mapperConfigPrism != null && mapperConfigPrism.disableSubMappingMethodsGeneration() ) { - return mapperConfigPrism.disableSubMappingMethodsGeneration(); - } + // @Mapper specific - return mapperPrism.disableSubMappingMethodsGeneration(); // fall back to default defined in the annotation + public DeclaredType mapperConfigType() { + return mapperConfigType; } - public BuilderPrism getBuilderPrism(BuilderPrism beanMappingBuilderPrism) { - if ( beanMappingBuilderPrism != null ) { - return beanMappingBuilderPrism; - } - else if ( mapperPrism.values.builder() != null ) { - return mapperPrism.builder(); - } - else if ( mapperConfigPrism != null && mapperConfigPrism.values.builder() != null ) { - return mapperConfigPrism.builder(); - } - else { - return null; - } - } - - public DeclaredType config() { - return config; + public boolean hasMapperConfig() { + return mapperConfigType != null; } public boolean isValid() { - return mapperPrism.isValid; + return prism.isValid; } public AnnotationMirror getAnnotationMirror() { - return mapperPrism.mirror; + return prism.mirror; } + + @Override + public boolean hasAnnotation() { + return true; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 75e44c2ed5..338f22db90 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -25,22 +25,28 @@ * @author Andreas Gudian */ public class MappingMethodOptions { - private static final MappingMethodOptions EMPTY = new MappingMethodOptions( Collections.emptySet(), + private static final MappingMethodOptions EMPTY = new MappingMethodOptions( + null, + Collections.emptySet(), null, null, null, Collections.emptyList() ); + private MapperOptions mapper; private Set mappings; private IterableMappingOptions iterableMapping; private MapMappingOptions mapMapping; private BeanMappingOptions beanMapping; - private List valueMappings; + private List valueMappings; private boolean fullyInitialized; - public MappingMethodOptions(Set mappings, IterableMappingOptions iterableMapping, MapMappingOptions mapMapping, - BeanMappingOptions beanMapping, List valueMappings ) { + public MappingMethodOptions(MapperOptions mapper, Set mappings, + IterableMappingOptions iterableMapping, + MapMappingOptions mapMapping, BeanMappingOptions beanMapping, + List valueMappings) { + this.mapper = mapper; this.mappings = mappings; this.iterableMapping = iterableMapping; this.mapMapping = mapMapping; @@ -58,8 +64,8 @@ public static MappingMethodOptions empty() { } /** - * @return the {@link MappingOptions}s configured for this method, keyed by target property name. Only for enum mapping - * methods a target will be mapped by several sources. TODO. Remove the value list when 2.0 + * @return the {@link MappingOptions}s configured for this method, keyed by target property name. Only for enum + * mapping methods a target will be mapped by several sources. */ public Set getMappings() { return mappings; @@ -77,7 +83,7 @@ public BeanMappingOptions getBeanMapping() { return beanMapping; } - public List getValueMappings() { + public List getValueMappings() { return valueMappings; } @@ -93,10 +99,14 @@ public void setBeanMapping(BeanMappingOptions beanMapping) { this.beanMapping = beanMapping; } - public void setValueMappings(List valueMappings) { + public void setValueMappings(List valueMappings) { this.valueMappings = valueMappings; } + public MapperOptions getMapper() { + return mapper; + } + /** * @return the {@code true}, iff the options have been fully initialized by applying all available inheritance * options @@ -117,34 +127,35 @@ public void markAsFullyInitialized() { * @param method the source method */ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse, SourceMethod method ) { - MappingMethodOptions inherited = templateMethod.getMappingOptions(); - if ( null != inherited ) { - if ( getIterableMapping() == null && inherited.getIterableMapping() != null) { - setIterableMapping( inherited.getIterableMapping() ); + MappingMethodOptions templateOptions = templateMethod.getOptions(); + if ( null != templateOptions ) { + if ( !getIterableMapping().hasAnnotation() && templateOptions.getIterableMapping().hasAnnotation() ) { + setIterableMapping( templateOptions.getIterableMapping() ); } - if ( getMapMapping() == null && inherited.getMapMapping() != null) { - setMapMapping( inherited.getMapMapping() ); + if ( !getMapMapping().hasAnnotation() && templateOptions.getMapMapping().hasAnnotation() ) { + setMapMapping( templateOptions.getMapMapping() ); } - if ( getBeanMapping() == null && inherited.getBeanMapping() != null ) { - setBeanMapping( BeanMappingOptions.forInheritance( inherited.getBeanMapping() ) ); + if ( !getBeanMapping().hasAnnotation() && templateOptions.getBeanMapping().hasAnnotation() ) { + setBeanMapping( BeanMappingOptions.forInheritance( templateOptions.getBeanMapping( ) ) ); } if ( getValueMappings() == null ) { - if ( inherited.getValueMappings() != null ) { + if ( templateOptions.getValueMappings() != null ) { // there were no mappings, so the inherited mappings are the new ones - setValueMappings( inherited.getValueMappings() ); + setValueMappings( templateOptions.getValueMappings() ); } else { setValueMappings( Collections.emptyList() ); } } else { - if ( inherited.getValueMappings() != null ) { + if ( templateOptions.getValueMappings() != null ) { // iff there are also inherited mappings, we inverse and add them. - for ( ValueMapping inheritedValueMapping : inherited.getValueMappings() ) { - ValueMapping valueMapping = isInverse ? inheritedValueMapping.inverse() : inheritedValueMapping; + for ( ValueMappingOptions inheritedValueMapping : templateOptions.getValueMappings() ) { + ValueMappingOptions valueMapping = + isInverse ? inheritedValueMapping.inverse() : inheritedValueMapping; if ( valueMapping != null && !getValueMappings().contains( valueMapping ) ) { getValueMappings().add( valueMapping ); @@ -154,14 +165,14 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } Set newMappings = new LinkedHashSet<>(); - for ( MappingOptions mapping : inherited.getMappings() ) { + for ( MappingOptions mapping : templateOptions.getMappings() ) { if ( isInverse ) { if ( mapping.canInverse() ) { - newMappings.add( mapping.copyForInverseInheritance( templateMethod ) ); + newMappings.add( mapping.copyForInverseInheritance( templateMethod, getBeanMapping() ) ); } } else { - newMappings.add( mapping.copyForForwardInheritance( templateMethod ) ); + newMappings.add( mapping.copyForForwardInheritance( templateMethod, getBeanMapping() ) ); } } @@ -174,12 +185,12 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } public void applyIgnoreAll(SourceMethod method, TypeFactory typeFactory ) { - CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy(); + CollectionMappingStrategyPrism cms = method.getOptions().getMapper().getCollectionMappingStrategy(); Type writeType = method.getResultType(); if ( !method.isUpdateMethod() ) { writeType = typeFactory.effectiveResultTypeFor( - writeType, - BeanMappingOptions.builderPrismFor( method ) + writeType, + method.getOptions().getBeanMapping().getBuilderPrism() ); } Map writeAccessors = writeType.getPropertyWriteAccessors( cms ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 74a9825721..469398c829 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -31,7 +31,7 @@ * * @author Gunnar Morling */ -public class MappingOptions { +public class MappingOptions extends DelegatingOptions { private static final Pattern JAVA_EXPRESSION = Pattern.compile( "^java\\((.*)\\)$" ); @@ -47,12 +47,9 @@ public class MappingOptions { private final boolean isIgnored; private final Set dependsOn; - private final AnnotationMirror mirror; private final AnnotationValue sourceAnnotationValue; private final AnnotationValue targetAnnotationValue; - private final AnnotationValue dependsOnAnnotationValue; - private final NullValueCheckStrategyPrism nullValueCheckStrategy; - private final NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy; + private final MappingPrism prism; private final InheritContext inheritContext; @@ -60,12 +57,12 @@ public static class InheritContext { private final boolean isReversed; private final boolean isForwarded; - private final Method inheritedFromMethod; + private final Method templateMethod; - public InheritContext(boolean isReversed, boolean isForwarded, Method inheritedFromMethod) { + public InheritContext(boolean isReversed, boolean isForwarded, Method templateMethod) { this.isReversed = isReversed; this.isForwarded = isForwarded; - this.inheritedFromMethod = inheritedFromMethod; + this.templateMethod = templateMethod; } public boolean isReversed() { @@ -76,115 +73,100 @@ public boolean isForwarded() { return isForwarded; } - public Method getInheritedFromMethod() { - return inheritedFromMethod; + public Method getTemplateMethod() { + return templateMethod; } } - public static Set getMappingTargetNamesBy(Predicate predicate, Set mappings) { + public static Set getMappingTargetNamesBy(Predicate predicate, + Set mappings) { return mappings.stream() - .filter( predicate ) - .map( MappingOptions::getTargetName ) - .collect( Collectors.toCollection( LinkedHashSet::new ) ); + .filter( predicate ) + .map( MappingOptions::getTargetName ) + .collect( Collectors.toCollection( LinkedHashSet::new ) ); } - public static MappingOptions getMappingByTargetName(String targetName, Set mappings) { - return mappings.stream().filter( mapping -> mapping.targetName.equals( targetName ) ).findAny().orElse( null ); - } - - public static void addFromMappingsPrism(MappingsPrism mappingsAnnotation, ExecutableElement method, - FormattingMessager messager, Types typeUtils, Set mappings) { + public static void addInstances(MappingsPrism prism, ExecutableElement method, + BeanMappingOptions beanMappingOptions, + FormattingMessager messager, Types typeUtils, + Set mappings) { - for ( MappingPrism mappingPrism : mappingsAnnotation.value() ) { - addFromMappingPrism( mappingPrism, method, messager, typeUtils, mappings ); + for ( MappingPrism mappingPrism : prism.value() ) { + addInstance( mappingPrism, method, beanMappingOptions, messager, typeUtils, mappings ); } } - public static void addFromMappingPrism(MappingPrism mappingPrism, ExecutableElement method, - FormattingMessager messager, Types typeUtils, Set mappings) { + public static void addInstance(MappingPrism prism, ExecutableElement method, BeanMappingOptions beanMappingOptions, + FormattingMessager messager, Types typeUtils, Set mappings) { - if (!isConsistent( mappingPrism, method, messager ) ) { + if ( !isConsistent( prism, method, messager ) ) { return; } - String source = mappingPrism.source().isEmpty() ? null : mappingPrism.source(); - String constant = mappingPrism.values.constant() == null ? null : mappingPrism.constant(); - String expression = getExpression( mappingPrism, method, messager ); - String defaultExpression = getDefaultExpression( mappingPrism, method, messager ); - String dateFormat = mappingPrism.values.dateFormat() == null ? null : mappingPrism.dateFormat(); - String numberFormat = mappingPrism.values.numberFormat() == null ? null : mappingPrism.numberFormat(); - String defaultValue = mappingPrism.values.defaultValue() == null ? null : mappingPrism.defaultValue(); + String source = prism.source().isEmpty() ? null : prism.source(); + String constant = prism.values.constant() == null ? null : prism.constant(); + String expression = getExpression( prism, method, messager ); + String defaultExpression = getDefaultExpression( prism, method, messager ); + String dateFormat = prism.values.dateFormat() == null ? null : prism.dateFormat(); + String numberFormat = prism.values.numberFormat() == null ? null : prism.numberFormat(); + String defaultValue = prism.values.defaultValue() == null ? null : prism.defaultValue(); - boolean resultTypeIsDefined = mappingPrism.values.resultType() != null; - Set dependsOn = mappingPrism.dependsOn() != null ? - new LinkedHashSet( mappingPrism.dependsOn() ) : + boolean resultTypeIsDefined = prism.values.resultType() != null; + Set dependsOn = prism.dependsOn() != null ? + new LinkedHashSet( prism.dependsOn() ) : Collections.emptySet(); FormattingParameters formattingParam = new FormattingParameters( dateFormat, numberFormat, - mappingPrism.mirror, - mappingPrism.values.dateFormat(), + prism.mirror, + prism.values.dateFormat(), method ); SelectionParameters selectionParams = new SelectionParameters( - mappingPrism.qualifiedBy(), - mappingPrism.qualifiedByName(), - resultTypeIsDefined ? mappingPrism.resultType() : null, + prism.qualifiedBy(), + prism.qualifiedByName(), + resultTypeIsDefined ? prism.resultType() : null, typeUtils ); - NullValueCheckStrategyPrism nullValueCheckStrategy = - null == mappingPrism.values.nullValueCheckStrategy() - ? null - : NullValueCheckStrategyPrism.valueOf( mappingPrism.nullValueCheckStrategy() ); - - NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy = - null == mappingPrism.values.nullValuePropertyMappingStrategy() - ? null - : NullValuePropertyMappingStrategyPrism.valueOf( mappingPrism.nullValuePropertyMappingStrategy() ); - - MappingOptions mapping = new MappingOptions( + MappingOptions options = new MappingOptions( + prism.target(), + prism.values.target(), source, + prism.values.source(), constant, expression, defaultExpression, - mappingPrism.target(), defaultValue, - mappingPrism.ignore(), - mappingPrism.mirror, - mappingPrism.values.source(), - mappingPrism.values.target(), + prism.ignore(), formattingParam, selectionParams, - mappingPrism.values.dependsOn(), dependsOn, - nullValueCheckStrategy, - nullValuePropertyMappingStrategy, - null + prism, + null, + beanMappingOptions ); - if ( mappings.contains( mapping ) ) { - messager.printMessage( method, Message.PROPERTYMAPPING_DUPLICATE_TARGETS, mappingPrism.target() ); + if ( mappings.contains( options ) ) { + messager.printMessage( method, Message.PROPERTYMAPPING_DUPLICATE_TARGETS, prism.target() ); } else { - mappings.add( mapping ); + mappings.add( options ); } } public static MappingOptions forIgnore(String targetName) { return new MappingOptions( + targetName, null, null, null, null, - targetName, - null, - true, - null, null, null, null, + true, null, null, Collections.emptySet(), @@ -194,66 +176,66 @@ public static MappingOptions forIgnore(String targetName) { ); } - private static boolean isConsistent(MappingPrism mappingPrism, ExecutableElement element, + private static boolean isConsistent(MappingPrism prism, ExecutableElement method, FormattingMessager messager) { - if ( mappingPrism.target().isEmpty() ) { + if ( prism.target().isEmpty() ) { messager.printMessage( - element, - mappingPrism.mirror, - mappingPrism.values.target(), + method, + prism.mirror, + prism.values.target(), Message.PROPERTYMAPPING_EMPTY_TARGET ); return false; } Message message = null; - if ( !mappingPrism.source().isEmpty() && mappingPrism.values.constant() != null ) { + if ( !prism.source().isEmpty() && prism.values.constant() != null ) { message = Message.PROPERTYMAPPING_SOURCE_AND_CONSTANT_BOTH_DEFINED; } - else if ( !mappingPrism.source().isEmpty() && mappingPrism.values.expression() != null ) { + else if ( !prism.source().isEmpty() && prism.values.expression() != null ) { message = Message.PROPERTYMAPPING_SOURCE_AND_EXPRESSION_BOTH_DEFINED; } - else if ( mappingPrism.values.expression() != null && mappingPrism.values.constant() != null ) { + else if ( prism.values.expression() != null && prism.values.constant() != null ) { message = Message.PROPERTYMAPPING_EXPRESSION_AND_CONSTANT_BOTH_DEFINED; } - else if ( mappingPrism.values.expression() != null && mappingPrism.values.defaultValue() != null ) { + else if ( prism.values.expression() != null && prism.values.defaultValue() != null ) { message = Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_VALUE_BOTH_DEFINED; } - else if ( mappingPrism.values.constant() != null && mappingPrism.values.defaultValue() != null ) { + else if ( prism.values.constant() != null && prism.values.defaultValue() != null ) { message = Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_VALUE_BOTH_DEFINED; } - else if ( mappingPrism.values.expression() != null && mappingPrism.values.defaultExpression() != null ) { + else if ( prism.values.expression() != null && prism.values.defaultExpression() != null ) { message = Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } - else if ( mappingPrism.values.constant() != null && mappingPrism.values.defaultExpression() != null ) { + else if ( prism.values.constant() != null && prism.values.defaultExpression() != null ) { message = Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } - else if ( mappingPrism.values.defaultValue() != null && mappingPrism.values.defaultExpression() != null ) { + else if ( prism.values.defaultValue() != null && prism.values.defaultExpression() != null ) { message = Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } - else if ( mappingPrism.values.expression() != null - && ( mappingPrism.values.qualifiedByName() != null || mappingPrism.values.qualifiedBy() != null ) ) { + else if ( prism.values.expression() != null + && ( prism.values.qualifiedByName() != null || prism.values.qualifiedBy() != null ) ) { message = Message.PROPERTYMAPPING_EXPRESSION_AND_QUALIFIER_BOTH_DEFINED; } - else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null - && mappingPrism.values.defaultValue() != null ) { + else if ( prism.values.nullValuePropertyMappingStrategy() != null + && prism.values.defaultValue() != null ) { message = Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_NVPMS; } - else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null - && mappingPrism.values.constant() != null ) { + else if ( prism.values.nullValuePropertyMappingStrategy() != null + && prism.values.constant() != null ) { message = Message.PROPERTYMAPPING_CONSTANT_VALUE_AND_NVPMS; } - else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null - && mappingPrism.values.expression() != null ) { + else if ( prism.values.nullValuePropertyMappingStrategy() != null + && prism.values.expression() != null ) { message = Message.PROPERTYMAPPING_EXPRESSION_VALUE_AND_NVPMS; } - else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null - && mappingPrism.values.defaultExpression() != null ) { + else if ( prism.values.nullValuePropertyMappingStrategy() != null + && prism.values.defaultExpression() != null ) { message = Message.PROPERTYMAPPING_DEFAULT_EXPERSSION_AND_NVPMS; } - else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null - && mappingPrism.ignore() != null && mappingPrism.ignore() ) { + else if ( prism.values.nullValuePropertyMappingStrategy() != null + && prism.ignore() != null && prism.ignore() ) { message = Message.PROPERTYMAPPING_IGNORE_AND_NVPMS; } @@ -261,36 +243,42 @@ else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null return true; } else { - messager.printMessage( element, mappingPrism.mirror, message ); + messager.printMessage( method, prism.mirror, message ); return false; } } @SuppressWarnings("checkstyle:parameternumber") - private MappingOptions(String sourceName, String constant, String javaExpression, String defaultJavaExpression, - String targetName, String defaultValue, boolean isIgnored, AnnotationMirror mirror, - AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue, - FormattingParameters formattingParameters, SelectionParameters selectionParameters, - AnnotationValue dependsOnAnnotationValue, Set dependsOn, - NullValueCheckStrategyPrism nullValueCheckStrategy, - NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy, - InheritContext inheritContext) { + private MappingOptions(String targetName, + AnnotationValue targetAnnotationValue, + String sourceName, + AnnotationValue sourceAnnotationValue, + String constant, + String javaExpression, + String defaultJavaExpression, + String defaultValue, + boolean isIgnored, + FormattingParameters formattingParameters, + SelectionParameters selectionParameters, + Set dependsOn, + MappingPrism prism, + InheritContext inheritContext, + DelegatingOptions next + ) { + super( next ); + this.targetName = targetName; + this.targetAnnotationValue = targetAnnotationValue; this.sourceName = sourceName; + this.sourceAnnotationValue = sourceAnnotationValue; this.constant = constant; this.javaExpression = javaExpression; this.defaultJavaExpression = defaultJavaExpression; - this.targetName = targetName; this.defaultValue = defaultValue; this.isIgnored = isIgnored; - this.mirror = mirror; - this.sourceAnnotationValue = sourceAnnotationValue; - this.targetAnnotationValue = targetAnnotationValue; this.formattingParameters = formattingParameters; this.selectionParameters = selectionParameters; - this.dependsOnAnnotationValue = dependsOnAnnotationValue; this.dependsOn = dependsOn; - this.nullValueCheckStrategy = nullValueCheckStrategy; - this.nullValuePropertyMappingStrategy = nullValuePropertyMappingStrategy; + this.prism = prism; this.inheritContext = inheritContext; } @@ -332,7 +320,13 @@ private static String getDefaultExpression(MappingPrism mappingPrism, Executable return javaExpressionMatcher.group( 1 ).trim(); } - // immutable properties + public String getTargetName() { + return targetName; + } + + public AnnotationValue getTargetAnnotationValue() { + return targetAnnotationValue; + } /** * Returns the complete source name of this mapping, either a qualified (e.g. {@code parameter1.foo}) or @@ -344,6 +338,10 @@ public String getSourceName() { return sourceName; } + public AnnotationValue getSourceAnnotationValue() { + return sourceAnnotationValue; + } + public String getConstant() { return constant; } @@ -356,10 +354,6 @@ public String getDefaultJavaExpression() { return defaultJavaExpression; } - public String getTargetName() { - return targetName; - } - public String getDefaultValue() { return defaultValue; } @@ -377,27 +371,11 @@ public boolean isIgnored() { } public AnnotationMirror getMirror() { - return mirror; - } - - public AnnotationValue getSourceAnnotationValue() { - return sourceAnnotationValue; - } - - public AnnotationValue getTargetAnnotationValue() { - return targetAnnotationValue; + return prism == null ? null : prism.mirror; } public AnnotationValue getDependsOnAnnotationValue() { - return dependsOnAnnotationValue; - } - - public NullValueCheckStrategyPrism getNullValueCheckStrategy() { - return nullValueCheckStrategy; - } - - public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { - return nullValuePropertyMappingStrategy; + return prism == null ? null : prism.values.dependsOn(); } public Set getDependsOn() { @@ -408,8 +386,19 @@ public InheritContext getInheritContext() { return inheritContext; } - //// mutable properties + @Override + public NullValueCheckStrategyPrism getNullValueCheckStrategy() { + return null == prism || null == prism.values.nullValueCheckStrategy() ? + next().getNullValueCheckStrategy() + : NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); + } + @Override + public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { + return null == prism || null == prism.values.nullValuePropertyMappingStrategy() ? + next().getNullValuePropertyMappingStrategy() + : NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); + } /** * mapping can only be inversed if the source was not a constant nor an expression nor a nested property @@ -421,27 +410,27 @@ public boolean canInverse() { return constant == null && javaExpression == null && !( isIgnored && sourceName == null ); } - public MappingOptions copyForInverseInheritance(SourceMethod method ) { + public MappingOptions copyForInverseInheritance(SourceMethod templateMethod, + BeanMappingOptions beanMappingOptions ) { - return new MappingOptions( + MappingOptions mappingOptions = new MappingOptions( + sourceName != null ? sourceName : targetName, + targetAnnotationValue, sourceName != null ? targetName : null, + sourceAnnotationValue, null, // constant null, // expression null, // defaultExpression - sourceName != null ? sourceName : targetName, null, isIgnored, - mirror, - sourceAnnotationValue, - targetAnnotationValue, formattingParameters, selectionParameters, - dependsOnAnnotationValue, Collections.emptySet(), - nullValueCheckStrategy, - nullValuePropertyMappingStrategy, - new InheritContext( true, false, method ) + prism, + new InheritContext( true, false, templateMethod ), + beanMappingOptions ); + return mappingOptions; } @@ -450,27 +439,26 @@ public MappingOptions copyForInverseInheritance(SourceMethod method ) { * * @return the copy */ - public MappingOptions copyForForwardInheritance(SourceMethod method ) { - return new MappingOptions( + public MappingOptions copyForForwardInheritance(SourceMethod templateMethod, + BeanMappingOptions beanMappingOptions ) { + MappingOptions mappingOptions = new MappingOptions( + targetName, + targetAnnotationValue, sourceName, + sourceAnnotationValue, constant, javaExpression, defaultJavaExpression, - targetName, defaultValue, isIgnored, - mirror, - sourceAnnotationValue, - targetAnnotationValue, formattingParameters, selectionParameters, - dependsOnAnnotationValue, dependsOn, - nullValueCheckStrategy, - nullValuePropertyMappingStrategy, - new InheritContext( false, true, method ) + prism, + new InheritContext( false, true, templateMethod ), + beanMappingOptions ); - + return mappingOptions; } @Override @@ -502,5 +490,10 @@ public String toString() { "\n}"; } + @Override + public boolean hasAnnotation() { + return prism != null; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index cf122fe2d8..3930dc76b1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -6,14 +6,12 @@ package org.mapstruct.ap.internal.model.source; import java.util.List; - import javax.lang.model.element.ExecutableElement; import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod; -import org.mapstruct.ap.internal.util.MapperOptions; /** * This interface makes available common method properties and a matching method There are 2 known implementors: @@ -164,12 +162,6 @@ public interface Method { */ Type getDefiningType(); - /** - * - * @return the mapper config when this method needs to be implemented - */ - MapperOptions getMapperConfiguration(); - /** * @return {@code true}, if the method represents a mapping lifecycle callback (Before/After mapping method) */ @@ -185,7 +177,7 @@ public interface Method { * * @return the mapping options for this method */ - MappingMethodOptions getMappingOptions(); + MappingMethodOptions getOptions(); /** * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 3048b86add..92c9b94f0a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -20,7 +20,6 @@ import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.prism.ObjectFactoryPrism; import org.mapstruct.ap.internal.util.Executables; -import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.model.source.MappingMethodUtils.isEnumMapping; @@ -49,8 +48,7 @@ public class SourceMethod implements Method { private final Type returnType; private final Accessibility accessibility; private final List exceptionTypes; - private final MapperOptions config; - private final MappingMethodOptions mappingOptions; + private final MappingMethodOptions mappingMethodOptions; private final List prototypeMethods; private final Type mapperToImplement; @@ -83,9 +81,9 @@ public static class Builder { private BeanMappingOptions beanMapping = null; private Types typeUtils; private TypeFactory typeFactory = null; - private MapperOptions mapperConfig = null; + private MapperOptions mapper = null; private List prototypeMethods = Collections.emptyList(); - private List valueMappings; + private List valueMappings; private ParameterProvidedMethods contextProvidedMethods; public Builder setDeclaringMapper(Type declaringMapper) { @@ -113,27 +111,27 @@ public Builder setExceptionTypes(List exceptionTypes) { return this; } - public Builder setMappings(Set mappings) { + public Builder setMappingOptions(Set mappings) { this.mappings = mappings; return this; } - public Builder setIterableMapping(IterableMappingOptions iterableMapping) { + public Builder setIterableMappingOptions(IterableMappingOptions iterableMapping) { this.iterableMapping = iterableMapping; return this; } - public Builder setMapMapping(MapMappingOptions mapMapping) { + public Builder setMapMappingOptions(MapMappingOptions mapMapping) { this.mapMapping = mapMapping; return this; } - public Builder setBeanMapping(BeanMappingOptions beanMapping) { + public Builder setBeanMappingOptions(BeanMappingOptions beanMapping) { this.beanMapping = beanMapping; return this; } - public Builder setValueMappings(List valueMappings) { + public Builder setValueMappingOptionss(List valueMappings) { this.valueMappings = valueMappings; return this; } @@ -148,8 +146,8 @@ public Builder setTypeFactory(TypeFactory typeFactory) { return this; } - public Builder setMapperConfiguration(MapperOptions mapperConfig) { - this.mapperConfig = mapperConfig; + public Builder setMapper(MapperOptions mapper) { + this.mapper = mapper; return this; } @@ -174,14 +172,14 @@ public SourceMethod build() { mappings = Collections.emptySet(); } - MappingMethodOptions mappingOptions = - new MappingMethodOptions( mappings, iterableMapping, mapMapping, beanMapping, valueMappings ); + MappingMethodOptions mappingMethodOptions = + new MappingMethodOptions( mapper, mappings, iterableMapping, mapMapping, beanMapping, valueMappings ); - return new SourceMethod( this, mappingOptions ); + return new SourceMethod( this, mappingMethodOptions ); } } - private SourceMethod(Builder builder, MappingMethodOptions mappingOptions) { + private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) { this.declaringMapper = builder.declaringMapper; this.executable = builder.executable; this.parameters = builder.parameters; @@ -189,7 +187,7 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingOptions) { this.exceptionTypes = builder.exceptionTypes; this.accessibility = Accessibility.fromModifiers( builder.executable.getModifiers() ); - this.mappingOptions = mappingOptions; + this.mappingMethodOptions = mappingMethodOptions; this.sourceParameters = Parameter.getSourceParameters( parameters ); this.contextParameters = Parameter.getContextParameters( parameters ); @@ -202,7 +200,6 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingOptions) { this.typeUtils = builder.typeUtils; this.typeFactory = builder.typeFactory; - this.config = builder.mapperConfig; this.prototypeMethods = builder.prototypeMethods; this.mapperToImplement = builder.definingType; } @@ -360,7 +357,7 @@ public boolean isRemovedEnumMapping() { public boolean isValueMapping() { if ( isValueMapping == null ) { - isValueMapping = isEnumMapping( this ) && mappingOptions.getMappings().isEmpty(); + isValueMapping = isEnumMapping( this ) && mappingMethodOptions.getMappings().isEmpty(); } return isValueMapping; } @@ -463,8 +460,8 @@ public List getThrownTypes() { } @Override - public MappingMethodOptions getMappingOptions() { - return mappingOptions; + public MappingMethodOptions getOptions() { + return mappingMethodOptions; } @Override @@ -482,11 +479,6 @@ public Type getDefiningType() { return mapperToImplement; } - @Override - public MapperOptions getMapperConfiguration() { - return config; - } - @Override public boolean isLifecycleCallbackMethod() { return Executables.isLifecycleCallbackMethod( getExecutable() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java similarity index 83% rename from processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java index cd0f0a4315..8d346af4b7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java @@ -24,7 +24,7 @@ * * @author Sjaak Derksen */ -public class ValueMapping { +public class ValueMappingOptions { private final String source; private final String target; @@ -33,11 +33,11 @@ public class ValueMapping { private final AnnotationValue targetAnnotationValue; public static void fromMappingsPrism(ValueMappingsPrism mappingsAnnotation, ExecutableElement method, - FormattingMessager messager, List mappings) { + FormattingMessager messager, List mappings) { boolean anyFound = false; for ( ValueMappingPrism mappingPrism : mappingsAnnotation.value() ) { - ValueMapping mapping = fromMappingPrism( mappingPrism ); + ValueMappingOptions mapping = fromMappingPrism( mappingPrism ); if ( mapping != null ) { if ( !mappings.contains( mapping ) ) { @@ -69,14 +69,14 @@ public static void fromMappingsPrism(ValueMappingsPrism mappingsAnnotation, Exec } } - public static ValueMapping fromMappingPrism( ValueMappingPrism mappingPrism ) { + public static ValueMappingOptions fromMappingPrism(ValueMappingPrism mappingPrism ) { - return new ValueMapping( mappingPrism.source(), mappingPrism.target(), mappingPrism.mirror, + return new ValueMappingOptions( mappingPrism.source(), mappingPrism.target(), mappingPrism.mirror, mappingPrism.values.source(), mappingPrism.values.target() ); } - private ValueMapping(String source, String target, AnnotationMirror mirror, AnnotationValue sourceAnnotationValue, - AnnotationValue targetAnnotationValue ) { + private ValueMappingOptions(String source, String target, AnnotationMirror mirror, + AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue ) { this.source = source; this.target = target; this.mirror = mirror; @@ -110,10 +110,10 @@ public AnnotationValue getTargetAnnotationValue() { return targetAnnotationValue; } - public ValueMapping inverse() { - ValueMapping result; + public ValueMappingOptions inverse() { + ValueMappingOptions result; if ( !(ANY_REMAINING.equals( source ) || ANY_UNMAPPED.equals( source ) ) ) { - result = new ValueMapping( + result = new ValueMappingOptions( target, source, mirror, @@ -141,7 +141,7 @@ public boolean equals(Object obj) { if ( getClass() != obj.getClass() ) { return false; } - final ValueMapping other = (ValueMapping) obj; + final ValueMappingOptions other = (ValueMappingOptions) obj; return Objects.equals( this.source, other.source ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java index 3fdfd0eeae..0621a68db7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java @@ -19,7 +19,6 @@ import org.mapstruct.ap.internal.model.source.MappingMethodOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; -import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.util.Collections.first; @@ -254,11 +253,6 @@ public Type getDefiningType() { return null; } - @Override - public MapperOptions getMapperConfiguration() { - return null; - } - @Override public boolean isLifecycleCallbackMethod() { return false; @@ -270,7 +264,7 @@ public boolean isUpdateMethod() { } @Override - public MappingMethodOptions getMappingOptions() { + public MappingMethodOptions getOptions() { return MappingMethodOptions.empty(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueMappingStrategyPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueMappingStrategyPrism.java index b0f11bcc7c..bd5f05ff76 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueMappingStrategyPrism.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueMappingStrategyPrism.java @@ -12,6 +12,16 @@ */ public enum NullValueMappingStrategyPrism { - RETURN_NULL, - RETURN_DEFAULT; + RETURN_NULL( false ), + RETURN_DEFAULT( true ); + + private final boolean returnDefault; + + NullValueMappingStrategyPrism(boolean returnDefault) { + this.returnDefault = returnDefault; + } + + public boolean isReturnDefault() { + return returnDefault; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java index 17707d446c..6991e4c8ff 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java @@ -24,7 +24,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; -import org.mapstruct.ap.internal.util.MapperOptions; +import org.mapstruct.ap.internal.model.source.MapperOptions; /** * An {@link ModelElementProcessor} which converts the given {@link Mapper} object into an annotation based component @@ -42,10 +42,10 @@ public abstract class AnnotationBasedComponentModelProcessor implements ModelEle public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, Mapper mapper) { this.typeFactory = context.getTypeFactory(); - MapperOptions mapperConfiguration = MapperOptions.getInstanceOn( mapperTypeElement ); + MapperOptions mapperAnnotation = MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() ); - String componentModel = mapperConfiguration.componentModel( context.getOptions() ); - InjectionStrategyPrism injectionStrategy = mapperConfiguration.getInjectionStrategy( context.getOptions() ); + String componentModel = mapperAnnotation.componentModel(); + InjectionStrategyPrism injectionStrategy = mapperAnnotation.getInjectionStrategy(); if ( !getComponentModelIdentifier().equalsIgnoreCase( componentModel ) ) { return mapper; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index c67780de43..02137a59e3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -40,7 +40,7 @@ import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.model.source.BeanMappingOptions; +import org.mapstruct.ap.internal.model.source.MapperOptions; import org.mapstruct.ap.internal.model.source.MappingMethodOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; @@ -56,7 +56,6 @@ import org.mapstruct.ap.internal.processor.creation.MappingResolverImpl; import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.FormattingMessager; -import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.version.VersionInformation; @@ -93,8 +92,8 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, L this.typeFactory = context.getTypeFactory(); this.accessorNaming = context.getAccessorNaming(); - MapperOptions mapperConfig = MapperOptions.getInstanceOn( mapperTypeElement ); - List mapperReferences = initReferencedMappers( mapperTypeElement, mapperConfig ); + MapperOptions mapperOptions = MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() ); + List mapperReferences = initReferencedMappers( mapperTypeElement, mapperOptions ); MappingBuilderContext ctx = new MappingBuilderContext( typeFactory, @@ -118,7 +117,7 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, L mapperReferences ); this.mappingContext = ctx; - return getMapper( mapperTypeElement, mapperConfig, sourceModel ); + return getMapper( mapperTypeElement, mapperOptions, sourceModel ); } @Override @@ -126,11 +125,11 @@ public int getPriority() { return 1000; } - private List initReferencedMappers(TypeElement element, MapperOptions mapperConfig) { + private List initReferencedMappers(TypeElement element, MapperOptions mapperAnnotation) { List result = new LinkedList<>(); List variableNames = new LinkedList<>(); - for ( TypeMirror usedMapper : mapperConfig.uses() ) { + for ( TypeMirror usedMapper : mapperAnnotation.uses() ) { DefaultMapperReference mapperReference = DefaultMapperReference.getInstance( typeFactory.getType( usedMapper ), MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null, @@ -145,9 +144,9 @@ private List initReferencedMappers(TypeElement element, MapperO return result; } - private Mapper getMapper(TypeElement element, MapperOptions mapperConfig, List methods) { + private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List methods) { - List mappingMethods = getMappingMethods( mapperConfig, methods ); + List mappingMethods = getMappingMethods( mapperOptions, methods ); mappingMethods.addAll( mappingContext.getUsedSupportedMappings() ); mappingMethods.addAll( mappingContext.getMappingsToGenerate() ); @@ -168,13 +167,13 @@ private Mapper getMapper(TypeElement element, MapperOptions mapperConfig, List methods, String implName, - String implPackage) { + String implPackage, SortedSet extraImports) { DecoratedWithPrism decoratorPrism = DecoratedWithPrism.getInstanceOn( element ); if ( decoratorPrism == null ) { @@ -248,31 +247,30 @@ else if ( constructor.getParameters().size() == 1 ) { .versionInformation( versionInformation ) .implName( implName ) .implPackage( implPackage ) - .extraImports( getExtraImports( element ) ) + .extraImports( extraImports ) .build(); return decorator; } - private SortedSet getExtraImports(TypeElement element) { + private SortedSet getExtraImports(TypeElement element, MapperOptions mapperOptions) { SortedSet extraImports = new TreeSet<>(); - MapperOptions mapperConfiguration = MapperOptions.getInstanceOn( element ); - for ( TypeMirror extraImport : mapperConfiguration.imports() ) { + for ( TypeMirror extraImport : mapperOptions.imports() ) { Type type = typeFactory.getType( extraImport ); extraImports.add( type ); } // Add original package if a dest package has been set - if ( !"default".equals( mapperConfiguration.implementationPackage() ) ) { + if ( !"default".equals( mapperOptions.implementationPackage() ) ) { extraImports.add( typeFactory.getType( element ) ); } return extraImports; } - private List getMappingMethods(MapperOptions mapperConfig, List methods) { + private List getMappingMethods(MapperOptions mapperAnnotation, List methods) { List mappingMethods = new ArrayList<>(); for ( SourceMethod method : methods ) { @@ -280,14 +278,16 @@ private List getMappingMethods(MapperOptions mapperConfig, List() ); + mergeInheritedOptions( method, mapperAnnotation, methods, new ArrayList<>() ); - MappingMethodOptions mappingOptions = method.getMappingOptions(); + MappingMethodOptions mappingOptions = method.getOptions(); boolean hasFactoryMethod = false; if ( method.isIterableMapping() ) { this.messager.note( 1, Message.ITERABLEMAPPING_CREATE_NOTE, method ); + + IterableMappingMethod iterableMappingMethod = createWithElementMappingMethod( method, mappingOptions, @@ -360,7 +360,7 @@ else if ( method.isStreamMapping() ) { } else { this.messager.note( 1, Message.BEANMAPPING_CREATE_NOTE, method ); - BuilderPrism builderPrism = BeanMappingOptions.builderPrismFor( method ); + BuilderPrism builderPrism = method.getOptions().getBeanMapping().getBuilderPrism(); BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder(); BeanMappingMethod beanMappingMethod = builder .mappingContext( mappingContext ) @@ -387,16 +387,14 @@ else if ( method.isStreamMapping() ) { } private M createWithElementMappingMethod(SourceMethod method, - MappingMethodOptions mappingOptions, ContainerMappingMethodBuilder builder) { + MappingMethodOptions mappingMethodOptions, ContainerMappingMethodBuilder builder) { FormattingParameters formattingParameters = null; SelectionParameters selectionParameters = null; - NullValueMappingStrategyPrism nullValueMappingStrategy = null; - if ( mappingOptions.getIterableMapping() != null ) { - formattingParameters = mappingOptions.getIterableMapping().getFormattingParameters(); - selectionParameters = mappingOptions.getIterableMapping().getSelectionParameters(); - nullValueMappingStrategy = mappingOptions.getIterableMapping().getNullValueMappingStrategy(); + if ( mappingMethodOptions.getIterableMapping() != null ) { + formattingParameters = mappingMethodOptions.getIterableMapping().getFormattingParameters(); + selectionParameters = mappingMethodOptions.getIterableMapping().getSelectionParameters(); } return builder @@ -404,7 +402,6 @@ private M createWithElementMappingMethod(Sour .method( method ) .formattingParameters( formattingParameters ) .selectionParameters( selectionParameters ) - .nullValueMappingStrategy( nullValueMappingStrategy ) .build(); } @@ -424,7 +421,7 @@ private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConf initializingMethods.add( method ); - MappingMethodOptions mappingOptions = method.getMappingOptions(); + MappingMethodOptions mappingOptions = method.getOptions(); List applicableReversePrototypeMethods = method.getApplicableReversePrototypeMethods(); SourceMethod inverseTemplateMethod = @@ -571,7 +568,7 @@ private SourceMethod extractInitializedOptions(SourceMethod resultMethod, MapperOptions mapperConfig, List initializingMethods) { if ( resultMethod != null ) { - if ( !resultMethod.getMappingOptions().isFullyInitialized() ) { + if ( !resultMethod.getOptions().isFullyInitialized() ) { mergeInheritedOptions( resultMethod, mapperConfig, rawMethods, initializingMethods ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java index 967d13f5f6..f74a84c491 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java @@ -6,7 +6,6 @@ package org.mapstruct.ap.internal.processor; import java.io.IOException; - import javax.annotation.processing.Filer; import javax.lang.model.element.TypeElement; import javax.tools.FileObject; @@ -15,7 +14,7 @@ import org.mapstruct.ap.internal.model.GeneratedType; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.ServicesEntry; -import org.mapstruct.ap.internal.util.MapperOptions; +import org.mapstruct.ap.internal.model.source.MapperOptions; import org.mapstruct.ap.internal.writer.ModelWriter; /** @@ -37,7 +36,7 @@ public Void process(ProcessorContext context, TypeElement mapperTypeElement, Map } else { String componentModel = - MapperOptions.getInstanceOn( mapperTypeElement ).componentModel( context.getOptions() ); + MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() ).componentModel( ); spiGenerationNeeded = "default".equals( componentModel ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index fcc365b3eb..e1f62bf6b0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -29,10 +29,11 @@ import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.IterableMappingOptions; import org.mapstruct.ap.internal.model.source.MapMappingOptions; +import org.mapstruct.ap.internal.model.source.MapperOptions; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SourceMethod; -import org.mapstruct.ap.internal.model.source.ValueMapping; +import org.mapstruct.ap.internal.model.source.ValueMappingOptions; import org.mapstruct.ap.internal.prism.BeanMappingPrism; import org.mapstruct.ap.internal.prism.IterableMappingPrism; import org.mapstruct.ap.internal.prism.MapMappingPrism; @@ -45,7 +46,6 @@ import org.mapstruct.ap.internal.util.AnnotationProcessingException; import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.FormattingMessager; -import org.mapstruct.ap.internal.util.MapperOptions; import org.mapstruct.ap.internal.util.Message; import static org.mapstruct.ap.internal.util.Executables.getAllEnclosedExecutableElements; @@ -81,21 +81,21 @@ public List process(ProcessorContext context, TypeElement mapperTy this.messager.note( 0, Message.PROCESSING_NOTE, mapperTypeElement ); - MapperOptions mapperConfig = MapperOptions.getInstanceOn( mapperTypeElement ); + MapperOptions mapperOptions = MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() ); - if ( mapperConfig != null ) { - this.messager.note( 0, Message.CONFIG_NOTE, mapperConfig.getClass().getName() ); + if ( mapperOptions.hasMapperConfig() ) { + this.messager.note( 0, Message.CONFIG_NOTE, mapperOptions.mapperConfigType().asElement().getSimpleName() ); } - if ( !mapperConfig.isValid() ) { + if ( !mapperOptions.isValid() ) { throw new AnnotationProcessingException( "Couldn't retrieve @Mapper annotation", mapperTypeElement, - mapperConfig.getAnnotationMirror() ); + mapperOptions.getAnnotationMirror() ); } - List prototypeMethods = retrievePrototypeMethods( mapperTypeElement, mapperConfig ); - return retrieveMethods( mapperTypeElement, mapperTypeElement, mapperConfig, prototypeMethods ); + List prototypeMethods = retrievePrototypeMethods( mapperTypeElement, mapperOptions ); + return retrieveMethods( mapperTypeElement, mapperTypeElement, mapperOptions, prototypeMethods ); } @Override @@ -104,16 +104,16 @@ public int getPriority() { } private List retrievePrototypeMethods(TypeElement mapperTypeElement, - MapperOptions mapperConfig) { - if ( mapperConfig.config() == null ) { + MapperOptions mapperAnnotation) { + if ( !mapperAnnotation.hasMapperConfig() ) { return Collections.emptyList(); } - TypeElement typeElement = asTypeElement( mapperConfig.config() ); + TypeElement typeElement = asTypeElement( mapperAnnotation.mapperConfigType() ); List methods = new ArrayList<>(); for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, typeElement ) ) { - ExecutableType methodType = typeFactory.getMethodType( mapperConfig.config(), executable ); + ExecutableType methodType = typeFactory.getMethodType( mapperAnnotation.mapperConfigType(), executable ); List parameters = typeFactory.getParameters( methodType, executable ); boolean containsTargetTypeParameter = SourceMethod.containsTargetTypeParameter( parameters ); @@ -126,7 +126,7 @@ private List retrievePrototypeMethods(TypeElement mapperTypeElemen executable, parameters, containsTargetTypeParameter, - mapperConfig, + mapperAnnotation, prototypeMethods, mapperTypeElement ); @@ -144,12 +144,12 @@ private List retrievePrototypeMethods(TypeElement mapperTypeElemen * * @param usedMapper The type of interest (either the mapper to implement or a used mapper via @uses annotation) * @param mapperToImplement the top level type (mapper) that requires implementation - * @param mapperConfig the mapper config + * @param mapperOptions the mapper config * @param prototypeMethods prototype methods defined in mapper config type * @return All mapping methods declared by the given type */ private List retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement, - MapperOptions mapperConfig, List prototypeMethods) { + MapperOptions mapperOptions, List prototypeMethods) { List methods = new ArrayList<>(); for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, usedMapper ) ) { @@ -157,7 +157,7 @@ private List retrieveMethods(TypeElement usedMapper, TypeElement m usedMapper, executable, mapperToImplement, - mapperConfig, + mapperOptions, prototypeMethods ); if ( method != null ) { @@ -167,11 +167,11 @@ private List retrieveMethods(TypeElement usedMapper, TypeElement m //Add all methods of used mappers in order to reference them in the aggregated model if ( usedMapper.equals( mapperToImplement ) ) { - for ( DeclaredType mapper : mapperConfig.uses() ) { + for ( DeclaredType mapper : mapperOptions.uses() ) { methods.addAll( retrieveMethods( asTypeElement( mapper ), mapperToImplement, - mapperConfig, + mapperOptions, prototypeMethods ) ); } } @@ -186,7 +186,7 @@ private TypeElement asTypeElement(DeclaredType type) { private SourceMethod getMethod(TypeElement usedMapper, ExecutableElement method, TypeElement mapperToImplement, - MapperOptions mapperConfig, + MapperOptions mapperOptions, List prototypeMethods) { ExecutableType methodType = typeFactory.getMethodType( (DeclaredType) usedMapper.asType(), method ); @@ -202,7 +202,7 @@ private SourceMethod getMethod(TypeElement usedMapper, method, parameters, containsTargetTypeParameter, - mapperConfig, + mapperOptions, prototypeMethods, mapperToImplement ); } @@ -219,7 +219,7 @@ else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( method, private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, ExecutableElement method, List parameters, boolean containsTargetTypeParameter, - MapperOptions mapperConfig, + MapperOptions mapperOptions, List prototypeMethods, TypeElement mapperToImplement) { Type returnType = typeFactory.getReturnType( methodType ); @@ -244,36 +244,48 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, } ParameterProvidedMethods contextProvidedMethods = - retrieveContextProvidedMethods( contextParameters, mapperToImplement, mapperConfig ); + retrieveContextProvidedMethods( contextParameters, mapperToImplement, mapperOptions ); + + BeanMappingOptions beanMappingOptions = BeanMappingOptions.getInstanceOn( + BeanMappingPrism.getInstanceOn( method ), + mapperOptions, + method, + messager, + typeUtils, + typeFactory ); + + Set mappingOptions = + getMappings( method, method, beanMappingOptions, new LinkedHashSet<>(), new HashSet<>() ); + + IterableMappingOptions iterableMappingOptions = IterableMappingOptions.fromPrism( + IterableMappingPrism.getInstanceOn( method ), + mapperOptions, + method, + messager, + typeUtils + ); + + MapMappingOptions mapMappingOptions = MapMappingOptions.fromPrism( + MapMappingPrism.getInstanceOn( method ), + mapperOptions, + method, + messager, + typeUtils + ); return new SourceMethod.Builder() .setExecutable( method ) .setParameters( parameters ) .setReturnType( returnType ) .setExceptionTypes( exceptionTypes ) - .setMappings( getMappings( method, method, new LinkedHashSet<>(), new HashSet<>() ) ) - .setIterableMapping( - IterableMappingOptions.fromPrism( - IterableMappingPrism.getInstanceOn( method ), - method, - messager, - typeUtils - ) ) - .setMapMapping( - MapMappingOptions.fromPrism( MapMappingPrism.getInstanceOn( method ), method, messager, typeUtils ) ) - .setBeanMapping( - new BeanMappingOptions.Builder() - .beanMappingPrism( BeanMappingPrism.getInstanceOn( method ) ) - .messager( messager ) - .method( method ) - .typeUtils( typeUtils ) - .typeFactory( typeFactory ) - .build() - ) - .setValueMappings( getValueMappings( method ) ) + .setMapper( mapperOptions ) + .setBeanMappingOptions( beanMappingOptions ) + .setMappingOptions( mappingOptions ) + .setIterableMappingOptions( iterableMappingOptions ) + .setMapMappingOptions( mapMappingOptions ) + .setValueMappingOptionss( getValueMappings( method ) ) .setTypeUtils( typeUtils ) .setTypeFactory( typeFactory ) - .setMapperConfiguration( mapperConfig ) .setPrototypeMethods( prototypeMethods ) .setContextProvidedMethods( contextProvidedMethods ) .build(); @@ -501,24 +513,26 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List getMappings(ExecutableElement method, Element element, Set mappings, + private Set getMappings(ExecutableElement method, Element element, + BeanMappingOptions beanMapping, Set mappings, Set handledElements) { for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { Element lElement = annotationMirror.getAnnotationType().asElement(); if ( isAnnotation( lElement, MAPPING_FQN ) ) { // although getInstanceOn does a search on annotation mirrors, the order is preserved - MappingPrism mappingAnnotation = MappingPrism.getInstanceOn( element ); - MappingOptions.addFromMappingPrism( mappingAnnotation, method, messager, typeUtils, mappings ); + MappingPrism mappingPrism = MappingPrism.getInstanceOn( element ); + MappingOptions.addInstance( mappingPrism, method, beanMapping, messager, typeUtils, mappings ); } else if ( isAnnotation( lElement, MAPPINGS_FQN ) ) { // although getInstanceOn does a search on annotation mirrors, the order is preserved - MappingsPrism mappingsAnnotation = MappingsPrism.getInstanceOn( element ); - MappingOptions.addFromMappingsPrism( mappingsAnnotation, method, messager, typeUtils, mappings ); + MappingsPrism mappingsPrism = MappingsPrism.getInstanceOn( element ); + MappingOptions.addInstances( mappingsPrism, method, beanMapping, messager, typeUtils, mappings ); } else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) && !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG ) @@ -526,7 +540,7 @@ else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) ) { // recur over annotation mirrors handledElements.add( lElement ); - getMappings( method, lElement, mappings, handledElements ); + getMappings( method, lElement, beanMapping, mappings, handledElements ); } } return mappings; @@ -554,21 +568,21 @@ private boolean isAnnotation(Element element, String annotationFQN) { * * @return The mappings for the given method, keyed by target property name */ - private List getValueMappings(ExecutableElement method) { - List valueMappings = new ArrayList<>(); + private List getValueMappings(ExecutableElement method) { + List valueMappings = new ArrayList<>(); ValueMappingPrism mappingAnnotation = ValueMappingPrism.getInstanceOn( method ); ValueMappingsPrism mappingsAnnotation = ValueMappingsPrism.getInstanceOn( method ); if ( mappingAnnotation != null ) { - ValueMapping valueMapping = ValueMapping.fromMappingPrism( mappingAnnotation ); + ValueMappingOptions valueMapping = ValueMappingOptions.fromMappingPrism( mappingAnnotation ); if ( valueMapping != null ) { valueMappings.add( valueMapping ); } } if ( mappingsAnnotation != null ) { - ValueMapping.fromMappingsPrism( mappingsAnnotation, method, messager, valueMappings ); + ValueMappingOptions.fromMappingsPrism( mappingsAnnotation, method, messager, valueMappings ); } return valueMappings; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 3a20a969de..8959eb3614 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -664,7 +664,7 @@ Assignment getAssignment() { void reportMessageWhenNarrowing(FormattingMessager messager, ResolvingAttempt attempt) { if ( NativeTypes.isNarrowing( sourceType.getFullyQualifiedName(), targetType.getFullyQualifiedName() ) ) { - ReportingPolicyPrism policy = attempt.mappingMethod.getMapperConfiguration().typeConversionPolicy(); + ReportingPolicyPrism policy = attempt.mappingMethod.getOptions().getMapper().typeConversionPolicy(); if ( policy == ReportingPolicyPrism.WARN ) { report( messager, attempt, Message.CONVERSION_LOSSY_WARNING ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/verbose/CreateBeanMapping.java b/processor/src/test/java/org/mapstruct/ap/test/verbose/CreateBeanMapping.java index 594318d9f3..65d2bb8039 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/verbose/CreateBeanMapping.java +++ b/processor/src/test/java/org/mapstruct/ap/test/verbose/CreateBeanMapping.java @@ -8,7 +8,7 @@ import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -@Mapper +@Mapper( config = CreateBeanMappingConfig.class ) public interface CreateBeanMapping { CreateBeanMapping INSTANCE = Mappers.getMapper( CreateBeanMapping.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/verbose/CreateBeanMappingConfig.java b/processor/src/test/java/org/mapstruct/ap/test/verbose/CreateBeanMappingConfig.java new file mode 100644 index 0000000000..157613b348 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/verbose/CreateBeanMappingConfig.java @@ -0,0 +1,12 @@ +/* + * 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.verbose; + +import org.mapstruct.MapperConfig; + +@MapperConfig +public interface CreateBeanMappingConfig { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java b/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java index ba5187e74b..0d8003f91f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java @@ -32,11 +32,11 @@ public class VerboseTest { @Test @DisabledOnCompiler( Compiler.ECLIPSE ) @ProcessorOption(name = "mapstruct.verbose", value = "true") - @WithClasses(CreateBeanMapping.class) + @WithClasses({ CreateBeanMapping.class, CreateBeanMappingConfig.class }) @ExpectedNote("^MapStruct: Using accessor naming strategy:.*DefaultAccessorNamingStrategy.*$") @ExpectedNote("^MapStruct: Using builder provider:.*DefaultBuilderProvider.*$") @ExpectedNote("^ MapStruct: processing:.*.CreateBeanMapping.*$") - @ExpectedNote("^ MapStruct: applying mapper configuration:.*MapperConfiguration.*$") + @ExpectedNote("^ MapStruct: applying mapper configuration:.*CreateBeanMappingConfig.*$") public void testGeneralMessages() { } @@ -45,11 +45,11 @@ public void testGeneralMessages() { @WithServiceImplementation(provides = BuilderProvider.class, value = ImmutablesBuilderProvider.class) @WithServiceImplementation(provides = AccessorNamingStrategy.class, value = ImmutablesAccessorNamingStrategy.class) @ProcessorOption(name = "mapstruct.verbose", value = "true") - @WithClasses(CreateBeanMapping.class) + @WithClasses({ CreateBeanMapping.class, CreateBeanMappingConfig.class }) @ExpectedNote("^MapStruct: Using accessor naming strategy:.*ImmutablesAccessorNamingStrategy.*$") @ExpectedNote("^MapStruct: Using builder provider:.*ImmutablesBuilderProvider.*$") @ExpectedNote("^ MapStruct: processing:.*.CreateBeanMapping.*$") - @ExpectedNote("^ MapStruct: applying mapper configuration:.*MapperConfiguration.*$") + @ExpectedNote("^ MapStruct: applying mapper configuration:.*CreateBeanMappingConfig.*$") public void testGeneralWithOtherSPI() { } @@ -58,7 +58,7 @@ public void testGeneralWithOtherSPI() { @WithServiceImplementation(provides = AstModifyingAnnotationProcessor.class, value = AstModifyingAnnotationProcessorSaysNo.class) @ProcessorOption(name = "mapstruct.verbose", value = "true") - @WithClasses(CreateBeanMapping.class) + @WithClasses({ CreateBeanMapping.class, CreateBeanMappingConfig.class }) @ExpectedNote("^MapStruct: referred types not available \\(yet\\), deferring mapper:.*CreateBeanMapping.*$") @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic( @@ -75,7 +75,7 @@ public void testDeferred() { @Test @DisabledOnCompiler( Compiler.ECLIPSE ) @ProcessorOption(name = "mapstruct.verbose", value = "true") - @WithClasses(CreateBeanMapping.class) + @WithClasses({ CreateBeanMapping.class, CreateBeanMappingConfig.class }) @ExpectedNote("^- MapStruct: creating bean mapping method implementation for.*$") @ExpectedNote("^-- MapStruct: creating property mapping.*$") public void testCreateBeanMapping() { From 2d3761051a680a5682b731bb866bdd7a79850ff8 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Mon, 27 Jan 2020 20:16:04 +0100 Subject: [PATCH 022/597] #2011 Introduce gem as replacement of hickory:prism (#2007) --- .../resources/build-config/import-control.xml | 4 +- parent/pom.xml | 17 +- processor/pom.xml | 42 +++-- .../org/mapstruct/ap/MappingProcessor.java | 12 +- .../CollectionMappingStrategyGem.java} | 6 +- .../ap/internal/gem/GemGenerator.java | 65 +++++++ .../InjectionStrategyGem.java} | 6 +- .../MappingConstantsGem.java} | 8 +- .../MappingInheritanceStrategyGem.java} | 8 +- .../NullValueCheckStrategyGem.java} | 6 +- .../NullValueMappingStrategyGem.java} | 8 +- .../NullValuePropertyMappingStrategyGem.java} | 6 +- .../ReportingPolicyGem.java} | 8 +- .../internal/{prism => gem}/package-info.java | 4 +- .../model/AbstractMappingMethodBuilder.java | 6 +- .../ap/internal/model/BeanMappingMethod.java | 18 +- .../model/BuilderFinisherMethodResolver.java | 14 +- .../model/CollectionAssignmentBuilder.java | 24 +-- .../ap/internal/model/Decorator.java | 11 +- .../ap/internal/model/MapMappingMethod.java | 6 +- .../ap/internal/model/PropertyMapping.java | 18 +- .../ap/internal/model/ValueMappingMethod.java | 23 ++- ...nceSetterWrapperForCollectionsAndMaps.java | 14 +- .../model/assignment/SetterWrapper.java | 14 +- .../model/beanmapping/TargetReference.java | 14 +- .../ap/internal/model/common/Parameter.java | 12 +- .../ap/internal/model/common/Type.java | 12 +- .../ap/internal/model/common/TypeFactory.java | 14 +- .../model/source/BeanMappingOptions.java | 107 ++++++----- .../internal/model/source/DefaultOptions.java | 73 ++++---- .../model/source/DelegatingOptions.java | 38 ++-- .../model/source/IterableMappingOptions.java | 67 +++---- .../model/source/MapMappingOptions.java | 102 ++++++----- .../model/source/MapperConfigOptions.java | 116 ++++++------ .../internal/model/source/MapperOptions.java | 132 +++++++------- .../model/source/MappingMethodOptions.java | 6 +- .../internal/model/source/MappingOptions.java | 169 +++++++++--------- .../internal/model/source/SourceMethod.java | 4 +- .../model/source/ValueMappingOptions.java | 34 ++-- .../source/selector/QualifierSelector.java | 11 +- .../selector/XmlElementDeclSelector.java | 22 +-- .../mapstruct/ap/internal/option/Options.java | 8 +- .../ap/internal/prism/PrismGenerator.java | 70 -------- ...nnotationBasedComponentModelProcessor.java | 16 +- .../processor/MapperCreationProcessor.java | 110 ++++++------ .../processor/MethodRetrievalProcessor.java | 52 +++--- .../creation/MappingResolverImpl.java | 8 +- .../ap/internal/util/Executables.java | 8 +- .../ap/test/{prism => gem}/ConstantTest.java | 10 +- .../EnumGemsTest.java} | 42 ++--- .../testutil/runner/CompilingStatement.java | 1 + readme.md | 2 +- 52 files changed, 836 insertions(+), 772 deletions(-) rename processor/src/main/java/org/mapstruct/ap/internal/{prism/CollectionMappingStrategyPrism.java => gem/CollectionMappingStrategyGem.java} (63%) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java rename processor/src/main/java/org/mapstruct/ap/internal/{prism/InjectionStrategyPrism.java => gem/InjectionStrategyGem.java} (60%) rename processor/src/main/java/org/mapstruct/ap/internal/{prism/MappingConstantsPrism.java => gem/MappingConstantsGem.java} (67%) rename processor/src/main/java/org/mapstruct/ap/internal/{prism/MappingInheritanceStrategyPrism.java => gem/MappingInheritanceStrategyGem.java} (76%) rename processor/src/main/java/org/mapstruct/ap/internal/{prism/NullValueCheckStrategyPrism.java => gem/NullValueCheckStrategyGem.java} (60%) rename processor/src/main/java/org/mapstruct/ap/internal/{prism/NullValueMappingStrategyPrism.java => gem/NullValueMappingStrategyGem.java} (65%) rename processor/src/main/java/org/mapstruct/ap/internal/{prism/NullValuePropertyMappingStrategyPrism.java => gem/NullValuePropertyMappingStrategyGem.java} (58%) rename processor/src/main/java/org/mapstruct/ap/internal/{prism/ReportingPolicyPrism.java => gem/ReportingPolicyGem.java} (78%) rename processor/src/main/java/org/mapstruct/ap/internal/{prism => gem}/package-info.java (52%) delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java rename processor/src/test/java/org/mapstruct/ap/test/{prism => gem}/ConstantTest.java (75%) rename processor/src/test/java/org/mapstruct/ap/test/{prism/EnumPrismsTest.java => gem/EnumGemsTest.java} (52%) diff --git a/build-config/src/main/resources/build-config/import-control.xml b/build-config/src/main/resources/build-config/import-control.xml index 7e04d49ce7..6c8056bd8d 100644 --- a/build-config/src/main/resources/build-config/import-control.xml +++ b/build-config/src/main/resources/build-config/import-control.xml @@ -10,8 +10,8 @@ - - + + diff --git a/parent/pom.xml b/parent/pom.xml index db190043a8..1960db3089 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -21,7 +21,7 @@ UTF-8 - 1.0.0 + 1.0.0.Alpha1 3.0.0-M1 3.0.0-M3 @@ -109,9 +109,14 @@ 19.0 - com.jolira - hickory - ${com.jolira.hickory.version} + org.mapstruct.tools.gem + gem-api + ${org.mapstruct.gem.version} + + + org.mapstruct.tools.gem + gem-processor + ${org.mapstruct.gem.version} junit @@ -287,7 +292,7 @@ specified as patterns within a source folder, so we can't exclude generated-sources altogether --> - **/*Prism.java,*/itest/jaxb/xsd/* + **/*Gem.java,*/itest/jaxb/xsd/* @@ -376,7 +381,7 @@ ${org.apache.maven.plugins.javadoc.version} true - org.mapstruct.ap.internal.prism;org.mapstruct.itest.jaxb.xsd.* + org.mapstruct.ap.internal.gem;org.mapstruct.itest.jaxb.xsd.* 8 diff --git a/processor/pom.xml b/processor/pom.xml index 4f792da14a..dfe9bdbe69 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -32,18 +32,9 @@ org.freemarker freemarker - - - - com.jolira - hickory - provided - true + org.mapstruct.tools.gem + gem-api ${project.groupId} @@ -182,6 +173,12 @@ true + + + org.freemarker:* + org.mapstruct.tools.gem:gem-api + + org.freemarker:freemarker @@ -189,12 +186,24 @@ META-INF/*.* + + org.mapstruct.tools.gem:gem-api + + META-INF/*.* + **/GemDefinition.class + **/GemDefinitions.class + + freemarker org.mapstruct.ap.shaded.freemarker + + org.mapstruct.tools.gem + org.mapstruct.ap.shaded.org.mapstruct.tools.gem + @@ -227,9 +236,14 @@ org.apache.maven.plugins maven-compiler-plugin - - net.java.dev.hickory.prism.internal.PrismGenerator - + + + org.mapstruct.tools.gem + gem-processor + ${org.mapstruct.gem.version} + + + diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index e31b2a07b7..546f06256f 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -34,8 +34,8 @@ import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.option.Options; -import org.mapstruct.ap.internal.prism.MapperPrism; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; +import org.mapstruct.ap.internal.gem.MapperGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext; import org.mapstruct.ap.internal.processor.ModelElementProcessor; import org.mapstruct.ap.internal.processor.ModelElementProcessor.ProcessorContext; @@ -66,8 +66,8 @@ *

  • if no error occurred, write out the model into Java source files
  • * *

    - * For reading annotation attributes, prisms as generated with help of the Hickory tool are used. These prisms allow a comfortable access to + * For reading annotation attributes, gems as generated with help of the Hickory tool are used. These gems allow a comfortable access to * annotations and their attributes without depending on their class objects. *

    * The creation of Java source files is done using the FreeMarker template engine. @@ -137,7 +137,7 @@ private Options createOptions() { return new Options( Boolean.valueOf( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ), Boolean.valueOf( processingEnv.getOptions().get( SUPPRESS_GENERATOR_VERSION_INFO_COMMENT ) ), - unmappedTargetPolicy != null ? ReportingPolicyPrism.valueOf( unmappedTargetPolicy.toUpperCase() ) : null, + unmappedTargetPolicy != null ? ReportingPolicyGem.valueOf( unmappedTargetPolicy.toUpperCase() ) : null, processingEnv.getOptions().get( DEFAULT_COMPONENT_MODEL ), processingEnv.getOptions().get( DEFAULT_INJECTION_STRATEGY ), Boolean.valueOf( processingEnv.getOptions().get( ALWAYS_GENERATE_SERVICE_FILE ) ), @@ -236,7 +236,7 @@ private Set getMappers(final Set annotations // on some JDKs, RoundEnvironment.getElementsAnnotatedWith( ... ) returns types with // annotations unknown to the compiler, even though they are not declared Mappers - if ( mapperTypeElement != null && MapperPrism.getInstanceOn( mapperTypeElement ) != null ) { + if ( mapperTypeElement != null && MapperGem.instanceOn( mapperTypeElement ) != null ) { mapperTypes.add( mapperTypeElement ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/CollectionMappingStrategyPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/CollectionMappingStrategyGem.java similarity index 63% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/CollectionMappingStrategyPrism.java rename to processor/src/main/java/org/mapstruct/ap/internal/gem/CollectionMappingStrategyGem.java index 457607d7c7..f88e21a763 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/CollectionMappingStrategyPrism.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/CollectionMappingStrategyGem.java @@ -3,14 +3,14 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.internal.gem; /** - * Prism for the enum {@link org.mapstruct.CollectionMappingStrategy} + * Gem for the enum {@link org.mapstruct.CollectionMappingStrategy} * * @author Andreas Gudian */ -public enum CollectionMappingStrategyPrism { +public enum CollectionMappingStrategyGem { ACCESSOR_ONLY, SETTER_PREFERRED, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java new file mode 100644 index 0000000000..6852f013e7 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -0,0 +1,65 @@ +/* + * 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.internal.gem; + +import javax.xml.bind.annotation.XmlElementDecl; +import javax.xml.bind.annotation.XmlElementRef; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeanMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.Builder; +import org.mapstruct.Context; +import org.mapstruct.DecoratedWith; +import org.mapstruct.InheritConfiguration; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.IterableMapping; +import org.mapstruct.MapMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MapperConfig; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.Mappings; +import org.mapstruct.Named; +import org.mapstruct.ObjectFactory; +import org.mapstruct.Qualifier; +import org.mapstruct.TargetType; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.tools.gem.GemDefinition; + +/** + * Triggers the generation of ge types using Hickory. + * + * @author Gunnar Morling + */ +@GemDefinition(Mapper.class) +@GemDefinition(Mapping.class) +@GemDefinition(Mappings.class) +@GemDefinition(IterableMapping.class) +@GemDefinition(BeanMapping.class) +@GemDefinition(MapMapping.class) +@GemDefinition(TargetType.class) +@GemDefinition(MappingTarget.class) +@GemDefinition(DecoratedWith.class) +@GemDefinition(MapperConfig.class) +@GemDefinition(InheritConfiguration.class) +@GemDefinition(InheritInverseConfiguration.class) +@GemDefinition(Qualifier.class) +@GemDefinition(Named.class) +@GemDefinition(ObjectFactory.class) +@GemDefinition(AfterMapping.class) +@GemDefinition(BeforeMapping.class) +@GemDefinition(ValueMapping.class) +@GemDefinition(ValueMappings.class) +@GemDefinition(Context.class) +@GemDefinition(Builder.class) + +// external types +@GemDefinition(XmlElementDecl.class) +@GemDefinition(XmlElementRef.class) +public class GemGenerator { +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/InjectionStrategyPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/InjectionStrategyGem.java similarity index 60% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/InjectionStrategyPrism.java rename to processor/src/main/java/org/mapstruct/ap/internal/gem/InjectionStrategyGem.java index 839bc26f9c..f8ed93eeb4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/InjectionStrategyPrism.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/InjectionStrategyGem.java @@ -3,14 +3,14 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.internal.gem; /** - * Prism for the enum {@link org.mapstruct.InjectionStrategy}. + * Gem for the enum {@link org.mapstruct.InjectionStrategy}. * * @author Kevin Grüneberg */ -public enum InjectionStrategyPrism { +public enum InjectionStrategyGem { FIELD, CONSTRUCTOR; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java similarity index 67% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java rename to processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java index c03bac6a7e..b988ccde40 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java @@ -3,16 +3,16 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.internal.gem; /** - * Prism for the enum {@link org.mapstruct.MappingConstants} + * Gem for the enum {@link org.mapstruct.MappingConstants} * * @author Sjaak Derksen */ -public final class MappingConstantsPrism { +public final class MappingConstantsGem { - private MappingConstantsPrism() { + private MappingConstantsGem() { } public static final String NULL = ""; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingInheritanceStrategyPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingInheritanceStrategyGem.java similarity index 76% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/MappingInheritanceStrategyPrism.java rename to processor/src/main/java/org/mapstruct/ap/internal/gem/MappingInheritanceStrategyGem.java index 7ea4ce8bbc..b07030d8fc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingInheritanceStrategyPrism.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingInheritanceStrategyGem.java @@ -3,15 +3,15 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.internal.gem; /** - * Prism for the enum {@link org.mapstruct.MappingInheritanceStrategy} + * Gem for the enum {@link org.mapstruct.MappingInheritanceStrategy} * * @author Andreas Gudian */ -public enum MappingInheritanceStrategyPrism { +public enum MappingInheritanceStrategyGem { EXPLICIT( false, false, false ), AUTO_INHERIT_FROM_CONFIG( true, true, false ), @@ -22,7 +22,7 @@ public enum MappingInheritanceStrategyPrism { private final boolean applyForward; private final boolean applyReverse; - MappingInheritanceStrategyPrism(boolean isAutoInherit, boolean applyForward, boolean applyReverse) { + MappingInheritanceStrategyGem(boolean isAutoInherit, boolean applyForward, boolean applyReverse) { this.autoInherit = isAutoInherit; this.applyForward = applyForward; this.applyReverse = applyReverse; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategyPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValueCheckStrategyGem.java similarity index 60% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategyPrism.java rename to processor/src/main/java/org/mapstruct/ap/internal/gem/NullValueCheckStrategyGem.java index 555231f987..b7ab803232 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategyPrism.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValueCheckStrategyGem.java @@ -3,15 +3,15 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.internal.gem; /** - * Prism for the enum {@link org.mapstruct.NullValueCheckStrategy} + * Gem for the enum {@link org.mapstruct.NullValueCheckStrategy} * * @author Sean Huang */ -public enum NullValueCheckStrategyPrism { +public enum NullValueCheckStrategyGem { ON_IMPLICIT_CONVERSION, ALWAYS; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueMappingStrategyPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValueMappingStrategyGem.java similarity index 65% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueMappingStrategyPrism.java rename to processor/src/main/java/org/mapstruct/ap/internal/gem/NullValueMappingStrategyGem.java index bd5f05ff76..3d8991f820 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueMappingStrategyPrism.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValueMappingStrategyGem.java @@ -3,21 +3,21 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.internal.gem; /** - * Prism for the enum {@link org.mapstruct.NullValueMappingStrategy} + * Gem for the enum {@link org.mapstruct.NullValueMappingStrategy} * * @author Sjaak Derksen */ -public enum NullValueMappingStrategyPrism { +public enum NullValueMappingStrategyGem { RETURN_NULL( false ), RETURN_DEFAULT( true ); private final boolean returnDefault; - NullValueMappingStrategyPrism(boolean returnDefault) { + NullValueMappingStrategyGem(boolean returnDefault) { this.returnDefault = returnDefault; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValuePropertyMappingStrategyPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValuePropertyMappingStrategyGem.java similarity index 58% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/NullValuePropertyMappingStrategyPrism.java rename to processor/src/main/java/org/mapstruct/ap/internal/gem/NullValuePropertyMappingStrategyGem.java index bc0cdae9e7..77e4aa48d6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValuePropertyMappingStrategyPrism.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValuePropertyMappingStrategyGem.java @@ -3,15 +3,15 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.internal.gem; /** - * Prism for the enum {@link org.mapstruct.NullValuePropertyMappingStrategy} + * Gem for the enum {@link org.mapstruct.NullValuePropertyMappingStrategy} * * @author Sjaak Derksen */ -public enum NullValuePropertyMappingStrategyPrism { +public enum NullValuePropertyMappingStrategyGem { SET_TO_NULL, SET_TO_DEFAULT, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/ReportingPolicyPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/ReportingPolicyGem.java similarity index 78% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/ReportingPolicyPrism.java rename to processor/src/main/java/org/mapstruct/ap/internal/gem/ReportingPolicyGem.java index 8c06231447..d985c79709 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/ReportingPolicyPrism.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/ReportingPolicyGem.java @@ -3,17 +3,17 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.internal.gem; import javax.tools.Diagnostic; import javax.tools.Diagnostic.Kind; /** - * Prism for the enum {@link org.mapstruct.ReportingPolicy}. + * Gem for the enum {@link org.mapstruct.ReportingPolicy}. * * @author Gunnar Morling */ -public enum ReportingPolicyPrism { +public enum ReportingPolicyGem { IGNORE( null, false, false ), WARN( Kind.WARNING, true, false ), @@ -23,7 +23,7 @@ public enum ReportingPolicyPrism { private final boolean requiresReport; private final boolean failsBuild; - ReportingPolicyPrism(Diagnostic.Kind diagnosticKind, boolean requiresReport, boolean failsBuild) { + ReportingPolicyGem(Diagnostic.Kind diagnosticKind, boolean requiresReport, boolean failsBuild) { this.requiresReport = requiresReport; this.diagnosticKind = diagnosticKind; this.failsBuild = failsBuild; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/package-info.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/package-info.java similarity index 52% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/package-info.java rename to processor/src/main/java/org/mapstruct/ap/internal/gem/package-info.java index 4c5a19a855..b5f4330e4e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/package-info.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/package-info.java @@ -5,7 +5,7 @@ */ /** *

    - * This package contains the generated prism types for accessing the MapStruct annotations in a comfortable way. + * This package contains the generated gem types for accessing the MapStruct annotations in a comfortable way. *

    */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.internal.gem; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index 8f0092578c..019f941741 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -8,7 +8,7 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.prism.BuilderPrism; +import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping; @@ -54,11 +54,11 @@ Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) { sourceRHS.getSourceErrorMessagePart() ); ForgedMethod forgedMethod = forElementMapping( name, sourceType, targetType, method, forgedHistory, true ); - BuilderPrism builderPrism = method.getOptions().getBeanMapping().getBuilderPrism(); + BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); return createForgedAssignment( sourceRHS, - ctx.getTypeFactory().builderTypeFor( targetType, builderPrism ), + ctx.getTypeFactory().builderTypeFor( targetType, builder ), forgedMethod ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index e193b19ffa..af14d384c1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -40,8 +40,8 @@ import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; @@ -158,7 +158,7 @@ else if ( !method.isUpdateMethod() ) { /* the type that needs to be used in the mapping process as target */ Type resultTypeToMap = returnTypeToConstruct == null ? method.getResultType() : returnTypeToConstruct; - CollectionMappingStrategyPrism cms = this.method.getOptions().getMapper().getCollectionMappingStrategy(); + CollectionMappingStrategyGem cms = this.method.getOptions().getMapper().getCollectionMappingStrategy(); // determine accessors Map accessors = resultTypeToMap.getPropertyWriteAccessors( cms ); @@ -823,9 +823,9 @@ private MappingReferences extractMappingReferences(String targetProperty, boolea return null; } - private ReportingPolicyPrism getUnmappedTargetPolicy() { + private ReportingPolicyGem getUnmappedTargetPolicy() { if ( mappingReferences.isForForgedMethods() ) { - return ReportingPolicyPrism.IGNORE; + return ReportingPolicyGem.IGNORE; } return method.getOptions().getMapper().unmappedTargetPolicy(); } @@ -833,7 +833,7 @@ private ReportingPolicyPrism getUnmappedTargetPolicy() { private void reportErrorForUnmappedTargetPropertiesIfRequired() { // fetch settings from element to implement - ReportingPolicyPrism unmappedTargetPolicy = getUnmappedTargetPolicy(); + ReportingPolicyGem unmappedTargetPolicy = getUnmappedTargetPolicy(); if ( method instanceof ForgedMethod && targetProperties.isEmpty() ) { //TODO until we solve 1140 we report this error when the target properties are empty @@ -904,15 +904,15 @@ else if ( !unprocessedTargetProperties.isEmpty() && unmappedTargetPolicy.require } } - private ReportingPolicyPrism getUnmappedSourcePolicy() { + private ReportingPolicyGem getUnmappedSourcePolicy() { if ( mappingReferences.isForForgedMethods() ) { - return ReportingPolicyPrism.IGNORE; + return ReportingPolicyGem.IGNORE; } return method.getOptions().getMapper().unmappedSourcePolicy(); } private void reportErrorForUnmappedSourcePropertiesIfRequired() { - ReportingPolicyPrism unmappedSourcePolicy = getUnmappedSourcePolicy(); + ReportingPolicyGem unmappedSourcePolicy = getUnmappedSourcePolicy(); if ( !unprocessedSourceProperties.isEmpty() && unmappedSourcePolicy.requiresReport() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java index e1a586c522..d72e7cef9c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java @@ -10,7 +10,7 @@ import org.mapstruct.ap.internal.model.common.BuilderType; import org.mapstruct.ap.internal.model.source.Method; -import org.mapstruct.ap.internal.prism.BuilderPrism; +import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; @@ -34,14 +34,14 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp return null; } - BuilderPrism builderMapping = method.getOptions().getBeanMapping().getBuilderPrism(); - if ( builderMapping == null && buildMethods.size() == 1 ) { + BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); + if ( builder == null && buildMethods.size() == 1 ) { return MethodReference.forMethodCall( first( buildMethods ).getSimpleName().toString() ); } else { String buildMethodPattern = DEFAULT_BUILD_METHOD_NAME; - if ( builderMapping != null ) { - buildMethodPattern = builderMapping.buildMethod(); + if ( builder != null ) { + buildMethodPattern = builder.buildMethod().get(); } for ( ExecutableElement buildMethod : buildMethods ) { String methodName = buildMethod.getSimpleName().toString(); @@ -50,7 +50,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp } } - if ( builderMapping == null ) { + if ( builder == null ) { ctx.getMessager().printMessage( method.getExecutable(), Message.BUILDER_NO_BUILD_METHOD_FOUND_DEFAULT, @@ -63,7 +63,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp else { ctx.getMessager().printMessage( method.getExecutable(), - builderMapping.mirror, + builder.mirror(), Message.BUILDER_NO_BUILD_METHOD_FOUND, buildMethodPattern, builderType.getBuilder(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index 1acacad461..04331c4e14 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -15,15 +15,15 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; -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.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_NULL; /** * A builder that is used for creating an assignment to a collection. @@ -61,8 +61,8 @@ public class CollectionAssignmentBuilder { private AccessorType targetAccessorType; private Assignment assignment; private SourceRHS sourceRHS; - private NullValueCheckStrategyPrism nvcs; - private NullValuePropertyMappingStrategyPrism nvpms; + private NullValueCheckStrategyGem nvcs; + private NullValuePropertyMappingStrategyGem nvpms; public CollectionAssignmentBuilder mappingBuilderContext(MappingBuilderContext ctx) { this.ctx = ctx; @@ -114,12 +114,12 @@ public CollectionAssignmentBuilder rightHandSide(SourceRHS sourceRHS) { return this; } - public CollectionAssignmentBuilder nullValueCheckStrategy( NullValueCheckStrategyPrism nvcs ) { + public CollectionAssignmentBuilder nullValueCheckStrategy( NullValueCheckStrategyGem nvcs ) { this.nvcs = nvcs; return this; } - public CollectionAssignmentBuilder nullValuePropertyMappingStrategy( NullValuePropertyMappingStrategyPrism nvpms ) { + public CollectionAssignmentBuilder nullValuePropertyMappingStrategy( NullValuePropertyMappingStrategyGem nvpms ) { this.nvpms = nvpms; return this; } @@ -127,8 +127,8 @@ public CollectionAssignmentBuilder nullValuePropertyMappingStrategy( NullValuePr public Assignment build() { Assignment result = assignment; - CollectionMappingStrategyPrism cms = method.getOptions().getMapper().getCollectionMappingStrategy(); - boolean targetImmutable = cms == CollectionMappingStrategyPrism.TARGET_IMMUTABLE || targetReadAccessor == null; + CollectionMappingStrategyGem cms = method.getOptions().getMapper().getCollectionMappingStrategy(); + boolean targetImmutable = cms == CollectionMappingStrategyGem.TARGET_IMMUTABLE || targetReadAccessor == null; if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) { @@ -169,7 +169,7 @@ else if ( method.isUpdateMethod() && !targetImmutable ) { ); } else if ( result.getType() == Assignment.AssignmentType.DIRECT || - nvcs == NullValueCheckStrategyPrism.ALWAYS ) { + nvcs == NullValueCheckStrategyGem.ALWAYS ) { result = new SetterWrapperForCollectionsAndMapsWithNullCheck( result, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java index ce034837e8..ab37cb77d5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.List; import java.util.SortedSet; - import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; @@ -16,7 +15,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.option.Options; -import org.mapstruct.ap.internal.prism.DecoratedWithPrism; +import org.mapstruct.ap.internal.gem.DecoratedWithGem; import org.mapstruct.ap.internal.version.VersionInformation; /** @@ -29,7 +28,7 @@ public class Decorator extends GeneratedType { public static class Builder extends GeneratedTypeBuilder { private TypeElement mapperElement; - private DecoratedWithPrism decoratorPrism; + private DecoratedWithGem decorator; private boolean hasDelegateConstructor; private String implName; @@ -44,8 +43,8 @@ public Builder mapperElement(TypeElement mapperElement) { return this; } - public Builder decoratorPrism(DecoratedWithPrism decoratorPrism) { - this.decoratorPrism = decoratorPrism; + public Builder decoratedWith(DecoratedWithGem decoratedGem) { + this.decorator = decoratedGem; return this; } @@ -68,7 +67,7 @@ public Decorator build() { String implementationName = implName.replace( Mapper.CLASS_NAME_PLACEHOLDER, Mapper.getFlatName( mapperElement ) ); - Type decoratorType = typeFactory.getType( decoratorPrism.value() ); + Type decoratorType = typeFactory.getType( decorator.value().get() ); DecoratorConstructor decoratorConstructor = new DecoratorConstructor( implementationName, implementationName + "_", diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java index 8fd88a405a..e14382318e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java @@ -20,7 +20,7 @@ import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; @@ -42,7 +42,7 @@ public static class Builder extends AbstractMappingMethodBuilder stringToEnumMapping(Method method, Type targetType ) } private SelectionParameters getSelectionParameters(Method method, Types typeUtils) { - BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( method.getExecutable() ); - if ( beanMappingPrism != null ) { - List qualifiers = beanMappingPrism.qualifiedBy(); - List qualifyingNames = beanMappingPrism.qualifiedByName(); - TypeMirror resultType = beanMappingPrism.resultType(); + BeanMappingGem beanMapping = BeanMappingGem.instanceOn( method.getExecutable() ); + if ( beanMapping != null ) { + List qualifiers = beanMapping.qualifiedBy().get(); + List qualifyingNames = beanMapping.qualifiedByName().get(); + TypeMirror resultType = beanMapping.resultType().get(); return new SelectionParameters( qualifiers, qualifyingNames, resultType, typeUtils ); } return null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java index d07c5acef8..06a2712f00 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java @@ -5,9 +5,9 @@ */ package org.mapstruct.ap.internal.model.assignment; -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; +import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; import java.util.HashSet; import java.util.List; @@ -16,8 +16,8 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; /** * This wrapper handles the situation where an assignment is done for an update method. @@ -41,8 +41,8 @@ public class ExistingInstanceSetterWrapperForCollectionsAndMaps public ExistingInstanceSetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, List thrownTypesToExclude, Type targetType, - NullValueCheckStrategyPrism nvcs, - NullValuePropertyMappingStrategyPrism nvpms, + NullValueCheckStrategyGem nvcs, + NullValuePropertyMappingStrategyGem nvpms, TypeFactory typeFactory, boolean fieldAssignment) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java index 3d8de9fd57..e9fc38741a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java @@ -10,12 +10,12 @@ 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 org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; -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; +import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; /** * Wraps the assignment in a target setter. @@ -92,8 +92,8 @@ public boolean isIncludeSourceNullCheck() { * * @return include a null check */ - public static boolean doSourceNullCheck(Assignment rhs, NullValueCheckStrategyPrism nvcs, - NullValuePropertyMappingStrategyPrism nvpms, Type targetType) { + public static boolean doSourceNullCheck(Assignment rhs, NullValueCheckStrategyGem nvcs, + NullValuePropertyMappingStrategyGem nvpms, Type targetType) { return !rhs.isSourceReferenceParameter() && !rhs.getSourceType().isPrimitive() && (ALWAYS == nvcs diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java index 6041453cf8..8a2a398232 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java @@ -20,8 +20,8 @@ import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; -import org.mapstruct.ap.internal.prism.BuilderPrism; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; @@ -161,7 +161,7 @@ && matchesSourceOrTargetParameter( segments[0], parameter, isReversed ) ) { private List getTargetEntries(Type type, String[] entryNames) { // initialize - CollectionMappingStrategyPrism cms = method.getOptions().getMapper().getCollectionMappingStrategy(); + CollectionMappingStrategyGem cms = method.getOptions().getMapper().getCollectionMappingStrategy(); List targetEntries = new ArrayList<>(); Type nextType = type; @@ -202,8 +202,8 @@ private List getTargetEntries(Type type, String[] entryNames) { ); } else { - BuilderPrism builderPrism = method.getOptions().getBeanMapping().getBuilderPrism(); - builderType = typeFactory.builderTypeFor( nextType, builderPrism ); + BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); + builderType = typeFactory.builderTypeFor( nextType, builder ); propertyEntry = PropertyEntry.forTargetReference( fullName, targetReadAccessor, targetWriteAccessor, @@ -270,8 +270,8 @@ private Type typeBasedOnMethod(Type type) { return type; } else { - BuilderPrism builderPrism = method.getOptions().getBeanMapping().getBuilderPrism(); - return typeFactory.effectiveResultTypeFor( type, builderPrism ); + BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); + return typeFactory.effectiveResultTypeFor( type, builder ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java index 0f3000913b..958725c966 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java @@ -11,9 +11,9 @@ import java.util.stream.Collectors; import javax.lang.model.element.VariableElement; -import org.mapstruct.ap.internal.prism.ContextPrism; -import org.mapstruct.ap.internal.prism.MappingTargetPrism; -import org.mapstruct.ap.internal.prism.TargetTypePrism; +import org.mapstruct.ap.internal.gem.ContextGem; +import org.mapstruct.ap.internal.gem.MappingTargetGem; +import org.mapstruct.ap.internal.gem.TargetTypeGem; import org.mapstruct.ap.internal.util.Collections; /** @@ -117,9 +117,9 @@ public static Parameter forElementAndType(VariableElement element, Type paramete return new Parameter( element.getSimpleName().toString(), parameterType, - MappingTargetPrism.getInstanceOn( element ) != null, - TargetTypePrism.getInstanceOn( element ) != null, - ContextPrism.getInstanceOn( element ) != null, + MappingTargetGem.instanceOn( element ) != null, + TargetTypeGem.instanceOn( element ) != null, + ContextGem.instanceOn( element ) != null, isVarArgs ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 0a448c88ad..103325a800 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -29,7 +29,7 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.Fields; @@ -536,7 +536,7 @@ public Map getPropertyPresenceCheckers() { * @param cmStrategy collection mapping strategy * @return an unmodifiable map of all write accessors indexed by property name */ - public Map getPropertyWriteAccessors( CollectionMappingStrategyPrism cmStrategy ) { + public Map getPropertyWriteAccessors( CollectionMappingStrategyGem cmStrategy ) { // collect all candidate target accessors List candidates = new ArrayList<>( getSetters() ); candidates.addAll( getAlternativeTargetAccessors() ); @@ -554,15 +554,15 @@ public Map getPropertyWriteAccessors( CollectionMappingStrateg // A target access is in general a setter method on the target object. However, in case of collections, // the current target accessor can also be a getter method. // The following if block, checks if the target accessor should be overruled by an add method. - if ( cmStrategy == CollectionMappingStrategyPrism.SETTER_PREFERRED - || cmStrategy == CollectionMappingStrategyPrism.ADDER_PREFERRED - || cmStrategy == CollectionMappingStrategyPrism.TARGET_IMMUTABLE ) { + if ( cmStrategy == CollectionMappingStrategyGem.SETTER_PREFERRED + || cmStrategy == CollectionMappingStrategyGem.ADDER_PREFERRED + || cmStrategy == CollectionMappingStrategyGem.TARGET_IMMUTABLE ) { // first check if there's a setter method. Accessor adderMethod = null; if ( candidate.getAccessorType() == AccessorType.SETTER // ok, the current accessor is a setter. So now the strategy determines what to use - && cmStrategy == CollectionMappingStrategyPrism.ADDER_PREFERRED ) { + && cmStrategy == CollectionMappingStrategyGem.ADDER_PREFERRED ) { adderMethod = getAdderForType( targetType, targetPropertyName ); } else if ( candidate.getAccessorType() == AccessorType.GETTER ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 0647834b81..8fc7fcdc5c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -39,7 +39,7 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; -import org.mapstruct.ap.internal.prism.BuilderPrism; +import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.util.AnnotationProcessingException; import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.internal.util.Extractor; @@ -517,8 +517,8 @@ private ImplementationType getImplementationType(TypeMirror mirror) { return null; } - private BuilderInfo findBuilder(TypeMirror type, BuilderPrism builderPrism, boolean report) { - if ( builderPrism != null && builderPrism.disableBuilder() ) { + private BuilderInfo findBuilder(TypeMirror type, BuilderGem builderGem, boolean report) { + if ( builderGem != null && builderGem.disableBuilder().get() ) { return null; } try { @@ -631,17 +631,17 @@ private boolean canBeProcessed(TypeMirror type) { return true; } - public BuilderType builderTypeFor( Type type, BuilderPrism builderPrism ) { + public BuilderType builderTypeFor( Type type, BuilderGem builder ) { if ( type != null ) { - BuilderInfo builderInfo = findBuilder( type.getTypeMirror(), builderPrism, true ); + BuilderInfo builderInfo = findBuilder( type.getTypeMirror(), builder, true ); return BuilderType.create( builderInfo, type, this, this.typeUtils ); } return null; } - public Type effectiveResultTypeFor( Type type, BuilderPrism builderPrism ) { + public Type effectiveResultTypeFor( Type type, BuilderGem builder ) { if ( type != null ) { - BuilderInfo builderInfo = findBuilder( type.getTypeMirror(), builderPrism, false ); + BuilderInfo builderInfo = findBuilder( type.getTypeMirror(), builder, false ); BuilderType builderType = BuilderType.create( builderInfo, type, this, this.typeUtils ); return builderType != null ? builderType.getBuilder() : type; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index 44aa02c134..984044233b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -8,19 +8,20 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.type.TypeKind; import javax.lang.model.util.Types; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.prism.BeanMappingPrism; -import org.mapstruct.ap.internal.prism.BuilderPrism; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.BeanMappingGem; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.tools.gem.GemValue; /** * Represents an bean mapping as configured via {@code @BeanMapping}. @@ -30,7 +31,7 @@ public class BeanMappingOptions extends DelegatingOptions { private final SelectionParameters selectionParameters; - private final BeanMappingPrism prism; + private final BeanMappingGem beanMapping; /** * creates a mapping for inheritance. Will set @@ -40,17 +41,17 @@ public class BeanMappingOptions extends DelegatingOptions { public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) { BeanMappingOptions options = new BeanMappingOptions( SelectionParameters.forInheritance( beanMapping.selectionParameters ), - beanMapping.prism, + beanMapping.beanMapping, beanMapping ); return options; } - public static BeanMappingOptions getInstanceOn(BeanMappingPrism prism, MapperOptions mapperOptions, + public static BeanMappingOptions getInstanceOn(BeanMappingGem beanMapping, MapperOptions mapperOptions, ExecutableElement method, FormattingMessager messager, Types typeUtils, TypeFactory typeFactory ) { - if ( prism == null || !isConsistent( prism, method, messager ) ) { + if ( beanMapping == null || !isConsistent( beanMapping, method, messager ) ) { BeanMappingOptions options = new BeanMappingOptions( null, null, mapperOptions ); return options; } @@ -62,28 +63,32 @@ public static BeanMappingOptions getInstanceOn(BeanMappingPrism prism, MapperOpt Objects.requireNonNull( typeFactory ); SelectionParameters selectionParameters = new SelectionParameters( - prism.qualifiedBy(), - prism.qualifiedByName(), - TypeKind.VOID != prism.resultType().getKind() ? prism.resultType() : null, + beanMapping.qualifiedBy().get(), + beanMapping.qualifiedByName().get(), + beanMapping.resultType().getValue(), typeUtils ); //TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support? - BeanMappingOptions options = new BeanMappingOptions( selectionParameters, prism, mapperOptions ); + BeanMappingOptions options = new BeanMappingOptions( + selectionParameters, + beanMapping, + mapperOptions + ); return options; } - private static boolean isConsistent(BeanMappingPrism prism, ExecutableElement method, + private static boolean isConsistent(BeanMappingGem gem, ExecutableElement method, FormattingMessager messager) { - if ( TypeKind.VOID == prism.resultType().getKind() - && prism.qualifiedBy().isEmpty() - && prism.qualifiedByName().isEmpty() - && prism.ignoreUnmappedSourceProperties().isEmpty() - && null == prism.values.nullValueCheckStrategy() - && null == prism.values.nullValuePropertyMappingStrategy() - && null == prism.values.nullValueMappingStrategy() - && null == prism.values.ignoreByDefault() - && null == prism.values.builder() ) { + if ( !gem.resultType().hasValue() + && !gem.qualifiedBy().hasValue() + && !gem.qualifiedByName().hasValue() + && !gem.ignoreUnmappedSourceProperties().hasValue() + && !gem.nullValueCheckStrategy().hasValue() + && !gem.nullValuePropertyMappingStrategy().hasValue() + && !gem.nullValueMappingStrategy().hasValue() + && !gem.ignoreByDefault().hasValue() + && !gem.builder().hasValue() ) { messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS ); return false; @@ -91,39 +96,49 @@ private static boolean isConsistent(BeanMappingPrism prism, ExecutableElement me return true; } - private BeanMappingOptions(SelectionParameters selectionParameters, BeanMappingPrism prism, + private BeanMappingOptions(SelectionParameters selectionParameters, + BeanMappingGem beanMapping, DelegatingOptions next) { super( next ); this.selectionParameters = selectionParameters; - this.prism = prism; + this.beanMapping = beanMapping; } // @Mapping, @BeanMapping @Override - public NullValueCheckStrategyPrism getNullValueCheckStrategy() { - return null == prism || null == prism.values.nullValueCheckStrategy() ? - next().getNullValueCheckStrategy() - : NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); + public NullValueCheckStrategyGem getNullValueCheckStrategy() { + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::nullValueCheckStrategy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( NullValueCheckStrategyGem::valueOf ) + .orElse( next().getNullValueCheckStrategy() ); } @Override - public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { - return null == prism || null == prism.values.nullValuePropertyMappingStrategy() ? - next().getNullValuePropertyMappingStrategy() - : NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); + public NullValuePropertyMappingStrategyGem getNullValuePropertyMappingStrategy() { + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::nullValuePropertyMappingStrategy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( NullValuePropertyMappingStrategyGem::valueOf ) + .orElse( next().getNullValuePropertyMappingStrategy() ); } @Override - public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - return null == prism || null == prism.values.nullValueMappingStrategy() ? - next().getNullValueMappingStrategy() - : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + public NullValueMappingStrategyGem getNullValueMappingStrategy() { + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::nullValueMappingStrategy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( NullValueMappingStrategyGem::valueOf ) + .orElse( next().getNullValueMappingStrategy() ); } @Override - public BuilderPrism getBuilderPrism() { - return null == prism || null == prism.values.builder() ? next().getBuilderPrism() : prism.builder(); + public BuilderGem getBuilder() { + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::builder ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .orElse( next().getBuilder() ); } // @BeanMapping specific @@ -133,19 +148,23 @@ public SelectionParameters getSelectionParameters() { } public boolean isignoreByDefault() { - return null == prism ? false : prism.ignoreByDefault(); + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::ignoreByDefault ) + .map( GemValue::get ) + .orElse( false ); } public List getIgnoreUnmappedSourceProperties() { - return null == prism ? Collections.emptyList() : prism.ignoreUnmappedSourceProperties(); + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::ignoreUnmappedSourceProperties ) + .map( GemValue::get ) + .orElse( Collections.emptyList() ); } public AnnotationMirror getMirror() { - return null == prism ? null : prism.mirror; + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::mirror ).orElse( null ); } @Override public boolean hasAnnotation() { - return prism != null; + return beanMapping != null; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index 4b7fdcbc50..adbc5286bc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -10,35 +10,35 @@ import javax.lang.model.type.DeclaredType; import org.mapstruct.ap.internal.option.Options; -import org.mapstruct.ap.internal.prism.BuilderPrism; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; -import org.mapstruct.ap.internal.prism.MapperPrism; -import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.InjectionStrategyGem; +import org.mapstruct.ap.internal.gem.MapperGem; +import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; public class DefaultOptions extends DelegatingOptions { - private final MapperPrism prism; + private final MapperGem mapper; private final Options options; - DefaultOptions(MapperPrism prism, Options options) { + DefaultOptions(MapperGem mapper, Options options) { super( null ); - this.prism = prism; + this.mapper = mapper; this.options = options; } @Override public String implementationName() { - return prism.implementationName(); + return mapper.implementationName().getDefaultValue(); } @Override public String implementationPackage() { - return prism.implementationPackage(); + return mapper.implementationPackage().getDefaultValue(); } @Override @@ -52,21 +52,21 @@ public Set imports() { } @Override - public ReportingPolicyPrism unmappedTargetPolicy() { + public ReportingPolicyGem unmappedTargetPolicy() { if ( options.getUnmappedTargetPolicy() != null ) { return options.getUnmappedTargetPolicy(); } - return ReportingPolicyPrism.valueOf( prism.unmappedTargetPolicy() ); + return ReportingPolicyGem.valueOf( mapper.unmappedTargetPolicy().getDefaultValue() ); } @Override - public ReportingPolicyPrism unmappedSourcePolicy() { - return ReportingPolicyPrism.valueOf( prism.unmappedSourcePolicy() ); + public ReportingPolicyGem unmappedSourcePolicy() { + return ReportingPolicyGem.valueOf( mapper.unmappedSourcePolicy().getDefaultValue() ); } @Override - public ReportingPolicyPrism typeConversionPolicy() { - return ReportingPolicyPrism.valueOf( prism.typeConversionPolicy() ); + public ReportingPolicyGem typeConversionPolicy() { + return ReportingPolicyGem.valueOf( mapper.typeConversionPolicy().getDefaultValue() ); } @Override @@ -74,46 +74,47 @@ public String componentModel() { if ( options.getDefaultComponentModel() != null ) { return options.getDefaultComponentModel(); } - return prism.componentModel(); + return mapper.componentModel().getDefaultValue(); } @Override - public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() { - return MappingInheritanceStrategyPrism.valueOf( prism.mappingInheritanceStrategy() ); + public MappingInheritanceStrategyGem getMappingInheritanceStrategy() { + return MappingInheritanceStrategyGem.valueOf( mapper.mappingInheritanceStrategy().getDefaultValue() ); } @Override - public InjectionStrategyPrism getInjectionStrategy() { + public InjectionStrategyGem getInjectionStrategy() { if ( options.getDefaultInjectionStrategy() != null ) { - return InjectionStrategyPrism.valueOf( options.getDefaultInjectionStrategy().toUpperCase() ); + return InjectionStrategyGem.valueOf( options.getDefaultInjectionStrategy().toUpperCase() ); } - return InjectionStrategyPrism.valueOf( prism.injectionStrategy() ); + return InjectionStrategyGem.valueOf( mapper.injectionStrategy().getDefaultValue() ); } @Override public Boolean isDisableSubMappingMethodsGeneration() { - return prism.disableSubMappingMethodsGeneration(); + return mapper.disableSubMappingMethodsGeneration().getDefaultValue(); } // BeanMapping and Mapping - public CollectionMappingStrategyPrism getCollectionMappingStrategy() { - return CollectionMappingStrategyPrism.valueOf( prism.collectionMappingStrategy() ); + public CollectionMappingStrategyGem getCollectionMappingStrategy() { + return CollectionMappingStrategyGem.valueOf( mapper.collectionMappingStrategy().getDefaultValue() ); } - public NullValueCheckStrategyPrism getNullValueCheckStrategy() { - return NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); + public NullValueCheckStrategyGem getNullValueCheckStrategy() { + return NullValueCheckStrategyGem.valueOf( mapper.nullValueCheckStrategy().getDefaultValue() ); } - public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { - return NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); + public NullValuePropertyMappingStrategyGem getNullValuePropertyMappingStrategy() { + return NullValuePropertyMappingStrategyGem.valueOf( + mapper.nullValuePropertyMappingStrategy().getDefaultValue() ); } - public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - return NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + public NullValueMappingStrategyGem getNullValueMappingStrategy() { + return NullValueMappingStrategyGem.valueOf( mapper.nullValueMappingStrategy().getDefaultValue() ); } - public BuilderPrism getBuilderPrism() { + public BuilderGem getBuilder() { // TODO: I realized this is not correct, however it needs to be null in order to keep downward compatibility // but assuming a default @Builder will make testcases fail. Not having a default means that you need to // specify this mandatory on @MappingConfig and @Mapper. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java index 1d249e7775..9062b0a031 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -12,14 +12,14 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import org.mapstruct.ap.internal.prism.BuilderPrism; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; -import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.InjectionStrategyGem; +import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; /** * Chain Of Responsibility Pattern. @@ -50,15 +50,15 @@ public Set imports() { return next.imports(); } - public ReportingPolicyPrism unmappedTargetPolicy() { + public ReportingPolicyGem unmappedTargetPolicy() { return next.unmappedTargetPolicy(); } - public ReportingPolicyPrism unmappedSourcePolicy() { + public ReportingPolicyGem unmappedSourcePolicy() { return next.unmappedSourcePolicy(); } - public ReportingPolicyPrism typeConversionPolicy() { + public ReportingPolicyGem typeConversionPolicy() { return next.typeConversionPolicy(); } @@ -66,11 +66,11 @@ public String componentModel() { return next.componentModel(); } - public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() { + public MappingInheritanceStrategyGem getMappingInheritanceStrategy() { return next.getMappingInheritanceStrategy(); } - public InjectionStrategyPrism getInjectionStrategy() { + public InjectionStrategyGem getInjectionStrategy() { return next.getInjectionStrategy(); } @@ -80,24 +80,24 @@ public Boolean isDisableSubMappingMethodsGeneration() { // BeanMapping and Mapping - public CollectionMappingStrategyPrism getCollectionMappingStrategy() { + public CollectionMappingStrategyGem getCollectionMappingStrategy() { return next.getCollectionMappingStrategy(); } - public NullValueCheckStrategyPrism getNullValueCheckStrategy() { + public NullValueCheckStrategyGem getNullValueCheckStrategy() { return next.getNullValueCheckStrategy(); } - public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { + public NullValuePropertyMappingStrategyGem getNullValuePropertyMappingStrategy() { return next.getNullValuePropertyMappingStrategy(); } - public NullValueMappingStrategyPrism getNullValueMappingStrategy() { + public NullValueMappingStrategyGem getNullValueMappingStrategy() { return next.getNullValueMappingStrategy(); } - public BuilderPrism getBuilderPrism() { - return next.getBuilderPrism(); + public BuilderGem getBuilder() { + return next.getBuilder(); } DelegatingOptions next() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java index 077883c311..70601a96c6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java @@ -5,16 +5,17 @@ */ package org.mapstruct.ap.internal.model.source; +import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.type.TypeKind; import javax.lang.model.util.Types; import org.mapstruct.ap.internal.model.common.FormattingParameters; -import org.mapstruct.ap.internal.prism.IterableMappingPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.IterableMappingGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.tools.gem.GemValue; /** * Represents an iterable mapping as configured via {@code @IterableMapping}. @@ -25,44 +26,45 @@ public class IterableMappingOptions extends DelegatingOptions { private final SelectionParameters selectionParameters; private final FormattingParameters formattingParameters; - private final IterableMappingPrism prism; + private final IterableMappingGem iterableMapping; - public static IterableMappingOptions fromPrism(IterableMappingPrism prism, - MapperOptions mappperOptions, ExecutableElement method, - FormattingMessager messager, Types typeUtils) { + public static IterableMappingOptions fromGem(IterableMappingGem iterableMapping, + MapperOptions mappperOptions, ExecutableElement method, + FormattingMessager messager, Types typeUtils) { - if ( prism == null || !isConsistent( prism, method, messager ) ) { + if ( iterableMapping == null || !isConsistent( iterableMapping, method, messager ) ) { IterableMappingOptions options = new IterableMappingOptions( null, null, null, mappperOptions ); return options; } SelectionParameters selection = new SelectionParameters( - prism.qualifiedBy(), - prism.qualifiedByName(), - TypeKind.VOID != prism.elementTargetType().getKind() ? prism.elementTargetType() : null, + iterableMapping.qualifiedBy().get(), + iterableMapping.qualifiedByName().get(), + iterableMapping.elementTargetType().getValue(), typeUtils ); FormattingParameters formatting = new FormattingParameters( - prism.dateFormat(), - prism.numberFormat(), - prism.mirror, - prism.values.dateFormat(), + iterableMapping.dateFormat().get(), + iterableMapping.numberFormat().get(), + iterableMapping.mirror(), + iterableMapping.dateFormat().getAnnotationValue(), method ); - IterableMappingOptions options = new IterableMappingOptions( formatting, selection, prism, mappperOptions ); + IterableMappingOptions options = + new IterableMappingOptions( formatting, selection, iterableMapping, mappperOptions ); return options; } - private static boolean isConsistent(IterableMappingPrism prism, ExecutableElement method, + private static boolean isConsistent(IterableMappingGem gem, ExecutableElement method, FormattingMessager messager) { - if ( prism.dateFormat().isEmpty() - && prism.numberFormat().isEmpty() - && prism.qualifiedBy().isEmpty() - && prism.qualifiedByName().isEmpty() - && TypeKind.VOID == prism.elementTargetType().getKind() - && null == prism.values.nullValueMappingStrategy() ) { + if ( !gem.dateFormat().hasValue() + && !gem.numberFormat().hasValue() + && !gem.qualifiedBy().hasValue() + && !gem.qualifiedByName().hasValue() + && !gem.elementTargetType().hasValue() + && !gem.nullValueMappingStrategy().hasValue() ) { messager.printMessage( method, Message.ITERABLEMAPPING_NO_ELEMENTS ); return false; } @@ -70,11 +72,12 @@ private static boolean isConsistent(IterableMappingPrism prism, ExecutableElemen } private IterableMappingOptions(FormattingParameters formattingParameters, SelectionParameters selectionParameters, - IterableMappingPrism prism, DelegatingOptions next ) { + IterableMappingGem iterableMapping, + DelegatingOptions next) { super( next ); this.formattingParameters = formattingParameters; this.selectionParameters = selectionParameters; - this.prism = prism; + this.iterableMapping = iterableMapping; } public SelectionParameters getSelectionParameters() { @@ -86,19 +89,21 @@ public FormattingParameters getFormattingParameters() { } public AnnotationMirror getMirror() { - return null == prism ? null : prism.mirror; + return Optional.ofNullable( iterableMapping ).map( IterableMappingGem::mirror ).orElse( null ); } @Override - public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - return null == prism || null == prism.values.nullValueMappingStrategy() ? - next().getNullValueMappingStrategy() - : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + public NullValueMappingStrategyGem getNullValueMappingStrategy() { + return Optional.ofNullable( iterableMapping ).map( IterableMappingGem::nullValueMappingStrategy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( NullValueMappingStrategyGem::valueOf ) + .orElse( next().getNullValueMappingStrategy() ); } @Override public boolean hasAnnotation() { - return prism != null; + return iterableMapping != null; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java index 83d8bec5ce..3dd3927f58 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java @@ -5,16 +5,17 @@ */ package org.mapstruct.ap.internal.model.source; +import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.type.TypeKind; import javax.lang.model.util.Types; import org.mapstruct.ap.internal.model.common.FormattingParameters; -import org.mapstruct.ap.internal.prism.MapMappingPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.MapMappingGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.tools.gem.GemValue; /** * Represents a map mapping as configured via {@code @MapMapping}. @@ -27,44 +28,50 @@ public class MapMappingOptions extends DelegatingOptions { private final SelectionParameters valueSelectionParameters; private final FormattingParameters keyFormattingParameters; private final FormattingParameters valueFormattingParameters; - private final MapMappingPrism prism; - - public static MapMappingOptions fromPrism(MapMappingPrism prism, MapperOptions mapperOptions, - ExecutableElement method, - FormattingMessager messager, Types typeUtils) { - - if ( prism == null || !isConsistent( prism, method, messager ) ) { - MapMappingOptions options = new MapMappingOptions( null, null, null, null, null, mapperOptions ); + private final MapMappingGem mapMapping; + + public static MapMappingOptions fromGem(MapMappingGem mapMapping, MapperOptions mapperOptions, + ExecutableElement method, FormattingMessager messager, Types typeUtils) { + + if ( mapMapping == null || !isConsistent( mapMapping, method, messager ) ) { + MapMappingOptions options = new MapMappingOptions( + null, + null, + null, + null, + null, + mapperOptions + ); return options; } SelectionParameters keySelection = new SelectionParameters( - prism.keyQualifiedBy(), - prism.keyQualifiedByName(), - TypeKind.VOID != prism.keyTargetType().getKind() ? prism.keyTargetType() : null, + mapMapping.keyQualifiedBy().get(), + mapMapping.keyQualifiedByName().get(), + mapMapping.keyTargetType().getValue(), typeUtils ); SelectionParameters valueSelection = new SelectionParameters( - prism.valueQualifiedBy(), - prism.valueQualifiedByName(), - TypeKind.VOID != prism.valueTargetType().getKind() ? prism.valueTargetType() : null, + mapMapping.valueQualifiedBy().get(), + mapMapping.valueQualifiedByName().get(), + mapMapping.valueTargetType().getValue(), typeUtils ); FormattingParameters keyFormatting = new FormattingParameters( - prism.keyDateFormat(), - prism.keyNumberFormat(), - prism.mirror, - prism.values.keyDateFormat(), + mapMapping.keyDateFormat().get(), + mapMapping.keyNumberFormat().get(), + mapMapping.mirror(), + mapMapping.keyDateFormat().getAnnotationValue(), method ); FormattingParameters valueFormatting = new FormattingParameters( - prism.valueDateFormat(), - prism.valueNumberFormat(), - prism.mirror, - prism.values.valueDateFormat(), + mapMapping.valueDateFormat().get(), + mapMapping.valueNumberFormat().get(), + mapMapping.mirror(), + mapMapping.valueDateFormat().getAnnotationValue(), method ); @@ -73,24 +80,25 @@ public static MapMappingOptions fromPrism(MapMappingPrism prism, MapperOptions m keySelection, valueFormatting, valueSelection, - prism, + mapMapping, mapperOptions ); return options; } - private static boolean isConsistent(MapMappingPrism prism, ExecutableElement method, FormattingMessager messager) { - if ( prism.keyDateFormat().isEmpty() - && prism.keyNumberFormat().isEmpty() - && prism.keyQualifiedBy().isEmpty() - && prism.keyQualifiedByName().isEmpty() - && prism.valueDateFormat().isEmpty() - && prism.valueNumberFormat().isEmpty() - && prism.valueQualifiedBy().isEmpty() - && prism.valueQualifiedByName().isEmpty() - && TypeKind.VOID == prism.keyTargetType().getKind() - && TypeKind.VOID == prism.valueTargetType().getKind() - && null == prism.values.nullValueMappingStrategy() ) { + private static boolean isConsistent(MapMappingGem gem, ExecutableElement method, + FormattingMessager messager) { + if ( !gem.keyDateFormat().hasValue() + && !gem.keyNumberFormat().hasValue() + && !gem.keyQualifiedBy().hasValue() + && !gem.keyQualifiedByName().hasValue() + && !gem.valueDateFormat().hasValue() + && !gem.valueNumberFormat().hasValue() + && !gem.valueQualifiedBy().hasValue() + && !gem.valueQualifiedByName().hasValue() + && !gem.keyTargetType().hasValue() + && !gem.valueTargetType().hasValue() + && !gem.nullValueMappingStrategy().hasValue() ) { messager.printMessage( method, Message.MAPMAPPING_NO_ELEMENTS ); return false; } @@ -99,13 +107,13 @@ private static boolean isConsistent(MapMappingPrism prism, ExecutableElement met private MapMappingOptions(FormattingParameters keyFormatting, SelectionParameters keySelectionParameters, FormattingParameters valueFormatting, SelectionParameters valueSelectionParameters, - MapMappingPrism prism, DelegatingOptions next ) { + MapMappingGem mapMapping, DelegatingOptions next ) { super( next ); this.keyFormattingParameters = keyFormatting; this.keySelectionParameters = keySelectionParameters; this.valueFormattingParameters = valueFormatting; this.valueSelectionParameters = valueSelectionParameters; - this.prism = prism; + this.mapMapping = mapMapping; } public FormattingParameters getKeyFormattingParameters() { @@ -125,19 +133,21 @@ public SelectionParameters getValueSelectionParameters() { } public AnnotationMirror getMirror() { - return null == prism ? null : prism.mirror; + return Optional.ofNullable( mapMapping ).map( MapMappingGem::mirror ).orElse( null ); } @Override - public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - return null == prism || null == prism.values.nullValueMappingStrategy() ? - next().getNullValueMappingStrategy() - : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + public NullValueMappingStrategyGem getNullValueMappingStrategy() { + return Optional.ofNullable( mapMapping ).map( MapMappingGem::nullValueMappingStrategy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( NullValueMappingStrategyGem::valueOf ) + .orElse( next().getNullValueMappingStrategy() ); } @Override public boolean hasAnnotation() { - return prism != null; + return mapMapping != null; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java index b83bbd2f90..5a3a0e1ade 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -8,124 +8,130 @@ import java.util.Set; import javax.lang.model.type.DeclaredType; -import org.mapstruct.ap.internal.prism.BuilderPrism; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; -import org.mapstruct.ap.internal.prism.MapperConfigPrism; -import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.InjectionStrategyGem; +import org.mapstruct.ap.internal.gem.MapperConfigGem; +import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; public class MapperConfigOptions extends DelegatingOptions { - private final MapperConfigPrism prism; + private final MapperConfigGem mapperConfig; - MapperConfigOptions(MapperConfigPrism prism, DelegatingOptions next ) { + MapperConfigOptions(MapperConfigGem mapperConfig, DelegatingOptions next ) { super( next ); - this.prism = prism; + this.mapperConfig = mapperConfig; } @Override public String implementationName() { - return null == prism.values.implementationName() ? next().implementationName() : - prism.implementationName(); + return mapperConfig.implementationName().hasValue() ? mapperConfig.implementationName().get() : + next().implementationName(); } @Override public String implementationPackage() { - return null == prism.values.implementationPackage() ? next().implementationPackage() : - prism.implementationPackage(); + return mapperConfig.implementationPackage().hasValue() ? mapperConfig.implementationPackage().get() : + next().implementationPackage(); } @Override public Set uses() { - return toDeclaredTypes( prism.uses(), next().uses() ); + return toDeclaredTypes( mapperConfig.uses().get(), next().uses() ); } @Override public Set imports() { - return toDeclaredTypes( prism.imports(), next().imports() ); + return toDeclaredTypes( mapperConfig.imports().get(), next().imports() ); } @Override - public ReportingPolicyPrism unmappedTargetPolicy() { - return null == prism.values.unmappedTargetPolicy() ? next().unmappedTargetPolicy() : - ReportingPolicyPrism.valueOf( prism.unmappedTargetPolicy() ); + public ReportingPolicyGem unmappedTargetPolicy() { + return mapperConfig.unmappedTargetPolicy().hasValue() ? + ReportingPolicyGem.valueOf( mapperConfig.unmappedTargetPolicy().get() ) : next().unmappedTargetPolicy(); + } @Override - public ReportingPolicyPrism unmappedSourcePolicy() { - return null == prism.values.unmappedSourcePolicy() ? next().unmappedSourcePolicy() : - ReportingPolicyPrism.valueOf( prism.unmappedSourcePolicy() ); + public ReportingPolicyGem unmappedSourcePolicy() { + return mapperConfig.unmappedSourcePolicy().hasValue() ? + ReportingPolicyGem.valueOf( mapperConfig.unmappedSourcePolicy().get() ) : next().unmappedSourcePolicy(); } @Override - public ReportingPolicyPrism typeConversionPolicy() { - return null == prism.values.typeConversionPolicy() ? next().typeConversionPolicy() : - ReportingPolicyPrism.valueOf( prism.typeConversionPolicy() ); + public ReportingPolicyGem typeConversionPolicy() { + return mapperConfig.typeConversionPolicy().hasValue() ? + ReportingPolicyGem.valueOf( mapperConfig.typeConversionPolicy().get() ) : next().typeConversionPolicy(); } @Override public String componentModel() { - return null == prism.values.componentModel() ? next().componentModel() : prism.componentModel(); + return mapperConfig.componentModel().hasValue() ? mapperConfig.componentModel().get() : next().componentModel(); } @Override - public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() { - return null == prism.values.mappingInheritanceStrategy() ? next().getMappingInheritanceStrategy() : - MappingInheritanceStrategyPrism.valueOf( prism.mappingInheritanceStrategy() ); + public MappingInheritanceStrategyGem getMappingInheritanceStrategy() { + return mapperConfig.mappingInheritanceStrategy().hasValue() ? + MappingInheritanceStrategyGem.valueOf( mapperConfig.mappingInheritanceStrategy().get() ) : + next().getMappingInheritanceStrategy(); } @Override - public InjectionStrategyPrism getInjectionStrategy() { - return null == prism.values.injectionStrategy() ? next().getInjectionStrategy() : - InjectionStrategyPrism.valueOf( prism.injectionStrategy() ); + public InjectionStrategyGem getInjectionStrategy() { + return mapperConfig.injectionStrategy().hasValue() ? + InjectionStrategyGem.valueOf( mapperConfig.injectionStrategy().get() ) : + next().getInjectionStrategy(); } @Override public Boolean isDisableSubMappingMethodsGeneration() { - return null == prism.values.disableSubMappingMethodsGeneration() ? - next().isDisableSubMappingMethodsGeneration() : prism.disableSubMappingMethodsGeneration(); + return mapperConfig.disableSubMappingMethodsGeneration().hasValue() ? + mapperConfig.disableSubMappingMethodsGeneration().get() : + next().isDisableSubMappingMethodsGeneration(); } + // @Mapping, @BeanMapping + @Override - public CollectionMappingStrategyPrism getCollectionMappingStrategy() { - return null == prism.values.collectionMappingStrategy() ? - next().getCollectionMappingStrategy() - : CollectionMappingStrategyPrism.valueOf( prism.collectionMappingStrategy() ); + public CollectionMappingStrategyGem getCollectionMappingStrategy() { + return mapperConfig.collectionMappingStrategy().hasValue() ? + CollectionMappingStrategyGem.valueOf( mapperConfig.collectionMappingStrategy().get() ) : + next().getCollectionMappingStrategy(); } @Override - public NullValueCheckStrategyPrism getNullValueCheckStrategy() { - return null == prism.values.nullValueCheckStrategy() ? - next().getNullValueCheckStrategy() - : NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); + public NullValueCheckStrategyGem getNullValueCheckStrategy() { + return mapperConfig.nullValueCheckStrategy().hasValue() ? + NullValueCheckStrategyGem.valueOf( mapperConfig.nullValueCheckStrategy().get() ) : + next().getNullValueCheckStrategy(); } @Override - public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { - return null == prism.values.nullValuePropertyMappingStrategy() ? - next().getNullValuePropertyMappingStrategy() - : NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); + public NullValuePropertyMappingStrategyGem getNullValuePropertyMappingStrategy() { + return mapperConfig.nullValuePropertyMappingStrategy().hasValue() ? + NullValuePropertyMappingStrategyGem.valueOf( mapperConfig.nullValuePropertyMappingStrategy().get() ) : + next().getNullValuePropertyMappingStrategy(); } @Override - public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - return null == prism.values.nullValueMappingStrategy() ? - next().getNullValueMappingStrategy() - : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + public NullValueMappingStrategyGem getNullValueMappingStrategy() { + return mapperConfig.nullValueMappingStrategy().hasValue() ? + NullValueMappingStrategyGem.valueOf( mapperConfig.nullValueMappingStrategy().get() ) : + next().getNullValueMappingStrategy(); } @Override - public BuilderPrism getBuilderPrism() { - return null == prism.values.builder() ? next().getBuilderPrism() : prism.builder(); + public BuilderGem getBuilder() { + return mapperConfig.builder().hasValue() ? mapperConfig.builder().get() : next().getBuilder(); } @Override public boolean hasAnnotation() { - return prism != null; + return mapperConfig != null; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index b1397154d1..9472694067 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -13,146 +13,150 @@ import javax.lang.model.type.TypeKind; import org.mapstruct.ap.internal.option.Options; -import org.mapstruct.ap.internal.prism.BuilderPrism; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; -import org.mapstruct.ap.internal.prism.MapperConfigPrism; -import org.mapstruct.ap.internal.prism.MapperPrism; -import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.InjectionStrategyGem; +import org.mapstruct.ap.internal.gem.MapperConfigGem; +import org.mapstruct.ap.internal.gem.MapperGem; +import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; public class MapperOptions extends DelegatingOptions { - private final MapperPrism prism; + private final MapperGem mapper; private final DeclaredType mapperConfigType; public static MapperOptions getInstanceOn(TypeElement typeElement, Options options) { - MapperPrism prism = MapperPrism.getInstanceOn( typeElement ); + MapperGem mapper = MapperGem.instanceOn( typeElement ); MapperOptions mapperAnnotation; - DelegatingOptions defaults = new DefaultOptions( prism, options ); + DelegatingOptions defaults = new DefaultOptions( mapper, options ); DeclaredType mapperConfigType; - if ( prism.values.config() != null && prism.config().getKind() == TypeKind.DECLARED ) { - mapperConfigType = (DeclaredType) prism.config(); + if ( mapper.config().hasValue() && mapper.config().getValue().getKind() == TypeKind.DECLARED ) { + mapperConfigType = (DeclaredType) mapper.config().get(); } else { mapperConfigType = null; } if ( mapperConfigType != null ) { Element mapperConfigElement = mapperConfigType.asElement(); - MapperConfigPrism configPrism = MapperConfigPrism.getInstanceOn( mapperConfigElement ); - MapperConfigOptions mapperConfigAnnotation = new MapperConfigOptions( configPrism, defaults ); - mapperAnnotation = new MapperOptions( prism, mapperConfigType, mapperConfigAnnotation ); + MapperConfigGem mapperConfig = MapperConfigGem.instanceOn( mapperConfigElement ); + MapperConfigOptions mapperConfigAnnotation = new MapperConfigOptions( mapperConfig, defaults ); + mapperAnnotation = new MapperOptions( mapper, mapperConfigType, mapperConfigAnnotation ); } else { - mapperAnnotation = new MapperOptions( prism, null, defaults ); + mapperAnnotation = new MapperOptions( mapper, null, defaults ); } return mapperAnnotation; } - private MapperOptions(MapperPrism prism, DeclaredType mapperConfigType, DelegatingOptions next) { + private MapperOptions(MapperGem mapper, DeclaredType mapperConfigType, DelegatingOptions next) { super( next ); - this.prism = prism; + this.mapper = mapper; this.mapperConfigType = mapperConfigType; } @Override public String implementationName() { - return null == prism.values.implementationName() ? next().implementationName() : prism.implementationName(); + return mapper.implementationName().hasValue() ? mapper.implementationName().get() : next().implementationName(); } @Override public String implementationPackage() { - return null == prism.values.implementationPackage() ? next().implementationPackage() : - prism.implementationPackage(); + return mapper.implementationPackage().hasValue() ? mapper.implementationPackage().get() : + next().implementationPackage(); } @Override public Set uses() { - return toDeclaredTypes( prism.uses(), next().uses() ); + return toDeclaredTypes( mapper.uses().get(), next().uses() ); } @Override public Set imports() { - return toDeclaredTypes( prism.imports(), next().imports() ); + return toDeclaredTypes( mapper.imports().get(), next().imports() ); } @Override - public ReportingPolicyPrism unmappedTargetPolicy() { - return null == prism.values.unmappedTargetPolicy() ? next().unmappedTargetPolicy() : - ReportingPolicyPrism.valueOf( prism.unmappedTargetPolicy() ); + public ReportingPolicyGem unmappedTargetPolicy() { + return mapper.unmappedTargetPolicy().hasValue() ? + ReportingPolicyGem.valueOf( mapper.unmappedTargetPolicy().get() ) : next().unmappedTargetPolicy(); + } @Override - public ReportingPolicyPrism unmappedSourcePolicy() { - return null == prism.values.unmappedSourcePolicy() ? next().unmappedSourcePolicy() : - ReportingPolicyPrism.valueOf( prism.unmappedSourcePolicy() ); + public ReportingPolicyGem unmappedSourcePolicy() { + return mapper.unmappedSourcePolicy().hasValue() ? + ReportingPolicyGem.valueOf( mapper.unmappedSourcePolicy().get() ) : next().unmappedSourcePolicy(); } @Override - public ReportingPolicyPrism typeConversionPolicy() { - return null == prism.values.typeConversionPolicy() ? next().typeConversionPolicy() : - ReportingPolicyPrism.valueOf( prism.typeConversionPolicy() ); + public ReportingPolicyGem typeConversionPolicy() { + return mapper.typeConversionPolicy().hasValue() ? + ReportingPolicyGem.valueOf( mapper.typeConversionPolicy().get() ) : next().typeConversionPolicy(); } @Override public String componentModel() { - return null == prism.values.componentModel() ? next().componentModel() : prism.componentModel(); + return mapper.componentModel().hasValue() ? mapper.componentModel().get() : next().componentModel(); } @Override - public MappingInheritanceStrategyPrism getMappingInheritanceStrategy() { - return null == prism.values.mappingInheritanceStrategy() ? next().getMappingInheritanceStrategy() : - MappingInheritanceStrategyPrism.valueOf( prism.mappingInheritanceStrategy() ); + public MappingInheritanceStrategyGem getMappingInheritanceStrategy() { + return mapper.mappingInheritanceStrategy().hasValue() ? + MappingInheritanceStrategyGem.valueOf( mapper.mappingInheritanceStrategy().get() ) : + next().getMappingInheritanceStrategy(); } @Override - public InjectionStrategyPrism getInjectionStrategy() { - return null == prism.values.injectionStrategy() ? next().getInjectionStrategy() : - InjectionStrategyPrism.valueOf( prism.injectionStrategy() ); + public InjectionStrategyGem getInjectionStrategy() { + return mapper.injectionStrategy().hasValue() ? + InjectionStrategyGem.valueOf( mapper.injectionStrategy().get() ) : + next().getInjectionStrategy(); } @Override public Boolean isDisableSubMappingMethodsGeneration() { - return null == prism.values.disableSubMappingMethodsGeneration() ? - next().isDisableSubMappingMethodsGeneration() : prism.disableSubMappingMethodsGeneration(); + return mapper.disableSubMappingMethodsGeneration().hasValue() ? + mapper.disableSubMappingMethodsGeneration().get() : + next().isDisableSubMappingMethodsGeneration(); } // @Mapping, @BeanMapping @Override - public CollectionMappingStrategyPrism getCollectionMappingStrategy() { - return null == prism.values.collectionMappingStrategy() ? - next().getCollectionMappingStrategy() - : CollectionMappingStrategyPrism.valueOf( prism.collectionMappingStrategy() ); + public CollectionMappingStrategyGem getCollectionMappingStrategy() { + return mapper.collectionMappingStrategy().hasValue() ? + CollectionMappingStrategyGem.valueOf( mapper.collectionMappingStrategy().get() ) : + next().getCollectionMappingStrategy(); } @Override - public NullValueCheckStrategyPrism getNullValueCheckStrategy() { - return null == prism.values.nullValueCheckStrategy() ? - next().getNullValueCheckStrategy() - : NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); + public NullValueCheckStrategyGem getNullValueCheckStrategy() { + return mapper.nullValueCheckStrategy().hasValue() ? + NullValueCheckStrategyGem.valueOf( mapper.nullValueCheckStrategy().get() ) : + next().getNullValueCheckStrategy(); } @Override - public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { - return null == prism.values.nullValuePropertyMappingStrategy() ? - next().getNullValuePropertyMappingStrategy() - : NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); + public NullValuePropertyMappingStrategyGem getNullValuePropertyMappingStrategy() { + return mapper.nullValuePropertyMappingStrategy().hasValue() ? + NullValuePropertyMappingStrategyGem.valueOf( mapper.nullValuePropertyMappingStrategy().get() ) : + next().getNullValuePropertyMappingStrategy(); } @Override - public NullValueMappingStrategyPrism getNullValueMappingStrategy() { - return null == prism.values.nullValueMappingStrategy() ? - next().getNullValueMappingStrategy() - : NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() ); + public NullValueMappingStrategyGem getNullValueMappingStrategy() { + return mapper.nullValueMappingStrategy().hasValue() ? + NullValueMappingStrategyGem.valueOf( mapper.nullValueMappingStrategy().get() ) : + next().getNullValueMappingStrategy(); } @Override - public BuilderPrism getBuilderPrism() { - return null == prism.values.builder() ? next().getBuilderPrism() : prism.builder(); + public BuilderGem getBuilder() { + return mapper.builder().hasValue() ? mapper.builder().get() : next().getBuilder(); } // @Mapper specific @@ -166,11 +170,11 @@ public boolean hasMapperConfig() { } public boolean isValid() { - return prism.isValid; + return mapper.isValid(); } public AnnotationMirror getAnnotationMirror() { - return prism.mirror; + return mapper.mirror(); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 338f22db90..587dc5acf4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -14,7 +14,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.util.accessor.Accessor; import static org.mapstruct.ap.internal.model.source.MappingOptions.getMappingTargetNamesBy; @@ -185,12 +185,12 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } public void applyIgnoreAll(SourceMethod method, TypeFactory typeFactory ) { - CollectionMappingStrategyPrism cms = method.getOptions().getMapper().getCollectionMappingStrategy(); + CollectionMappingStrategyGem cms = method.getOptions().getMapper().getCollectionMappingStrategy(); Type writeType = method.getResultType(); if ( !method.isUpdateMethod() ) { writeType = typeFactory.effectiveResultTypeFor( writeType, - method.getOptions().getBeanMapping().getBuilderPrism() + method.getOptions().getBeanMapping().getBuilder() ); } Map writeAccessors = writeType.getPropertyWriteAccessors( cms ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 469398c829..1557d68920 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.regex.Matcher; @@ -19,12 +20,13 @@ import javax.lang.model.util.Types; import org.mapstruct.ap.internal.model.common.FormattingParameters; -import org.mapstruct.ap.internal.prism.MappingPrism; -import org.mapstruct.ap.internal.prism.MappingsPrism; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.MappingGem; +import org.mapstruct.ap.internal.gem.MappingsGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.tools.gem.GemValue; /** * Represents a property mapping as configured via {@code @Mapping} (no intermediate state). @@ -49,7 +51,7 @@ public class MappingOptions extends DelegatingOptions { private final AnnotationValue sourceAnnotationValue; private final AnnotationValue targetAnnotationValue; - private final MappingPrism prism; + private final MappingGem mapping; private final InheritContext inheritContext; @@ -86,70 +88,70 @@ public static Set getMappingTargetNamesBy(Predicate pred .collect( Collectors.toCollection( LinkedHashSet::new ) ); } - public static void addInstances(MappingsPrism prism, ExecutableElement method, + public static void addInstances(MappingsGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, FormattingMessager messager, Types typeUtils, Set mappings) { - for ( MappingPrism mappingPrism : prism.value() ) { - addInstance( mappingPrism, method, beanMappingOptions, messager, typeUtils, mappings ); + for ( MappingGem mapping : gem.value().getValue() ) { + addInstance( mapping, method, beanMappingOptions, messager, typeUtils, mappings ); } } - public static void addInstance(MappingPrism prism, ExecutableElement method, BeanMappingOptions beanMappingOptions, - FormattingMessager messager, Types typeUtils, Set mappings) { + public static void addInstance(MappingGem mapping, ExecutableElement method, + BeanMappingOptions beanMappingOptions, FormattingMessager messager, Types typeUtils, + Set mappings) { - if ( !isConsistent( prism, method, messager ) ) { + if ( !isConsistent( mapping, method, messager ) ) { return; } - String source = prism.source().isEmpty() ? null : prism.source(); - String constant = prism.values.constant() == null ? null : prism.constant(); - String expression = getExpression( prism, method, messager ); - String defaultExpression = getDefaultExpression( prism, method, messager ); - String dateFormat = prism.values.dateFormat() == null ? null : prism.dateFormat(); - String numberFormat = prism.values.numberFormat() == null ? null : prism.numberFormat(); - String defaultValue = prism.values.defaultValue() == null ? null : prism.defaultValue(); + String source = mapping.source().getValue(); + String constant = mapping.constant().getValue(); + String expression = getExpression( mapping, method, messager ); + String defaultExpression = getDefaultExpression( mapping, method, messager ); + String dateFormat = mapping.dateFormat().getValue(); + String numberFormat = mapping.numberFormat().getValue(); + String defaultValue = mapping.defaultValue().getValue(); - boolean resultTypeIsDefined = prism.values.resultType() != null; - Set dependsOn = prism.dependsOn() != null ? - new LinkedHashSet( prism.dependsOn() ) : + Set dependsOn = mapping.dependsOn().hasValue() ? + new LinkedHashSet( mapping.dependsOn().getValue() ) : Collections.emptySet(); FormattingParameters formattingParam = new FormattingParameters( dateFormat, numberFormat, - prism.mirror, - prism.values.dateFormat(), + mapping.mirror(), + mapping.dateFormat().getAnnotationValue(), method ); SelectionParameters selectionParams = new SelectionParameters( - prism.qualifiedBy(), - prism.qualifiedByName(), - resultTypeIsDefined ? prism.resultType() : null, + mapping.qualifiedBy().get(), + mapping.qualifiedByName().get(), + mapping.resultType().getValue(), typeUtils ); MappingOptions options = new MappingOptions( - prism.target(), - prism.values.target(), + mapping.target().getValue(), + mapping.target().getAnnotationValue(), source, - prism.values.source(), + mapping.source().getAnnotationValue(), constant, expression, defaultExpression, defaultValue, - prism.ignore(), + mapping.ignore().get(), formattingParam, selectionParams, dependsOn, - prism, + mapping, null, beanMappingOptions ); if ( mappings.contains( options ) ) { - messager.printMessage( method, Message.PROPERTYMAPPING_DUPLICATE_TARGETS, prism.target() ); + messager.printMessage( method, Message.PROPERTYMAPPING_DUPLICATE_TARGETS, mapping.target().get() ); } else { mappings.add( options ); @@ -176,66 +178,62 @@ public static MappingOptions forIgnore(String targetName) { ); } - private static boolean isConsistent(MappingPrism prism, ExecutableElement method, + private static boolean isConsistent(MappingGem gem, ExecutableElement method, FormattingMessager messager) { - if ( prism.target().isEmpty() ) { + if ( !gem.target().hasValue() ) { messager.printMessage( method, - prism.mirror, - prism.values.target(), + gem.mirror(), + gem.target().getAnnotationValue(), Message.PROPERTYMAPPING_EMPTY_TARGET ); return false; } Message message = null; - if ( !prism.source().isEmpty() && prism.values.constant() != null ) { + if ( gem.source().hasValue() && gem.constant().hasValue() ) { message = Message.PROPERTYMAPPING_SOURCE_AND_CONSTANT_BOTH_DEFINED; } - else if ( !prism.source().isEmpty() && prism.values.expression() != null ) { + else if ( gem.source().hasValue() && gem.expression().hasValue() ) { message = Message.PROPERTYMAPPING_SOURCE_AND_EXPRESSION_BOTH_DEFINED; } - else if ( prism.values.expression() != null && prism.values.constant() != null ) { + else if (gem.expression().hasValue() && gem.constant().hasValue() ) { message = Message.PROPERTYMAPPING_EXPRESSION_AND_CONSTANT_BOTH_DEFINED; } - else if ( prism.values.expression() != null && prism.values.defaultValue() != null ) { + else if ( gem.expression().hasValue() && gem.defaultValue().hasValue() ) { message = Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_VALUE_BOTH_DEFINED; } - else if ( prism.values.constant() != null && prism.values.defaultValue() != null ) { + else if ( gem.constant().hasValue() && gem.defaultValue().hasValue() ) { message = Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_VALUE_BOTH_DEFINED; } - else if ( prism.values.expression() != null && prism.values.defaultExpression() != null ) { + else if ( gem.expression().hasValue() && gem.defaultExpression().hasValue() ) { message = Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } - else if ( prism.values.constant() != null && prism.values.defaultExpression() != null ) { + else if ( gem.constant().hasValue() && gem.defaultExpression().hasValue() ) { message = Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } - else if ( prism.values.defaultValue() != null && prism.values.defaultExpression() != null ) { + else if ( gem.defaultValue().hasValue() && gem.defaultExpression().hasValue() ) { message = Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } - else if ( prism.values.expression() != null - && ( prism.values.qualifiedByName() != null || prism.values.qualifiedBy() != null ) ) { + else if ( gem.expression().hasValue() + && ( gem.qualifiedByName().hasValue() || gem.qualifiedBy().hasValue() ) ) { message = Message.PROPERTYMAPPING_EXPRESSION_AND_QUALIFIER_BOTH_DEFINED; } - else if ( prism.values.nullValuePropertyMappingStrategy() != null - && prism.values.defaultValue() != null ) { + else if ( gem.nullValuePropertyMappingStrategy().hasValue() && gem.defaultValue().hasValue() ) { message = Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_NVPMS; } - else if ( prism.values.nullValuePropertyMappingStrategy() != null - && prism.values.constant() != null ) { + else if ( gem.nullValuePropertyMappingStrategy().hasValue() && gem.constant().hasValue() ) { message = Message.PROPERTYMAPPING_CONSTANT_VALUE_AND_NVPMS; } - else if ( prism.values.nullValuePropertyMappingStrategy() != null - && prism.values.expression() != null ) { + else if ( gem.nullValuePropertyMappingStrategy().hasValue() && gem.expression().hasValue() ) { message = Message.PROPERTYMAPPING_EXPRESSION_VALUE_AND_NVPMS; } - else if ( prism.values.nullValuePropertyMappingStrategy() != null - && prism.values.defaultExpression() != null ) { + else if ( gem.nullValuePropertyMappingStrategy().hasValue() && gem.defaultExpression().hasValue() ) { message = Message.PROPERTYMAPPING_DEFAULT_EXPERSSION_AND_NVPMS; } - else if ( prism.values.nullValuePropertyMappingStrategy() != null - && prism.ignore() != null && prism.ignore() ) { + else if ( gem.nullValuePropertyMappingStrategy().hasValue() + && gem.ignore().hasValue() && gem.ignore().getValue() ) { message = Message.PROPERTYMAPPING_IGNORE_AND_NVPMS; } @@ -243,7 +241,7 @@ else if ( prism.values.nullValuePropertyMappingStrategy() != null return true; } else { - messager.printMessage( method, prism.mirror, message ); + messager.printMessage( method, gem.mirror(), message ); return false; } } @@ -261,7 +259,7 @@ private MappingOptions(String targetName, FormattingParameters formattingParameters, SelectionParameters selectionParameters, Set dependsOn, - MappingPrism prism, + MappingGem mapping, InheritContext inheritContext, DelegatingOptions next ) { @@ -278,21 +276,23 @@ private MappingOptions(String targetName, this.formattingParameters = formattingParameters; this.selectionParameters = selectionParameters; this.dependsOn = dependsOn; - this.prism = prism; + this.mapping = mapping; this.inheritContext = inheritContext; } - private static String getExpression(MappingPrism mappingPrism, ExecutableElement element, + private static String getExpression(MappingGem mapping, ExecutableElement element, FormattingMessager messager) { - if ( mappingPrism.expression().isEmpty() ) { + if ( !mapping.expression().hasValue() ) { return null; } - Matcher javaExpressionMatcher = JAVA_EXPRESSION.matcher( mappingPrism.expression() ); + Matcher javaExpressionMatcher = JAVA_EXPRESSION.matcher( mapping.expression().get() ); if ( !javaExpressionMatcher.matches() ) { messager.printMessage( - element, mappingPrism.mirror, mappingPrism.values.expression(), + element, + mapping.mirror(), + mapping.expression().getAnnotationValue(), Message.PROPERTYMAPPING_INVALID_EXPRESSION ); return null; @@ -301,17 +301,19 @@ private static String getExpression(MappingPrism mappingPrism, ExecutableElement return javaExpressionMatcher.group( 1 ).trim(); } - private static String getDefaultExpression(MappingPrism mappingPrism, ExecutableElement element, + private static String getDefaultExpression(MappingGem mapping, ExecutableElement element, FormattingMessager messager) { - if ( mappingPrism.defaultExpression().isEmpty() ) { + if ( !mapping.defaultExpression().hasValue() ) { return null; } - Matcher javaExpressionMatcher = JAVA_EXPRESSION.matcher( mappingPrism.defaultExpression() ); + Matcher javaExpressionMatcher = JAVA_EXPRESSION.matcher( mapping.defaultExpression().get() ); if ( !javaExpressionMatcher.matches() ) { messager.printMessage( - element, mappingPrism.mirror, mappingPrism.values.defaultExpression(), + element, + mapping.mirror(), + mapping.defaultExpression().getAnnotationValue(), Message.PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION ); return null; @@ -371,11 +373,14 @@ public boolean isIgnored() { } public AnnotationMirror getMirror() { - return prism == null ? null : prism.mirror; + return Optional.ofNullable( mapping ).map( MappingGem::mirror ).orElse( null ); } public AnnotationValue getDependsOnAnnotationValue() { - return prism == null ? null : prism.values.dependsOn(); + return Optional.ofNullable( mapping ) + .map( MappingGem::dependsOn ) + .map( GemValue::getAnnotationValue ) + .orElse( null ); } public Set getDependsOn() { @@ -387,17 +392,21 @@ public InheritContext getInheritContext() { } @Override - public NullValueCheckStrategyPrism getNullValueCheckStrategy() { - return null == prism || null == prism.values.nullValueCheckStrategy() ? - next().getNullValueCheckStrategy() - : NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() ); + public NullValueCheckStrategyGem getNullValueCheckStrategy() { + return Optional.ofNullable( mapping ).map( MappingGem::nullValueCheckStrategy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( NullValueCheckStrategyGem::valueOf ) + .orElse( next().getNullValueCheckStrategy() ); } @Override - public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() { - return null == prism || null == prism.values.nullValuePropertyMappingStrategy() ? - next().getNullValuePropertyMappingStrategy() - : NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() ); + public NullValuePropertyMappingStrategyGem getNullValuePropertyMappingStrategy() { + return Optional.ofNullable( mapping ).map( MappingGem::nullValuePropertyMappingStrategy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( NullValuePropertyMappingStrategyGem::valueOf ) + .orElse( next().getNullValuePropertyMappingStrategy() ); } /** @@ -426,7 +435,7 @@ public MappingOptions copyForInverseInheritance(SourceMethod templateMethod, formattingParameters, selectionParameters, Collections.emptySet(), - prism, + mapping, new InheritContext( true, false, templateMethod ), beanMappingOptions ); @@ -454,7 +463,7 @@ public MappingOptions copyForForwardInheritance(SourceMethod templateMethod, formattingParameters, selectionParameters, dependsOn, - prism, + mapping, new InheritContext( false, true, templateMethod ), beanMappingOptions ); @@ -492,7 +501,7 @@ public String toString() { @Override public boolean hasAnnotation() { - return prism != null; + return mapping != null; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 92c9b94f0a..3c1a5e1789 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -18,7 +18,7 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.prism.ObjectFactoryPrism; +import org.mapstruct.ap.internal.gem.ObjectFactoryGem; import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.Strings; @@ -195,7 +195,7 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters ); this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters ); - this.hasObjectFactoryAnnotation = ObjectFactoryPrism.getInstanceOn( executable ) != null; + this.hasObjectFactoryAnnotation = ObjectFactoryGem.instanceOn( executable ) != null; this.isObjectFactory = determineIfIsObjectFactory(); this.typeUtils = builder.typeUtils; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java index 8d346af4b7..10aef4bfa8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java @@ -11,13 +11,13 @@ import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; -import org.mapstruct.ap.internal.prism.ValueMappingPrism; -import org.mapstruct.ap.internal.prism.ValueMappingsPrism; +import org.mapstruct.ap.internal.gem.ValueMappingGem; +import org.mapstruct.ap.internal.gem.ValueMappingsGem; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; -import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_REMAINING; -import static org.mapstruct.ap.internal.prism.MappingConstantsPrism.ANY_UNMAPPED; +import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_REMAINING; +import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_UNMAPPED; /** * Represents the mapping between one value constant and another. @@ -32,12 +32,12 @@ public class ValueMappingOptions { private final AnnotationValue sourceAnnotationValue; private final AnnotationValue targetAnnotationValue; - public static void fromMappingsPrism(ValueMappingsPrism mappingsAnnotation, ExecutableElement method, - FormattingMessager messager, List mappings) { + public static void fromMappingsGem(ValueMappingsGem mappingsGem, ExecutableElement method, + FormattingMessager messager, List mappings) { boolean anyFound = false; - for ( ValueMappingPrism mappingPrism : mappingsAnnotation.value() ) { - ValueMappingOptions mapping = fromMappingPrism( mappingPrism ); + for ( ValueMappingGem mappingGem : mappingsGem.value().get() ) { + ValueMappingOptions mapping = fromMappingGem( mappingGem ); if ( mapping != null ) { if ( !mappings.contains( mapping ) ) { @@ -46,10 +46,10 @@ public static void fromMappingsPrism(ValueMappingsPrism mappingsAnnotation, Exec else { messager.printMessage( method, - mappingPrism.mirror, - mappingPrism.values.target(), + mappingGem.mirror(), + mappingGem.target().getAnnotationValue(), Message.VALUEMAPPING_DUPLICATE_SOURCE, - mappingPrism.source() + mappingGem.source().get() ); } if ( ANY_REMAINING.equals( mapping.source ) @@ -57,10 +57,10 @@ public static void fromMappingsPrism(ValueMappingsPrism mappingsAnnotation, Exec if ( anyFound ) { messager.printMessage( method, - mappingPrism.mirror, - mappingPrism.values.target(), + mappingGem.mirror(), + mappingGem.target().getAnnotationValue(), Message.VALUEMAPPING_ANY_AREADY_DEFINED, - mappingPrism.source() + mappingGem.source().get() ); } anyFound = true; @@ -69,10 +69,10 @@ public static void fromMappingsPrism(ValueMappingsPrism mappingsAnnotation, Exec } } - public static ValueMappingOptions fromMappingPrism(ValueMappingPrism mappingPrism ) { + public static ValueMappingOptions fromMappingGem(ValueMappingGem mapping ) { - return new ValueMappingOptions( mappingPrism.source(), mappingPrism.target(), mappingPrism.mirror, - mappingPrism.values.source(), mappingPrism.values.target() ); + return new ValueMappingOptions( mapping.source().get(), mapping.target().get(), mapping.mirror(), + mapping.source().getAnnotationValue(), mapping.target().getAnnotationValue() ); } private ValueMappingOptions(String source, String target, AnnotationMirror mirror, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java index 5c24e423ed..27ad9eb60d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java @@ -9,7 +9,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; - import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; @@ -19,8 +18,8 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SourceMethod; -import org.mapstruct.ap.internal.prism.NamedPrism; -import org.mapstruct.ap.internal.prism.QualifierPrism; +import org.mapstruct.ap.internal.gem.NamedGem; +import org.mapstruct.ap.internal.gem.QualifierGem; /** * This selector selects a best match based on qualifier annotations. @@ -117,8 +116,8 @@ public List> getMatchingMethods(Method mapp // Match! we have an annotation which has the @Qualifer marker ( could be @Named as well ) if ( typeUtils.isSameType( qualifierAnnotationType, namedAnnotationTypeMirror ) ) { // Match! its an @Named, so do the additional check on name. - NamedPrism namedPrism = NamedPrism.getInstance( qualifierAnnotationMirror ); - if ( namedPrism.value() != null && qualfiedByNames.contains( namedPrism.value() ) ) { + NamedGem named = NamedGem.instanceOn( qualifierAnnotationMirror ); + if ( named.value().hasValue() && qualfiedByNames.contains( named.value().get() ) ) { // Match! its an @Name and the value matches as well. Oh boy. matchingQualifierCounter++; } @@ -168,7 +167,7 @@ private Set getQualifierAnnotationMirrors( Method candidate ) private void addOnlyWhenQualifier( Set annotationSet, AnnotationMirror candidate ) { // only add the candidate annotation when the candidate itself has the annotation 'Qualifier' - if ( QualifierPrism.getInstanceOn( candidate.getAnnotationType().asElement() ) != null ) { + if ( QualifierGem.instanceOn( candidate.getAnnotationType().asElement() ) != null ) { annotationSet.add( candidate ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java index 4f51e91b6a..cb9d309171 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java @@ -7,18 +7,17 @@ import java.util.ArrayList; import java.util.List; - import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.gem.XmlElementRefGem; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SourceMethod; -import org.mapstruct.ap.internal.prism.XmlElementDeclPrism; -import org.mapstruct.ap.internal.prism.XmlElementRefPrism; +import org.mapstruct.ap.internal.gem.XmlElementDeclGem; /** * Finds the {@link javax.xml.bind.annotation.XmlElementRef} annotation on a field (of the mapping result type or its @@ -46,9 +45,9 @@ public XmlElementDeclSelector(Types typeUtils) { @Override public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, Type targetType, - SelectionCriteria criteria) { + List> methods, + List sourceTypes, Type targetType, + SelectionCriteria criteria) { List> nameMatches = new ArrayList<>(); List> scopeMatches = new ArrayList<>(); @@ -62,14 +61,15 @@ public List> getMatchingMethods(Method mapp } SourceMethod candidateMethod = (SourceMethod) candidate.getMethod(); - XmlElementDeclPrism xmlElementDecl = XmlElementDeclPrism.getInstanceOn( candidateMethod.getExecutable() ); + XmlElementDeclGem xmlElementDecl = + XmlElementDeclGem.instanceOn( candidateMethod.getExecutable() ); if ( xmlElementDecl == null ) { continue; } - String name = xmlElementDecl.name(); - TypeMirror scope = xmlElementDecl.scope(); + String name = xmlElementDecl.name().get(); + TypeMirror scope = xmlElementDecl.scope().get(); boolean nameIsSetAndMatches = name != null && name.equals( xmlElementRefInfo.nameValue() ); boolean scopeIsSetAndMatches = @@ -140,9 +140,9 @@ private XmlElementRefInfo findXmlElementRef(Type resultType, String targetProper for ( Element enclosed : currentElement.getEnclosedElements() ) { if ( enclosed.getKind().equals( ElementKind.FIELD ) && enclosed.getSimpleName().contentEquals( targetPropertyName ) ) { - XmlElementRefPrism xmlElementRef = XmlElementRefPrism.getInstanceOn( enclosed ); + XmlElementRefGem xmlElementRef = XmlElementRefGem.instanceOn( enclosed ); if ( xmlElementRef != null ) { - return new XmlElementRefInfo( xmlElementRef.name(), currentMirror ); + return new XmlElementRefInfo( xmlElementRef.name().get(), currentMirror ); } } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java index 92dcbc5dc4..54b69c28b3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java @@ -5,7 +5,7 @@ */ package org.mapstruct.ap.internal.option; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; /** * The options passed to the code generator. @@ -16,14 +16,14 @@ public class Options { private final boolean suppressGeneratorTimestamp; private final boolean suppressGeneratorVersionComment; - private final ReportingPolicyPrism unmappedTargetPolicy; + private final ReportingPolicyGem unmappedTargetPolicy; private final boolean alwaysGenerateSpi; private final String defaultComponentModel; private final String defaultInjectionStrategy; private final boolean verbose; public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVersionComment, - ReportingPolicyPrism unmappedTargetPolicy, + ReportingPolicyGem unmappedTargetPolicy, String defaultComponentModel, String defaultInjectionStrategy, boolean alwaysGenerateSpi, boolean verbose) { this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; @@ -43,7 +43,7 @@ public boolean isSuppressGeneratorVersionComment() { return suppressGeneratorVersionComment; } - public ReportingPolicyPrism getUnmappedTargetPolicy() { + public ReportingPolicyGem getUnmappedTargetPolicy() { return unmappedTargetPolicy; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java deleted file mode 100644 index f40cedd20a..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.internal.prism; - -import javax.xml.bind.annotation.XmlElementDecl; -import javax.xml.bind.annotation.XmlElementRef; - -import org.mapstruct.AfterMapping; -import org.mapstruct.BeanMapping; -import org.mapstruct.BeforeMapping; -import org.mapstruct.Builder; -import org.mapstruct.Context; -import org.mapstruct.DecoratedWith; -import org.mapstruct.InheritConfiguration; -import org.mapstruct.InheritInverseConfiguration; -import org.mapstruct.IterableMapping; -import org.mapstruct.MapMapping; -import org.mapstruct.Mapper; -import org.mapstruct.MapperConfig; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.Mappings; -import org.mapstruct.Named; -import org.mapstruct.ObjectFactory; -import org.mapstruct.Qualifier; -import org.mapstruct.TargetType; -import org.mapstruct.ValueMapping; -import org.mapstruct.ValueMappings; - -import net.java.dev.hickory.prism.GeneratePrism; -import net.java.dev.hickory.prism.GeneratePrisms; - -/** - * Triggers the generation of prism types using Hickory. - * - * @author Gunnar Morling - */ -@GeneratePrisms({ - @GeneratePrism(value = Mapper.class, publicAccess = true), - @GeneratePrism(value = Mapping.class, publicAccess = true), - @GeneratePrism(value = Mappings.class, publicAccess = true), - @GeneratePrism(value = IterableMapping.class, publicAccess = true), - @GeneratePrism(value = BeanMapping.class, publicAccess = true), - @GeneratePrism(value = MapMapping.class, publicAccess = true), - @GeneratePrism(value = TargetType.class, publicAccess = true), - @GeneratePrism(value = MappingTarget.class, publicAccess = true), - @GeneratePrism(value = DecoratedWith.class, publicAccess = true), - @GeneratePrism(value = MapperConfig.class, publicAccess = true), - @GeneratePrism(value = InheritConfiguration.class, publicAccess = true), - @GeneratePrism(value = InheritInverseConfiguration.class, publicAccess = true), - @GeneratePrism(value = Qualifier.class, publicAccess = true), - @GeneratePrism(value = Named.class, publicAccess = true), - @GeneratePrism(value = ObjectFactory.class, publicAccess = true), - @GeneratePrism(value = AfterMapping.class, publicAccess = true), - @GeneratePrism(value = BeforeMapping.class, publicAccess = true), - @GeneratePrism(value = ValueMapping.class, publicAccess = true), - @GeneratePrism(value = ValueMappings.class, publicAccess = true), - @GeneratePrism(value = Context.class, publicAccess = true), - @GeneratePrism(value = Builder.class, publicAccess = true), - - // external types - @GeneratePrism(value = XmlElementDecl.class, publicAccess = true), - @GeneratePrism(value = XmlElementRef.class, publicAccess = true) -}) -public class PrismGenerator { - -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java index 6991e4c8ff..4efb8011c3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java @@ -23,7 +23,7 @@ import org.mapstruct.ap.internal.model.MapperReference; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; +import org.mapstruct.ap.internal.gem.InjectionStrategyGem; import org.mapstruct.ap.internal.model.source.MapperOptions; /** @@ -45,7 +45,7 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, M MapperOptions mapperAnnotation = MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() ); String componentModel = mapperAnnotation.componentModel(); - InjectionStrategyPrism injectionStrategy = mapperAnnotation.getInjectionStrategy(); + InjectionStrategyGem injectionStrategy = mapperAnnotation.getInjectionStrategy(); if ( !getComponentModelIdentifier().equalsIgnoreCase( componentModel ) ) { return mapper; @@ -74,14 +74,14 @@ else if ( mapper.getDecorator() != null ) { } } - if ( injectionStrategy == InjectionStrategyPrism.CONSTRUCTOR ) { + if ( injectionStrategy == InjectionStrategyGem.CONSTRUCTOR ) { buildConstructors( mapper ); } return mapper; } - protected void adjustDecorator(Mapper mapper, InjectionStrategyPrism injectionStrategy) { + protected void adjustDecorator(Mapper mapper, InjectionStrategyGem injectionStrategy) { Decorator decorator = mapper.getDecorator(); for ( Annotation typeAnnotation : getDecoratorAnnotations() ) { @@ -220,15 +220,15 @@ protected List getDelegatorReferenceAnnotations(Mapper mapper) { /** * @param originalReference the reference to be replaced * @param annotations the list of annotations - * @param injectionStrategyPrism strategy for injection + * @param injectionStrategy strategy for injection * @return the mapper reference replacing the original one */ protected Field replacementMapperReference(Field originalReference, List annotations, - InjectionStrategyPrism injectionStrategyPrism) { + InjectionStrategyGem injectionStrategy) { boolean finalField = - injectionStrategyPrism == InjectionStrategyPrism.CONSTRUCTOR && !additionalPublicEmptyConstructor(); + injectionStrategy == InjectionStrategyGem.CONSTRUCTOR && !additionalPublicEmptyConstructor(); - boolean includeAnnotationsOnField = injectionStrategyPrism == InjectionStrategyPrism.FIELD; + boolean includeAnnotationsOnField = injectionStrategy == InjectionStrategyGem.FIELD; return new AnnotationMapperReference( originalReference.getType(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 02137a59e3..197d9fa63c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -46,13 +46,13 @@ import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.option.Options; -import org.mapstruct.ap.internal.prism.BuilderPrism; -import org.mapstruct.ap.internal.prism.DecoratedWithPrism; -import org.mapstruct.ap.internal.prism.InheritConfigurationPrism; -import org.mapstruct.ap.internal.prism.InheritInverseConfigurationPrism; -import org.mapstruct.ap.internal.prism.MapperPrism; -import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.gem.DecoratedWithGem; +import org.mapstruct.ap.internal.gem.InheritConfigurationGem; +import org.mapstruct.ap.internal.gem.InheritInverseConfigurationGem; +import org.mapstruct.ap.internal.gem.MapperGem; +import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.processor.creation.MappingResolverImpl; import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.FormattingMessager; @@ -132,7 +132,7 @@ private List initReferencedMappers(TypeElement element, MapperO for ( TypeMirror usedMapper : mapperAnnotation.uses() ) { DefaultMapperReference mapperReference = DefaultMapperReference.getInstance( typeFactory.getType( usedMapper ), - MapperPrism.getInstanceOn( typeUtils.asElement( usedMapper ) ) != null, + MapperGem.instanceOn( typeUtils.asElement( usedMapper ) ) != null, typeFactory, variableNames ); @@ -186,16 +186,16 @@ private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List< private Decorator getDecorator(TypeElement element, List methods, String implName, String implPackage, SortedSet extraImports) { - DecoratedWithPrism decoratorPrism = DecoratedWithPrism.getInstanceOn( element ); + DecoratedWithGem decoratedWith = DecoratedWithGem.instanceOn( element ); - if ( decoratorPrism == null ) { + if ( decoratedWith == null ) { return null; } - TypeElement decoratorElement = (TypeElement) typeUtils.asElement( decoratorPrism.value() ); + TypeElement decoratorElement = (TypeElement) typeUtils.asElement( decoratedWith.value().get() ); if ( !typeUtils.isAssignable( decoratorElement.asType(), element.asType() ) ) { - messager.printMessage( element, decoratorPrism.mirror, Message.DECORATOR_NO_SUBTYPE ); + messager.printMessage( element, decoratedWith.mirror(), Message.DECORATOR_NO_SUBTYPE ); } List mappingMethods = new ArrayList<>( methods.size() ); @@ -233,14 +233,14 @@ else if ( constructor.getParameters().size() == 1 ) { } if ( !hasDelegateConstructor && !hasDefaultConstructor ) { - messager.printMessage( element, decoratorPrism.mirror, Message.DECORATOR_CONSTRUCTOR ); + messager.printMessage( element, decoratedWith.mirror(), Message.DECORATOR_CONSTRUCTOR ); } Decorator decorator = new Decorator.Builder() .elementUtils( elementUtils ) .typeFactory( typeFactory ) .mapperElement( element ) - .decoratorPrism( decoratorPrism ) + .decoratedWith( decoratedWith ) .methods( mappingMethods ) .hasDelegateConstructor( hasDelegateConstructor ) .options( options ) @@ -305,7 +305,7 @@ else if ( method.isMapMapping() ) { FormattingParameters keyFormattingParameters = null; SelectionParameters valueSelectionParameters = null; FormattingParameters valueFormattingParameters = null; - NullValueMappingStrategyPrism nullValueMappingStrategy = null; + NullValueMappingStrategyGem nullValueMappingStrategy = null; if ( mappingOptions.getMapMapping() != null ) { keySelectionParameters = mappingOptions.getMapMapping().getKeySelectionParameters(); @@ -360,12 +360,12 @@ else if ( method.isStreamMapping() ) { } else { this.messager.note( 1, Message.BEANMAPPING_CREATE_NOTE, method ); - BuilderPrism builderPrism = method.getOptions().getBeanMapping().getBuilderPrism(); - BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder(); - BeanMappingMethod beanMappingMethod = builder + BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); + BeanMappingMethod.Builder beanMappingBuilder = new BeanMappingMethod.Builder(); + BeanMappingMethod beanMappingMethod = beanMappingBuilder .mappingContext( mappingContext ) .sourceMethod( method ) - .returnTypeBuilder( typeFactory.builderTypeFor( method.getReturnType(), builderPrism ) ) + .returnTypeBuilder( typeFactory.builderTypeFor( method.getReturnType(), builder ) ) .build(); if ( beanMappingMethod != null ) { @@ -448,7 +448,7 @@ private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConf } // apply auto inherited options - MappingInheritanceStrategyPrism inheritanceStrategy = mapperConfig.getMappingInheritanceStrategy(); + MappingInheritanceStrategyGem inheritanceStrategy = mapperConfig.getMappingInheritanceStrategy(); if ( inheritanceStrategy.isAutoInherit() ) { // but.. there should not be an @InheritedConfiguration @@ -511,11 +511,10 @@ private SourceMethod getInverseTemplateMethod(List rawMethods, Sou List initializingMethods, MapperOptions mapperConfig) { SourceMethod resultMethod = null; - InheritInverseConfigurationPrism inversePrism = InheritInverseConfigurationPrism.getInstanceOn( - method.getExecutable() - ); + InheritInverseConfigurationGem inverseConfiguration = + InheritInverseConfigurationGem.instanceOn( method.getExecutable() ); - if ( inversePrism != null ) { + if ( inverseConfiguration != null ) { // method is configured as being inverse method, collect candidates List candidates = new ArrayList<>(); @@ -525,7 +524,7 @@ private SourceMethod getInverseTemplateMethod(List rawMethods, Sou } } - String name = inversePrism.name(); + String name = inverseConfiguration.name().get(); if ( candidates.size() == 1 ) { // no ambiguity: if no configuredBy is specified, or configuredBy specified and match if ( name.isEmpty() ) { @@ -535,7 +534,7 @@ else if ( candidates.get( 0 ).getName().equals( name ) ) { resultMethod = candidates.get( 0 ); } else { - reportErrorWhenNonMatchingName( candidates.get( 0 ), method, inversePrism ); + reportErrorWhenNonMatchingName( candidates.get( 0 ), method, inverseConfiguration ); } } else if ( candidates.size() > 1 ) { @@ -552,10 +551,10 @@ else if ( candidates.size() > 1 ) { resultMethod = nameFilteredcandidates.get( 0 ); } else if ( nameFilteredcandidates.size() > 1 ) { - reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, inversePrism ); + reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, inverseConfiguration ); } else { - reportErrorWhenAmbigousReverseMapping( candidates, method, inversePrism ); + reportErrorWhenAmbigousReverseMapping( candidates, method, inverseConfiguration ); } } } @@ -588,11 +587,10 @@ private SourceMethod getForwardTemplateMethod(List rawMethods, Sou List initializingMethods, MapperOptions mapperConfig) { SourceMethod resultMethod = null; - InheritConfigurationPrism forwardPrism = InheritConfigurationPrism.getInstanceOn( - method.getExecutable() - ); + InheritConfigurationGem inheritConfiguration = + InheritConfigurationGem.instanceOn( method.getExecutable() ); - if ( forwardPrism != null ) { + if ( inheritConfiguration != null ) { List candidates = new ArrayList<>(); for ( SourceMethod oneMethod : rawMethods ) { @@ -602,7 +600,7 @@ private SourceMethod getForwardTemplateMethod(List rawMethods, Sou } } - String name = forwardPrism.name(); + String name = inheritConfiguration.name().get(); if ( candidates.size() == 1 ) { // no ambiguity: if no configuredBy is specified, or configuredBy specified and match SourceMethod sourceMethod = first( candidates ); @@ -613,7 +611,7 @@ else if ( sourceMethod.getName().equals( name ) ) { resultMethod = sourceMethod; } else { - reportErrorWhenNonMatchingName( sourceMethod, method, forwardPrism ); + reportErrorWhenNonMatchingName( sourceMethod, method, inheritConfiguration ); } } else if ( candidates.size() > 1 ) { @@ -630,10 +628,10 @@ else if ( candidates.size() > 1 ) { resultMethod = first( nameFilteredcandidates ); } else if ( nameFilteredcandidates.size() > 1 ) { - reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, forwardPrism ); + reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, inheritConfiguration ); } else { - reportErrorWhenAmbigousMapping( candidates, method, forwardPrism ); + reportErrorWhenAmbigousMapping( candidates, method, inheritConfiguration ); } } } @@ -642,17 +640,17 @@ else if ( nameFilteredcandidates.size() > 1 ) { } private void reportErrorWhenAmbigousReverseMapping(List candidates, SourceMethod method, - InheritInverseConfigurationPrism inversePrism) { + InheritInverseConfigurationGem inverseGem) { List candidateNames = new ArrayList<>(); for ( SourceMethod candidate : candidates ) { candidateNames.add( candidate.getName() ); } - String name = inversePrism.name(); + String name = inverseGem.name().get(); if ( name.isEmpty() ) { messager.printMessage( method.getExecutable(), - inversePrism.mirror, + inverseGem.mirror(), Message.INHERITINVERSECONFIGURATION_DUPLICATES, Strings.join( candidateNames, "(), " ) @@ -660,7 +658,7 @@ private void reportErrorWhenAmbigousReverseMapping(List candidates } else { messager.printMessage( method.getExecutable(), - inversePrism.mirror, + inverseGem.mirror(), Message.INHERITINVERSECONFIGURATION_INVALID_NAME, Strings.join( candidateNames, "(), " ), name @@ -670,40 +668,40 @@ private void reportErrorWhenAmbigousReverseMapping(List candidates } private void reportErrorWhenSeveralNamesMatch(List candidates, SourceMethod method, - InheritInverseConfigurationPrism inversePrism) { + InheritInverseConfigurationGem inverseGem) { messager.printMessage( method.getExecutable(), - inversePrism.mirror, + inverseGem.mirror(), Message.INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES, - inversePrism.name(), + inverseGem.name().get(), Strings.join( candidates, ", " ) ); } private void reportErrorWhenNonMatchingName(SourceMethod onlyCandidate, SourceMethod method, - InheritInverseConfigurationPrism inversePrism) { + InheritInverseConfigurationGem inverseGem) { messager.printMessage( method.getExecutable(), - inversePrism.mirror, + inverseGem.mirror(), Message.INHERITINVERSECONFIGURATION_NO_NAME_MATCH, - inversePrism.name(), + inverseGem.name().get(), onlyCandidate.getName() ); } private void reportErrorWhenAmbigousMapping(List candidates, SourceMethod method, - InheritConfigurationPrism prism) { + InheritConfigurationGem gem) { List candidateNames = new ArrayList<>(); for ( SourceMethod candidate : candidates ) { candidateNames.add( candidate.getName() ); } - String name = prism.name(); + String name = gem.name().get(); if ( name.isEmpty() ) { messager.printMessage( method.getExecutable(), - prism.mirror, + gem.mirror(), Message.INHERITCONFIGURATION_DUPLICATES, Strings.join( candidateNames, "(), " ) ); @@ -711,7 +709,7 @@ private void reportErrorWhenAmbigousMapping(List candidates, Sourc else { messager.printMessage( method.getExecutable(), - prism.mirror, + gem.mirror(), Message.INHERITCONFIGURATION_INVALIDNAME, Strings.join( candidateNames, "(), " ), name @@ -720,25 +718,25 @@ private void reportErrorWhenAmbigousMapping(List candidates, Sourc } private void reportErrorWhenSeveralNamesMatch(List candidates, SourceMethod method, - InheritConfigurationPrism prism) { + InheritConfigurationGem gem) { messager.printMessage( method.getExecutable(), - prism.mirror, + gem.mirror(), Message.INHERITCONFIGURATION_DUPLICATE_MATCHES, - prism.name(), + gem.name().get(), Strings.join( candidates, ", " ) ); } private void reportErrorWhenNonMatchingName(SourceMethod onlyCandidate, SourceMethod method, - InheritConfigurationPrism prims) { + InheritConfigurationGem gem) { messager.printMessage( method.getExecutable(), - prims.mirror, + gem.mirror(), Message.INHERITCONFIGURATION_NO_NAME_MATCH, - prims.name(), + gem.name().get(), onlyCandidate.getName() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index e1f62bf6b0..09a0cc95f2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -34,14 +34,14 @@ import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.ValueMappingOptions; -import org.mapstruct.ap.internal.prism.BeanMappingPrism; -import org.mapstruct.ap.internal.prism.IterableMappingPrism; -import org.mapstruct.ap.internal.prism.MapMappingPrism; -import org.mapstruct.ap.internal.prism.MappingPrism; -import org.mapstruct.ap.internal.prism.MappingsPrism; -import org.mapstruct.ap.internal.prism.ObjectFactoryPrism; -import org.mapstruct.ap.internal.prism.ValueMappingPrism; -import org.mapstruct.ap.internal.prism.ValueMappingsPrism; +import org.mapstruct.ap.internal.gem.BeanMappingGem; +import org.mapstruct.ap.internal.gem.IterableMappingGem; +import org.mapstruct.ap.internal.gem.MapMappingGem; +import org.mapstruct.ap.internal.gem.MappingGem; +import org.mapstruct.ap.internal.gem.MappingsGem; +import org.mapstruct.ap.internal.gem.ObjectFactoryGem; +import org.mapstruct.ap.internal.gem.ValueMappingGem; +import org.mapstruct.ap.internal.gem.ValueMappingsGem; import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.AnnotationProcessingException; import org.mapstruct.ap.internal.util.Executables; @@ -247,7 +247,7 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, retrieveContextProvidedMethods( contextParameters, mapperToImplement, mapperOptions ); BeanMappingOptions beanMappingOptions = BeanMappingOptions.getInstanceOn( - BeanMappingPrism.getInstanceOn( method ), + BeanMappingGem.instanceOn( method ), mapperOptions, method, messager, @@ -257,16 +257,16 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, Set mappingOptions = getMappings( method, method, beanMappingOptions, new LinkedHashSet<>(), new HashSet<>() ); - IterableMappingOptions iterableMappingOptions = IterableMappingOptions.fromPrism( - IterableMappingPrism.getInstanceOn( method ), + IterableMappingOptions iterableMappingOptions = IterableMappingOptions.fromGem( + IterableMappingGem.instanceOn( method ), mapperOptions, method, messager, typeUtils ); - MapMappingOptions mapMappingOptions = MapMappingOptions.fromPrism( - MapMappingPrism.getInstanceOn( method ), + MapMappingOptions mapMappingOptions = MapMappingOptions.fromGem( + MapMappingGem.instanceOn( method ), mapperOptions, method, messager, @@ -359,7 +359,7 @@ private boolean isValidFactoryMethod(ExecutableElement method, List p } private boolean hasFactoryAnnotation(ExecutableElement method) { - return ObjectFactoryPrism.getInstanceOn( method ) != null; + return ObjectFactoryGem.instanceOn( method ) != null; } private boolean isVoid(Type returnType) { @@ -514,25 +514,25 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List getMappings(ExecutableElement method, Element element, - BeanMappingOptions beanMapping, Set mappings, + BeanMappingOptions beanMapping, Set mappingOptions, Set handledElements) { for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { Element lElement = annotationMirror.getAnnotationType().asElement(); if ( isAnnotation( lElement, MAPPING_FQN ) ) { // although getInstanceOn does a search on annotation mirrors, the order is preserved - MappingPrism mappingPrism = MappingPrism.getInstanceOn( element ); - MappingOptions.addInstance( mappingPrism, method, beanMapping, messager, typeUtils, mappings ); + MappingGem mapping = MappingGem.instanceOn( element ); + MappingOptions.addInstance( mapping, method, beanMapping, messager, typeUtils, mappingOptions ); } else if ( isAnnotation( lElement, MAPPINGS_FQN ) ) { // although getInstanceOn does a search on annotation mirrors, the order is preserved - MappingsPrism mappingsPrism = MappingsPrism.getInstanceOn( element ); - MappingOptions.addInstances( mappingsPrism, method, beanMapping, messager, typeUtils, mappings ); + MappingsGem mappings = MappingsGem.instanceOn( element ); + MappingOptions.addInstances( mappings, method, beanMapping, messager, typeUtils, mappingOptions ); } else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) && !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG ) @@ -540,10 +540,10 @@ else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) ) { // recur over annotation mirrors handledElements.add( lElement ); - getMappings( method, lElement, beanMapping, mappings, handledElements ); + getMappings( method, lElement, beanMapping, mappingOptions, handledElements ); } } - return mappings; + return mappingOptions; } private boolean isAnnotationInPackage(Element element, String packageFQN ) { @@ -571,18 +571,18 @@ private boolean isAnnotation(Element element, String annotationFQN) { private List getValueMappings(ExecutableElement method) { List valueMappings = new ArrayList<>(); - ValueMappingPrism mappingAnnotation = ValueMappingPrism.getInstanceOn( method ); - ValueMappingsPrism mappingsAnnotation = ValueMappingsPrism.getInstanceOn( method ); + ValueMappingGem mappingAnnotation = ValueMappingGem.instanceOn( method ); + ValueMappingsGem mappingsAnnotation = ValueMappingsGem.instanceOn( method ); if ( mappingAnnotation != null ) { - ValueMappingOptions valueMapping = ValueMappingOptions.fromMappingPrism( mappingAnnotation ); + ValueMappingOptions valueMapping = ValueMappingOptions.fromMappingGem( mappingAnnotation ); if ( valueMapping != null ) { valueMappings.add( valueMapping ); } } if ( mappingsAnnotation != null ) { - ValueMappingOptions.fromMappingsPrism( mappingsAnnotation, method, messager, valueMappings ); + ValueMappingOptions.fromMappingsGem( mappingsAnnotation, method, messager, valueMappings ); } return valueMappings; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 8959eb3614..25de97d2b9 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -43,7 +43,7 @@ import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; @@ -664,11 +664,11 @@ Assignment getAssignment() { void reportMessageWhenNarrowing(FormattingMessager messager, ResolvingAttempt attempt) { if ( NativeTypes.isNarrowing( sourceType.getFullyQualifiedName(), targetType.getFullyQualifiedName() ) ) { - ReportingPolicyPrism policy = attempt.mappingMethod.getOptions().getMapper().typeConversionPolicy(); - if ( policy == ReportingPolicyPrism.WARN ) { + ReportingPolicyGem policy = attempt.mappingMethod.getOptions().getMapper().typeConversionPolicy(); + if ( policy == ReportingPolicyGem.WARN ) { report( messager, attempt, Message.CONVERSION_LOSSY_WARNING ); } - else if ( policy == ReportingPolicyPrism.ERROR ) { + else if ( policy == ReportingPolicyGem.ERROR ) { report( messager, attempt, Message.CONVERSION_LOSSY_ERROR ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java index 4fbae2bdbd..b6de4662b1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java @@ -18,8 +18,8 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; -import org.mapstruct.ap.internal.prism.AfterMappingPrism; -import org.mapstruct.ap.internal.prism.BeforeMappingPrism; +import org.mapstruct.ap.internal.gem.AfterMappingGem; +import org.mapstruct.ap.internal.gem.BeforeMappingGem; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.spi.TypeHierarchyErroneousException; @@ -233,7 +233,7 @@ public static boolean isLifecycleCallbackMethod(ExecutableElement executableElem * @return {@code true}, if the executable element is a method annotated with {@code @AfterMapping} */ public static boolean isAfterMappingMethod(ExecutableElement executableElement) { - return AfterMappingPrism.getInstanceOn( executableElement ) != null; + return AfterMappingGem.instanceOn( executableElement ) != null; } /** @@ -241,6 +241,6 @@ public static boolean isAfterMappingMethod(ExecutableElement executableElement) * @return {@code true}, if the executable element is a method annotated with {@code @BeforeMapping} */ public static boolean isBeforeMappingMethod(ExecutableElement executableElement) { - return BeforeMappingPrism.getInstanceOn( executableElement ) != null; + return BeforeMappingGem.instanceOn( executableElement ) != null; } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/prism/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java similarity index 75% rename from processor/src/test/java/org/mapstruct/ap/test/prism/ConstantTest.java rename to processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java index 2f439cfe75..b33f00964b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/prism/ConstantTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java @@ -3,13 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.prism; +package org.mapstruct.ap.test.gem; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; import org.mapstruct.MappingConstants; -import org.mapstruct.ap.internal.prism.MappingConstantsPrism; +import org.mapstruct.ap.internal.gem.MappingConstantsGem; /** * Test constants values @@ -20,8 +20,8 @@ public class ConstantTest { @Test public void constantsShouldBeEqual() { - assertThat( MappingConstants.ANY_REMAINING ).isEqualTo( MappingConstantsPrism.ANY_REMAINING ); - assertThat( MappingConstants.ANY_UNMAPPED ).isEqualTo( MappingConstantsPrism.ANY_UNMAPPED ); - assertThat( MappingConstants.NULL ).isEqualTo( MappingConstantsPrism.NULL ); + assertThat( MappingConstants.ANY_REMAINING ).isEqualTo( MappingConstantsGem.ANY_REMAINING ); + assertThat( MappingConstants.ANY_UNMAPPED ).isEqualTo( MappingConstantsGem.ANY_UNMAPPED ); + assertThat( MappingConstants.NULL ).isEqualTo( MappingConstantsGem.NULL ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/prism/EnumPrismsTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java similarity index 52% rename from processor/src/test/java/org/mapstruct/ap/test/prism/EnumPrismsTest.java rename to processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java index 3090ead472..3da35b91fb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/prism/EnumPrismsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.prism; +package org.mapstruct.ap.test.gem; import static org.assertj.core.api.Assertions.assertThat; @@ -18,53 +18,53 @@ import org.mapstruct.NullValueCheckStrategy; import org.mapstruct.NullValueMappingStrategy; import org.mapstruct.ReportingPolicy; -import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.InjectionStrategyPrism; -import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; -import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.ReportingPolicyPrism; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.InjectionStrategyGem; +import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; /** - * Test for manually created prisms on enumeration types + * Test for manually created gems on enumeration types * * @author Andreas Gudian */ -public class EnumPrismsTest { +public class EnumGemsTest { @Test - public void collectionMappingStrategyPrismIsCorrect() { + public void collectionMappingStrategyGemIsCorrect() { assertThat( namesOf( CollectionMappingStrategy.values() ) ).isEqualTo( - namesOf( CollectionMappingStrategyPrism.values() ) ); + namesOf( CollectionMappingStrategyGem.values() ) ); } @Test - public void mappingInheritanceStrategyPrismIsCorrect() { + public void mappingInheritanceStrategyGemIsCorrect() { assertThat( namesOf( MappingInheritanceStrategy.values() ) ).isEqualTo( - namesOf( MappingInheritanceStrategyPrism.values() ) ); + namesOf( MappingInheritanceStrategyGem.values() ) ); } @Test - public void nullValueCheckStrategyPrismIsCorrect() { + public void nullValueCheckStrategyGemIsCorrect() { assertThat( namesOf( NullValueCheckStrategy.values() ) ).isEqualTo( - namesOf( NullValueCheckStrategyPrism.values() ) ); + namesOf( NullValueCheckStrategyGem.values() ) ); } @Test - public void nullValueMappingStrategyPrismIsCorrect() { + public void nullValueMappingStrategyGemIsCorrect() { assertThat( namesOf( NullValueMappingStrategy.values() ) ).isEqualTo( - namesOf( NullValueMappingStrategyPrism.values() ) ); + namesOf( NullValueMappingStrategyGem.values() ) ); } @Test - public void reportingPolicyPrismIsCorrect() { + public void reportingPolicyGemIsCorrect() { assertThat( namesOf( ReportingPolicy.values() ) ).isEqualTo( - namesOf( ReportingPolicyPrism.values() ) ); + namesOf( ReportingPolicyGem.values() ) ); } @Test - public void injectionStrategyPrismIsCorrect() { + public void injectionStrategyGemIsCorrect() { assertThat( namesOf( InjectionStrategy.values() ) ).isEqualTo( - namesOf( InjectionStrategyPrism.values() ) ); + namesOf( InjectionStrategyGem.values() ) ); } private static List namesOf(Enum[] values) { diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java index 24d3aeb9ec..ca51554f30 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java @@ -144,6 +144,7 @@ private static List buildProcessorClasspath() { "freemarker", "javax.inject", "spring-context", + "gem-api", "joda-time" }; return filterBootClassPath( whitelist ); diff --git a/readme.md b/readme.md index cbd410ab10..37376c8714 100644 --- a/readme.md +++ b/readme.md @@ -145,7 +145,7 @@ from the root of the project directory. To skip the distribution module, run ## Importing into IDE -MapStruct uses the hickory annotation processor to generate mapping prisms for it's own annotations. +MapStruct uses the gem annotation processor to generate mapping gems for it's own annotations. Therefore for seamless integration within an IDE annotation processing needs to be enabled. ### IntelliJ From 58da2d293f74309b3679b331013f6466d2d09aa8 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sun, 2 Feb 2020 10:05:26 +0100 Subject: [PATCH 023/597] #695 user control over mapping means (direct, method, conversion, 2step) --- .../main/java/org/mapstruct/BeanMapping.java | 16 ++ .../java/org/mapstruct/IterableMapping.java | 18 +- .../main/java/org/mapstruct/MapMapping.java | 32 ++- core/src/main/java/org/mapstruct/Mapper.java | 15 ++ .../main/java/org/mapstruct/MapperConfig.java | 17 ++ core/src/main/java/org/mapstruct/Mapping.java | 17 ++ .../java/org/mapstruct/control/DeepClone.java | 24 +++ .../org/mapstruct/control/MappingControl.java | 150 +++++++++++++++ .../mapstruct/control/MappingControls.java | 23 +++ .../mapstruct/control/NoComplexMapping.java | 29 +++ .../chapter-5-data-type-conversions.asciidoc | 24 ++- .../ap/internal/gem/GemGenerator.java | 5 + .../ap/internal/gem/MappingControlUseGem.java | 18 ++ .../ap/internal/model/BeanMappingMethod.java | 1 + .../model/ContainerMappingMethodBuilder.java | 1 + .../ap/internal/model/MapMappingMethod.java | 15 +- .../ap/internal/model/PropertyMapping.java | 21 +- .../ap/internal/model/common/Type.java | 4 + .../model/source/BeanMappingOptions.java | 10 + .../internal/model/source/DefaultOptions.java | 6 + .../model/source/DelegatingOptions.java | 5 + .../model/source/IterableMappingOptions.java | 9 + .../model/source/MapMappingOptions.java | 17 ++ .../model/source/MapperConfigOptions.java | 8 + .../internal/model/source/MapperOptions.java | 8 + .../internal/model/source/MappingControl.java | 119 ++++++++++++ .../internal/model/source/MappingOptions.java | 12 +- .../source/selector/SelectionCriteria.java | 51 ++++- .../creation/MappingResolverImpl.java | 107 ++++++---- .../ap/test/mappingcontrol/CloningMapper.java | 19 ++ .../ap/test/mappingcontrol/ComplexMapper.java | 23 +++ .../ap/test/mappingcontrol/Config.java | 16 ++ .../test/mappingcontrol/ConversionMapper.java | 18 ++ .../ap/test/mappingcontrol/CoolBeerDTO.java | 19 ++ .../ap/test/mappingcontrol/DirectMapper.java | 20 ++ .../ErroneousComplexMapper.java | 23 +++ .../ErroneousComplexMapperWithConfig.java | 23 +++ .../ErroneousConversionMapper.java | 18 ++ .../mappingcontrol/ErroneousDirectMapper.java | 20 ++ .../mappingcontrol/ErroneousMethodMapper.java | 19 ++ .../ap/test/mappingcontrol/Fridge.java | 19 ++ .../ap/test/mappingcontrol/FridgeDTO.java | 19 ++ .../mappingcontrol/MappingControlTest.java | 182 ++++++++++++++++++ .../ap/test/mappingcontrol/MethodMapper.java | 23 +++ .../ap/test/mappingcontrol/ShelveDTO.java | 19 ++ .../ap/test/mappingcontrol/UseComplex.java | 18 ++ .../ap/test/mappingcontrol/UseDirect.java | 18 ++ 47 files changed, 1237 insertions(+), 61 deletions(-) create mode 100644 core/src/main/java/org/mapstruct/control/DeepClone.java create mode 100644 core/src/main/java/org/mapstruct/control/MappingControl.java create mode 100644 core/src/main/java/org/mapstruct/control/MappingControls.java create mode 100644 core/src/main/java/org/mapstruct/control/NoComplexMapping.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/gem/MappingControlUseGem.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingControl.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ComplexMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/Config.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ConversionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CoolBeerDTO.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/DirectMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousComplexMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousComplexMapperWithConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousConversionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousDirectMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/Fridge.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/FridgeDTO.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ShelveDTO.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/UseComplex.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/UseDirect.java diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java index 55ab05ae5e..329d7bd50e 100644 --- a/core/src/main/java/org/mapstruct/BeanMapping.java +++ b/core/src/main/java/org/mapstruct/BeanMapping.java @@ -11,6 +11,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.mapstruct.control.MappingControl; + import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; /** @@ -156,4 +158,18 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() * @since 1.3 */ Builder builder() default @Builder; + + /** + * Allows detailed control over the mapping process. + * + * @return the mapping control + * + * @since 1.4 + * + * @see org.mapstruct.control.DeepClone + * @see org.mapstruct.control.NoComplexMapping + * @see org.mapstruct.control.MappingControl + */ + Class mappingControl() default MappingControl.class; + } diff --git a/core/src/main/java/org/mapstruct/IterableMapping.java b/core/src/main/java/org/mapstruct/IterableMapping.java index 3041c4bfd0..76153127bd 100644 --- a/core/src/main/java/org/mapstruct/IterableMapping.java +++ b/core/src/main/java/org/mapstruct/IterableMapping.java @@ -10,10 +10,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.text.SimpleDateFormat; import java.text.DecimalFormat; +import java.text.SimpleDateFormat; import java.util.Date; +import org.mapstruct.control.MappingControl; + /** * Configures the mapping between two iterable like types, e.g. {@code List} and {@code List}. * @@ -121,4 +123,18 @@ * @return The strategy to be applied when {@code null} is passed as source value to the methods of this mapping. */ NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL; + + /** + * Allows detailed control over the mapping process. + * + * @return the mapping control + * + * @since 1.4 + * + * @see org.mapstruct.control.DeepClone + * @see org.mapstruct.control.NoComplexMapping + * @see org.mapstruct.control.MappingControl + */ + Class elementMappingControl() default MappingControl.class; + } diff --git a/core/src/main/java/org/mapstruct/MapMapping.java b/core/src/main/java/org/mapstruct/MapMapping.java index 4e014d9ba6..271272bb45 100644 --- a/core/src/main/java/org/mapstruct/MapMapping.java +++ b/core/src/main/java/org/mapstruct/MapMapping.java @@ -10,10 +10,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.text.SimpleDateFormat; import java.text.DecimalFormat; +import java.text.SimpleDateFormat; import java.util.Date; +import org.mapstruct.control.MappingControl; + /** * Configures the mapping between two map types, e.g. Map<String, String> and Map<Long, Date>. * @@ -167,4 +169,32 @@ * @return The strategy to be applied when {@code null} is passed as source value to the methods of this mapping. */ NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL; + + /** + * Allows detailed control over the key mapping process. + * + * @return the mapping control + * + * @since 1.4 + + * @see org.mapstruct.control.DeepClone + * @see org.mapstruct.control.NoComplexMapping + * @see org.mapstruct.control.MappingControl + */ + Class keyMappingControl() default MappingControl.class; + + + /** + * Allows detailed control over the value mapping process. + * + * @return the mapping control + * + * @since 1.4 + * + * @see org.mapstruct.control.DeepClone + * @see org.mapstruct.control.NoComplexMapping + * @see org.mapstruct.control.MappingControl + */ + Class valueMappingControl() default MappingControl.class; + } diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 52d12426fc..ce8eebf1cb 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -5,11 +5,13 @@ */ package org.mapstruct; +import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.mapstruct.control.MappingControl; import org.mapstruct.factory.Mappers; import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; @@ -281,4 +283,17 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default * @since 1.3 */ Builder builder() default @Builder; + + /** + * Allows detailed control over the mapping process. + * + * @return the mapping control + * + * @since 1.4 + * + * @see org.mapstruct.control.DeepClone + * @see org.mapstruct.control.NoComplexMapping + * @see org.mapstruct.control.MappingControl + */ + Class mappingControl() default MappingControl.class; } diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java index c03217f08e..326041164f 100644 --- a/core/src/main/java/org/mapstruct/MapperConfig.java +++ b/core/src/main/java/org/mapstruct/MapperConfig.java @@ -5,11 +5,13 @@ */ package org.mapstruct; +import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.mapstruct.control.MappingControl; import org.mapstruct.factory.Mappers; import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; @@ -255,4 +257,19 @@ MappingInheritanceStrategy mappingInheritanceStrategy() * @since 1.3 */ Builder builder() default @Builder; + + /** + * Allows detailed control over the mapping process. + * + * @return the mapping control + * + * @since 1.4 + * + * @see org.mapstruct.control.DeepClone + * @see org.mapstruct.control.NoComplexMapping + * @see org.mapstruct.control.MappingControl + */ + Class mappingControl() default MappingControl.class; + } + diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 98a309e057..b5ce5c7a83 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -15,6 +15,8 @@ import java.text.SimpleDateFormat; import java.util.Date; +import org.mapstruct.control.MappingControl; + import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; /** @@ -394,4 +396,19 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default NullValuePropertyMappingStrategy.SET_TO_NULL; + /** + * Allows detailed control over the mapping process. + * + * @return the mapping control + * + * @since 1.4 + * + * @see org.mapstruct.control.DeepClone + * @see org.mapstruct.control.NoComplexMapping + * @see org.mapstruct.control.MappingControl + */ + Class mappingControl() default MappingControl.class; + + + } diff --git a/core/src/main/java/org/mapstruct/control/DeepClone.java b/core/src/main/java/org/mapstruct/control/DeepClone.java new file mode 100644 index 0000000000..a5264c861e --- /dev/null +++ b/core/src/main/java/org/mapstruct/control/DeepClone.java @@ -0,0 +1,24 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.control; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.util.Experimental; + +/** + * Clones a source type to a target type (assuming source and target are of the same type). + * + * @author Sjaak Derksen + * + * @since 1.4 + */ +@Retention(RetentionPolicy.CLASS) +@Experimental +@MappingControl( MappingControl.Use.MAPPING_METHOD ) +public @interface DeepClone { +} diff --git a/core/src/main/java/org/mapstruct/control/MappingControl.java b/core/src/main/java/org/mapstruct/control/MappingControl.java new file mode 100644 index 0000000000..975ff52e90 --- /dev/null +++ b/core/src/main/java/org/mapstruct/control/MappingControl.java @@ -0,0 +1,150 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.control; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * Controls which means of mapping are considered between the source and the target in mappings. + * + *

    + * There are several applications of MappingControl conceivable. One application, "deep cloning" is + * explained below in the example. + *

    + * + *

    + * Another application is controlling so called "complex mappings", which are not always desirable and sometimes lead to + * unexpected behaviour and prolonged compilation time. + *

    + * + *

    Example:Cloning of an object

    + *

    + * When all methods are allowed, MapStruct would make a shallow copy. It would take the ShelveDTO in + * the FridgeDTO and directly enter that as target on the target FridgeDTO. By disabling all + * other kinds of mappings apart from {@link MappingControl.Use#MAPPING_METHOD}, see {@link DeepClone} MapStruct is + * forced to generate mapping methods all through the object graph `FridgeDTO` and hence create a deep clone. + *

    + *
    
    + * public class FridgeDTO {
    + *
    + *     private ShelveDTO shelve;
    + *
    + *     public ShelveDTO getShelve() {
    + *         return shelve;
    + *     }
    + *
    + *     public void setShelve(ShelveDTO shelve) {
    + *         this.shelve = shelve;
    + *     }
    + * }
    + * 
    + *
    
    + * public class ShelveDTO {
    + *
    + *     private CoolBeerDTO coolBeer;
    + *
    + *     public CoolBeerDTO getCoolBeer() {
    + *         return coolBeer;
    + *     }
    + *
    + *     public void setCoolBeer(CoolBeerDTO coolBeer) {
    + *         this.coolBeer = coolBeer;
    + *     }
    + * }
    + * 
    + *
    
    + * public class CoolBeerDTO {
    + *
    + *     private String beerCount;
    + *
    + *     public String getBeerCount() {
    + *         return beerCount;
    + *     }
    + *
    + *     public void setBeerCount(String beerCount) {
    + *         this.beerCount = beerCount;
    + *     }
    + * }
    + * 
    + * + *
    
    + * @Mapper(mappingControl = DeepClone.class)
    + * public interface CloningMapper {
    + *
    + *     CloningMapper INSTANCE = Mappers.getMapper( CloningMapper.class );
    + *
    + *     FridgeDTO clone(FridgeDTO in);
    + *
    + * }
    + * 
    + * + * @author Sjaak Derksen + */ +@Retention(RetentionPolicy.CLASS) +@Repeatable(MappingControls.class) +@Target( ElementType.ANNOTATION_TYPE ) +@MappingControl( MappingControl.Use.DIRECT ) +@MappingControl( MappingControl.Use.BUILT_IN_CONVERSION ) +@MappingControl( MappingControl.Use.MAPPING_METHOD ) +@MappingControl( MappingControl.Use.COMPLEX_MAPPING ) +public @interface MappingControl { + + Use value(); + + enum Use { + + /** + * Controls the mapping, allows for type conversion from source type to target type + *

    + * Type conversions are typically supported directly in Java. The "toString()" is such an example, + * which allows for mapping for instance a {@link java.lang.Number} type to a {@link java.lang.String}. + *

    + * Please refer to the MapStruct guide for more info. + * + * @since 1.4 + */ + BUILT_IN_CONVERSION, + + /** + * Controls the mapping from source to target type, allows mapping by calling: + *

      + *
    1. A type conversion, passed into a mapping method
    2. + *
    3. A mapping method, passed into a type conversion
    4. + *
    5. A mapping method passed into another mapping method
    6. + *
    + * + * @since 1.4 + */ + COMPLEX_MAPPING, + /** + * Controls the mapping, allows for a direct mapping from source type to target type. + *

    + * This means if source type and target type are of the same type, MapStruct will not perform + * any mappings anymore and assign the target to the source direct. + *

    + * An exception are types from the package {@link java}, which will be mapped always directly. + * + * @since 1.4 + */ + DIRECT, + + /** + * Controls the mapping, allows for Direct Mapping from source type to target type. + *

    + * The mapping method can be either a custom referred mapping method, or a MapStruct built in + * mapping method. + * + * @since 1.4 + */ + MAPPING_METHOD + } + +} diff --git a/core/src/main/java/org/mapstruct/control/MappingControls.java b/core/src/main/java/org/mapstruct/control/MappingControls.java new file mode 100644 index 0000000000..d3b4fe1556 --- /dev/null +++ b/core/src/main/java/org/mapstruct/control/MappingControls.java @@ -0,0 +1,23 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.control; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Allows multiple {@link MappingControl} on a class declaration. + * + * @author Sjaak Derksen + */ +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.ANNOTATION_TYPE) +public @interface MappingControls { + + MappingControl[] value(); +} diff --git a/core/src/main/java/org/mapstruct/control/NoComplexMapping.java b/core/src/main/java/org/mapstruct/control/NoComplexMapping.java new file mode 100644 index 0000000000..5894f602a5 --- /dev/null +++ b/core/src/main/java/org/mapstruct/control/NoComplexMapping.java @@ -0,0 +1,29 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.control; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.util.Experimental; + +/** + * Disables complex mappings, mappings that require 2 mapping means (method, built-in conversion) to constitute + * a mapping from source to target. + * + * @see MappingControl.Use#COMPLEX_MAPPING + * + * @author Sjaak Derksen + * + * @since 1.4 + */ +@Retention(RetentionPolicy.CLASS) +@Experimental +@MappingControl( MappingControl.Use.DIRECT ) +@MappingControl( MappingControl.Use.BUILT_IN_CONVERSION ) +@MappingControl( MappingControl.Use.MAPPING_METHOD ) +public @interface NoComplexMapping { +} diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 2a446dee5e..7a22aad559 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -145,15 +145,27 @@ That way it is possible to map arbitrary deep object graphs. When mapping from e When generating the implementation of a mapping method, MapStruct will apply the following routine for each attribute pair in the source and target object: -* If source and target attribute have the same type, the value will be simply copied from source to target. If the attribute is a collection (e.g. a `List`) a copy of the collection will be set into the target attribute. -* If source and target attribute type differ, check whether there is another mapping method which has the type of the source attribute as parameter type and the type of the target attribute as return type. If such a method exists it will be invoked in the generated mapping implementation. -* If no such method exists MapStruct will look whether a built-in conversion for the source and target type of the attribute exists. If this is the case, the generated mapping code will apply this conversion. -* If no such method was found MapStruct will try to generate an automatic sub-mapping method that will do the mapping between the source and target attributes. -* If MapStruct could not create a name based mapping method an error will be raised at build time, indicating the non-mappable attribute and its path. +. If source and target attribute have the same type, the value will be simply copied *direct* from source to target. If the attribute is a collection (e.g. a `List`) a copy of the collection will be set into the target attribute. +. If source and target attribute type differ, check whether there is another *mapping method* which has the type of the source attribute as parameter type and the type of the target attribute as return type. If such a method exists it will be invoked in the generated mapping implementation. +. If no such method exists MapStruct will look whether a *built-in conversion* for the source and target type of the attribute exists. If this is the case, the generated mapping code will apply this conversion. +. If no such method exists MapStruct will apply *complex* conversions: +.. mapping method, the result mapped by mapping method, like this: `target = method1( method2( source ) )` +.. built-in conversion, the result mapped by mapping method, like this: `target = method( conversion( source ) )` +.. mapping method, the result mapped by build-in conversion, like this: `target = conversion( method( source ) )` +. If no such method was found MapStruct will try to generate an automatic sub-mapping method that will do the mapping between the source and target attributes. +. If MapStruct could not create a name based mapping method an error will be raised at build time, indicating the non-mappable attribute and its path. + +A mapping control (`MappingControl`) can be defined on all levels (`@MappingConfig`, `@Mapper`, `@BeanMapping`, `@Mapping`), the latter taking precedence over the former. For example: `@Mapper( mappingControl = NoComplexMapping.class )` takes precedence over `@MapperConfig( mappingControl = DeepClone.class )`. `@IterableMapping` and `@MapMapping` work similar as `@Mapping`. MappingControl is experimental from MapStruct 1.4. +`MappingControl` has an enum that corresponds to the first 4 options above: `MappingControl.Use#DIRECT`, `MappingControl.Use#MAPPING_METHOD`, `MappingControl.Use#BUILT_IN_CONVERSION` and `MappingControl.Use#COMPLEX_MAPPING` the presence of which allows the user to switch *on* a option. The absence of an enum switches *off* a mapping option. Default they are all present enabling all mapping options. [NOTE] ==== -In order to stop MapStruct from generating automatic sub-mapping methods, one can use `@Mapper( disableSubMappingMethodsGeneration = true )`. +In order to stop MapStruct from generating automatic sub-mapping methods as in 5. above, one can use `@Mapper( disableSubMappingMethodsGeneration = true )`. +==== + +[TIP] +==== +The user has full control over the mapping by means of meta annotations. Some handy ones have been defined such as `@DeepClone` which only allows direct mappings. The result: if source and target type are the same, MapStruct will make a deep clone of the source. Sub-mappings-methods have to be allowed (default option). ==== [NOTE] diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index 6852f013e7..b4d7758a10 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -29,6 +29,8 @@ import org.mapstruct.TargetType; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; +import org.mapstruct.control.MappingControl; +import org.mapstruct.control.MappingControls; import org.mapstruct.tools.gem.GemDefinition; /** @@ -58,6 +60,9 @@ @GemDefinition(Context.class) @GemDefinition(Builder.class) +@GemDefinition(MappingControl.class) +@GemDefinition(MappingControls.class) + // external types @GemDefinition(XmlElementDecl.class) @GemDefinition(XmlElementRef.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingControlUseGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingControlUseGem.java new file mode 100644 index 0000000000..94c8350ef8 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingControlUseGem.java @@ -0,0 +1,18 @@ +/* + * 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.internal.gem; + +/** + * + * @author Sjaak + */ +public enum MappingControlUseGem { + + BUILT_IN_CONVERSION, + COMPLEX_MAPPING, + DIRECT, + MAPPING_METHOD +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index af14d384c1..1fcf8e4ce2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -577,6 +577,7 @@ else if ( mapping.getConstant() != null ) { .targetPropertyName( targetPropertyName ) .formattingParameters( mapping.getFormattingParameters() ) .selectionParameters( mapping.getSelectionParameters() ) + .options( mapping ) .existingVariableNames( existingVariableNames ) .dependsOn( mapping.getDependsOn() ) .mirror( mapping.getMirror() ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java index e669e69aad..67c03fd3de 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java @@ -77,6 +77,7 @@ public final M build() { ); SelectionCriteria criteria = SelectionCriteria.forMappingMethods( selectionParameters, + method.getOptions().getIterableMapping().getMappingControl( ctx.getElementUtils() ), callingContextTargetPropertyName, false ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java index e14382318e..0fbe6f9c00 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java @@ -87,8 +87,12 @@ public MapMappingMethod build() { SourceRHS keySourceRHS = new SourceRHS( "entry.getKey()", keySourceType, new HashSet<>(), "map key" ); - SelectionCriteria keyCriteria = - SelectionCriteria.forMappingMethods( keySelectionParameters, null, false ); + SelectionCriteria keyCriteria = SelectionCriteria.forMappingMethods( + keySelectionParameters, + method.getOptions().getMapMapping().getKeyMappingControl( ctx.getElementUtils() ), + null, + false + ); Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment( method, @@ -130,8 +134,11 @@ public MapMappingMethod build() { SourceRHS valueSourceRHS = new SourceRHS( "entry.getValue()", valueSourceType, new HashSet<>(), "map value" ); - SelectionCriteria valueCriteria = - SelectionCriteria.forMappingMethods( valueSelectionParameters, null, false ); + SelectionCriteria valueCriteria = SelectionCriteria.forMappingMethods( + valueSelectionParameters, + method.getOptions().getMapMapping().getValueMappingControl( ctx.getElementUtils() ), + null, + false ); Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment( method, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 7b806f57ef..06de2aa397 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -31,6 +31,8 @@ import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.DelegatingOptions; +import org.mapstruct.ap.internal.model.source.MappingControl; +import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; @@ -154,6 +156,7 @@ public static class PropertyMappingBuilder extends MappingBuilderBase MappingControl.fromTypeMirror( mc, elementUtils ) ) + .orElse( next().getMappingControl( elementUtils ) ); + } + // @BeanMapping specific public SelectionParameters getSelectionParameters() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index adbc5286bc..77911be2e6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.Set; import javax.lang.model.type.DeclaredType; +import javax.lang.model.util.Elements; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.gem.BuilderGem; @@ -121,6 +122,11 @@ public BuilderGem getBuilder() { return null; } + @Override + public MappingControl getMappingControl(Elements elementUtils) { + return MappingControl.fromTypeMirror( mapper.mappingControl().getDefaultValue(), elementUtils ); + } + @Override public boolean hasAnnotation() { return false; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java index 9062b0a031..b9dc068d07 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -11,6 +11,7 @@ import java.util.stream.Collectors; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; @@ -100,6 +101,10 @@ public BuilderGem getBuilder() { return next.getBuilder(); } + public MappingControl getMappingControl(Elements elementUtils) { + return next.getMappingControl( elementUtils ); + } + DelegatingOptions next() { return next; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java index 70601a96c6..73ae1907cf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java @@ -8,6 +8,7 @@ import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.mapstruct.ap.internal.model.common.FormattingParameters; @@ -101,6 +102,14 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { .orElse( next().getNullValueMappingStrategy() ); } + public MappingControl getElementMappingControl(Elements elementUtils) { + return Optional.ofNullable( iterableMapping ).map( IterableMappingGem::elementMappingControl ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( mc -> MappingControl.fromTypeMirror( mc, elementUtils ) ) + .orElse( next().getMappingControl( elementUtils ) ); + } + @Override public boolean hasAnnotation() { return iterableMapping != null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java index 3dd3927f58..96ff577f48 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java @@ -8,6 +8,7 @@ import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.mapstruct.ap.internal.model.common.FormattingParameters; @@ -145,6 +146,22 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { .orElse( next().getNullValueMappingStrategy() ); } + public MappingControl getKeyMappingControl(Elements elementUtils) { + return Optional.ofNullable( mapMapping ).map( MapMappingGem::keyMappingControl ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( mc -> MappingControl.fromTypeMirror( mc, elementUtils ) ) + .orElse( next().getMappingControl( elementUtils ) ); + } + + public MappingControl getValueMappingControl(Elements elementUtils) { + return Optional.ofNullable( mapMapping ).map( MapMappingGem::valueMappingControl ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( mc -> MappingControl.fromTypeMirror( mc, elementUtils ) ) + .orElse( next().getMappingControl( elementUtils ) ); + } + @Override public boolean hasAnnotation() { return mapMapping != null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java index 5a3a0e1ade..ef25fda053 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -7,6 +7,7 @@ import java.util.Set; import javax.lang.model.type.DeclaredType; +import javax.lang.model.util.Elements; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; @@ -129,6 +130,13 @@ public BuilderGem getBuilder() { return mapperConfig.builder().hasValue() ? mapperConfig.builder().get() : next().getBuilder(); } + @Override + public MappingControl getMappingControl(Elements elementUtils) { + return mapperConfig.mappingControl().hasValue() ? + MappingControl.fromTypeMirror( mapperConfig.mappingControl().getValue(), elementUtils ) : + next().getMappingControl( elementUtils ); + } + @Override public boolean hasAnnotation() { return mapperConfig != null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index 9472694067..19b7d8f0b4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -11,6 +11,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; +import javax.lang.model.util.Elements; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.gem.BuilderGem; @@ -159,6 +160,13 @@ public BuilderGem getBuilder() { return mapper.builder().hasValue() ? mapper.builder().get() : next().getBuilder(); } + @Override + public MappingControl getMappingControl(Elements elementUtils) { + return mapper.mappingControl().hasValue() ? + MappingControl.fromTypeMirror( mapper.mappingControl().getValue(), elementUtils ) : + next().getMappingControl( elementUtils ); + } + // @Mapper specific public DeclaredType mapperConfigType() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingControl.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingControl.java new file mode 100644 index 0000000000..fc896bb52e --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingControl.java @@ -0,0 +1,119 @@ +/* + * 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.internal.model.source; + +import java.util.HashSet; +import java.util.Set; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; + +import org.mapstruct.ap.internal.gem.MappingControlGem; +import org.mapstruct.ap.internal.gem.MappingControlUseGem; +import org.mapstruct.ap.internal.gem.MappingControlsGem; + +public class MappingControl { + + private static final String JAVA_LANG_ANNOTATION_PGK = "java.lang.annotation"; + private static final String ORG_MAPSTRUCT_PKG = "org.mapstruct"; + private static final String MAPPING_CONTROL_FQN = "org.mapstruct.control.MappingControl"; + private static final String MAPPING_CONTROLS_FQN = "org.mapstruct.control.MappingControls"; + + private boolean allowDirect = false; + private boolean allowTypeConversion = false; + private boolean allowMappingMethod = false; + private boolean allow2Steps = false; + + public static MappingControl fromTypeMirror(TypeMirror mirror, Elements elementUtils) { + MappingControl mappingControl = new MappingControl(); + if ( TypeKind.DECLARED == mirror.getKind() ) { + resolveControls( mappingControl, ( (DeclaredType) mirror ).asElement(), new HashSet<>(), elementUtils ); + } + return mappingControl; + } + + private MappingControl() { + } + + public boolean allowDirect() { + return allowDirect; + } + + public boolean allowTypeConversion() { + return allowTypeConversion; + } + + public boolean allowMappingMethod() { + return allowMappingMethod; + } + + public boolean allowBy2Steps() { + return allow2Steps; + } + + private static void resolveControls(MappingControl control, Element element, Set handledElements, + Elements elementUtils) { + for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { + Element lElement = annotationMirror.getAnnotationType().asElement(); + if ( isAnnotation( lElement, MAPPING_CONTROL_FQN ) ) { + determineMappingControl( control, MappingControlGem.instanceOn( element ) ); + } + else if ( isAnnotation( lElement, MAPPING_CONTROLS_FQN ) ) { + MappingControlsGem.instanceOn( element ) + .value() + .get() + .forEach( m -> determineMappingControl( control, m ) ); + } + else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK, elementUtils ) + && !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG, elementUtils ) + && !handledElements.contains( lElement ) + ) { + // recur over annotation mirrors + handledElements.add( lElement ); + resolveControls( control, lElement, handledElements, elementUtils ); + } + } + } + + private static void determineMappingControl(MappingControl in, MappingControlGem gem) { + MappingControlUseGem use = MappingControlUseGem.valueOf( gem.value().get() ); + switch ( use ) { + case DIRECT: + in.allowDirect = true; + break; + case MAPPING_METHOD: + in.allowMappingMethod = true; + break; + case BUILT_IN_CONVERSION: + in.allowTypeConversion = true; + break; + case COMPLEX_MAPPING: + in.allow2Steps = true; + break; + default: + } + } + + private static boolean isAnnotationInPackage(Element element, String packageFQN, Elements elementUtils) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return packageFQN.equals( elementUtils.getPackageOf( element ).getQualifiedName().toString() ); + } + return false; + } + + private static boolean isAnnotation(Element element, String annotationFQN) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return annotationFQN.equals( ( (TypeElement) element ).getQualifiedName().toString() ); + } + return false; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 1557d68920..ed88e5107a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -17,13 +17,14 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.util.Elements; import javax.lang.model.util.Types; -import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.gem.MappingGem; import org.mapstruct.ap.internal.gem.MappingsGem; import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; +import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.tools.gem.GemValue; @@ -409,6 +410,15 @@ public NullValuePropertyMappingStrategyGem getNullValuePropertyMappingStrategy() .orElse( next().getNullValuePropertyMappingStrategy() ); } + @Override + public MappingControl getMappingControl(Elements elementUtils) { + return Optional.ofNullable( mapping ).map( MappingGem::mappingControl ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( mc -> MappingControl.fromTypeMirror( mc, elementUtils ) ) + .orElse( next().getMappingControl( elementUtils ) ); + } + /** * mapping can only be inversed if the source was not a constant nor an expression nor a nested property * and the mapping is not a 'target-source-ignore' mapping diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java index c632fec6c4..23c67b230e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java @@ -11,6 +11,7 @@ import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.model.common.SourceRHS; +import org.mapstruct.ap.internal.model.source.MappingControl; import org.mapstruct.ap.internal.model.source.SelectionParameters; /** @@ -28,9 +29,13 @@ public class SelectionCriteria { private boolean preferUpdateMapping; private final boolean objectFactoryRequired; private final boolean lifecycleCallbackRequired; + private final boolean allowDirect; + private final boolean allowConversion; + private final boolean allowMappingMethod; + private final boolean allow2Steps; - public SelectionCriteria(SelectionParameters selectionParameters, String targetPropertyName, - boolean preferUpdateMapping, boolean objectFactoryRequired, + public SelectionCriteria(SelectionParameters selectionParameters, MappingControl mappingControl, + String targetPropertyName, boolean preferUpdateMapping, boolean objectFactoryRequired, boolean lifecycleCallbackRequired) { if ( selectionParameters != null ) { qualifiers.addAll( selectionParameters.getQualifiers() ); @@ -42,6 +47,18 @@ public SelectionCriteria(SelectionParameters selectionParameters, String targetP this.qualifyingResultType = null; sourceRHS = null; } + if ( mappingControl != null ) { + this.allowDirect = mappingControl.allowDirect(); + this.allowConversion = mappingControl.allowTypeConversion(); + this.allowMappingMethod = mappingControl.allowMappingMethod(); + this.allow2Steps = mappingControl.allowBy2Steps(); + } + else { + this.allowDirect = true; + this.allowConversion = true; + this.allowMappingMethod = true; + this.allow2Steps = true; + } this.targetPropertyName = targetPropertyName; this.preferUpdateMapping = preferUpdateMapping; this.objectFactoryRequired = objectFactoryRequired; @@ -94,17 +111,41 @@ public boolean hasQualfiers() { return !qualifiedByNames.isEmpty() || !qualifiers.isEmpty(); } + public boolean isAllowDirect() { + return allowDirect; + } + + public boolean isAllowConversion() { + return allowConversion; + } + + public boolean isAllowMappingMethod() { + return allowMappingMethod; + } + + public boolean isAllow2Steps() { + return allow2Steps; + } + public static SelectionCriteria forMappingMethods(SelectionParameters selectionParameters, + MappingControl mappingControl, String targetPropertyName, boolean preferUpdateMapping) { - return new SelectionCriteria( selectionParameters, targetPropertyName, preferUpdateMapping, false, false ); + return new SelectionCriteria( + selectionParameters, + mappingControl, + targetPropertyName, + preferUpdateMapping, + false, + false + ); } public static SelectionCriteria forFactoryMethods(SelectionParameters selectionParameters) { - return new SelectionCriteria( selectionParameters, null, false, true, false ); + return new SelectionCriteria( selectionParameters, null, null, false, true, false ); } public static SelectionCriteria forLifecycleMethods(SelectionParameters selectionParameters) { - return new SelectionCriteria( selectionParameters, null, false, false, true ); + return new SelectionCriteria( selectionParameters, null, null, false, false, true ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 25de97d2b9..39d661f528 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -177,16 +177,20 @@ private List filterPossibleCandidateMethods(List candid private Assignment getTargetAssignment(Type sourceType, Type targetType) { + Assignment referencedMethod; + // first simple mapping method - Assignment referencedMethod = resolveViaMethod( sourceType, targetType, false ); - if ( referencedMethod != null ) { - referencedMethod.setAssignment( sourceRHS ); - return referencedMethod; + if ( allowMappingMethod() ) { + referencedMethod = resolveViaMethod( sourceType, targetType, false ); + if ( referencedMethod != null ) { + referencedMethod.setAssignment( sourceRHS ); + return referencedMethod; + } } // then direct assignable if ( !hasQualfiers() ) { - if ( sourceType.isAssignableTo( targetType ) || + if ( ( sourceType.isAssignableTo( targetType ) && allowDirect( sourceType, targetType ) ) || isAssignableThroughCollectionCopyConstructor( sourceType, targetType ) ) { Assignment simpleAssignment = sourceRHS; return simpleAssignment; @@ -206,47 +210,51 @@ private Assignment getTargetAssignment(Type sourceType, Type targetType) { } // then type conversion - if ( !hasQualfiers() ) { - ConversionAssignment conversion = resolveViaConversion( sourceType, targetType ); - if ( conversion != null ) { - conversion.reportMessageWhenNarrowing( messager, this ); - conversion.getAssignment().setAssignment( sourceRHS ); - return conversion.getAssignment(); + if ( allowConversion() ) { + if ( !hasQualfiers() ) { + ConversionAssignment conversion = resolveViaConversion( sourceType, targetType ); + if ( conversion != null ) { + conversion.reportMessageWhenNarrowing( messager, this ); + conversion.getAssignment().setAssignment( sourceRHS ); + return conversion.getAssignment(); + } } - } - // check for a built-in method - if (!hasQualfiers() ) { - Assignment builtInMethod = resolveViaBuiltInMethod( sourceType, targetType ); - if ( builtInMethod != null ) { - builtInMethod.setAssignment( sourceRHS ); - usedSupportedMappings.addAll( supportingMethodCandidates ); - return builtInMethod; + // check for a built-in method + if ( !hasQualfiers() ) { + Assignment builtInMethod = resolveViaBuiltInMethod( sourceType, targetType ); + if ( builtInMethod != null ) { + builtInMethod.setAssignment( sourceRHS ); + usedSupportedMappings.addAll( supportingMethodCandidates ); + return builtInMethod; + } } } - // 2 step method, first: method(method(source)) - referencedMethod = resolveViaMethodAndMethod( sourceType, targetType ); - if ( referencedMethod != null ) { - usedSupportedMappings.addAll( supportingMethodCandidates ); - return referencedMethod; - } + if ( allow2Steps() ) { + // 2 step method, first: method(method(source)) + referencedMethod = resolveViaMethodAndMethod( sourceType, targetType ); + if ( referencedMethod != null ) { + usedSupportedMappings.addAll( supportingMethodCandidates ); + return referencedMethod; + } - // 2 step method, then: method(conversion(source)) - referencedMethod = resolveViaConversionAndMethod( sourceType, targetType ); - if ( referencedMethod != null ) { - usedSupportedMappings.addAll( supportingMethodCandidates ); - return referencedMethod; - } + // 2 step method, then: method(conversion(source)) + referencedMethod = resolveViaConversionAndMethod( sourceType, targetType ); + if ( referencedMethod != null ) { + usedSupportedMappings.addAll( supportingMethodCandidates ); + return referencedMethod; + } - // stop here when looking for update methods. - selectionCriteria.setPreferUpdateMapping( false ); + // stop here when looking for update methods. + selectionCriteria.setPreferUpdateMapping( false ); - // 2 step method, finally: conversion(method(source)) - ConversionAssignment conversion = resolveViaMethodAndConversion( sourceType, targetType ); - if ( conversion != null ) { - usedSupportedMappings.addAll( supportingMethodCandidates ); - return conversion.getAssignment(); + // 2 step method, finally: conversion(method(source)) + ConversionAssignment conversion = resolveViaMethodAndConversion( sourceType, targetType ); + if ( conversion != null ) { + usedSupportedMappings.addAll( supportingMethodCandidates ); + return conversion.getAssignment(); + } } if ( hasQualfiers() ) { @@ -258,7 +266,8 @@ private Assignment getTargetAssignment(Type sourceType, Type targetType) { Strings.join( selectionCriteria.getQualifiedByNames(), ", " ) ); } - else { + else if ( allowMappingMethod() ) { + // only forge if we would allow mapping method return forger.get(); } @@ -270,6 +279,26 @@ private boolean hasQualfiers() { return selectionCriteria != null && selectionCriteria.hasQualfiers(); } + private boolean allowDirect( Type sourceType, Type targetType ) { + if ( sourceType.isPrimitive() || targetType.isPrimitive() + || sourceType.isJavaLangType() || targetType.isJavaLangType() ) { + return true; + } + return selectionCriteria != null && selectionCriteria.isAllowDirect(); + } + + private boolean allowConversion() { + return selectionCriteria != null && selectionCriteria.isAllowConversion(); + } + + private boolean allowMappingMethod() { + return selectionCriteria != null && selectionCriteria.isAllowMappingMethod(); + } + + private boolean allow2Steps() { + return selectionCriteria != null && selectionCriteria.isAllow2Steps(); + } + private ConversionAssignment resolveViaConversion(Type sourceType, Type targetType) { ConversionProvider conversionProvider = conversions.getConversion( sourceType, targetType ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningMapper.java new file mode 100644 index 0000000000..c1ff968d2c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningMapper.java @@ -0,0 +1,19 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.control.DeepClone; +import org.mapstruct.factory.Mappers; + +@Mapper(mappingControl = DeepClone.class) +public interface CloningMapper { + + CloningMapper INSTANCE = Mappers.getMapper( CloningMapper.class ); + + FridgeDTO clone(FridgeDTO in); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ComplexMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ComplexMapper.java new file mode 100644 index 0000000000..768e6117a5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ComplexMapper.java @@ -0,0 +1,23 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ComplexMapper { + + ComplexMapper INSTANCE = Mappers.getMapper( ComplexMapper.class ); + + @Mapping(target = "beerCount", source = "shelve") + Fridge map(FridgeDTO in); + + default String toBeerCount(ShelveDTO in) { + return in.getCoolBeer().getBeerCount(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/Config.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/Config.java new file mode 100644 index 0000000000..b37c8e4f25 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/Config.java @@ -0,0 +1,16 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.MapperConfig; +import org.mapstruct.control.NoComplexMapping; + +/** + * @author Sjaak Derksen + */ +@MapperConfig( mappingControl = NoComplexMapping.class ) +public interface Config { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ConversionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ConversionMapper.java new file mode 100644 index 0000000000..d4e1c3249d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ConversionMapper.java @@ -0,0 +1,18 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ConversionMapper { + + ConversionMapper INSTANCE = Mappers.getMapper( ConversionMapper.class ); + + Fridge map(CoolBeerDTO in); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CoolBeerDTO.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CoolBeerDTO.java new file mode 100644 index 0000000000..1505d3f291 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CoolBeerDTO.java @@ -0,0 +1,19 @@ +/* + * 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.mappingcontrol; + +public class CoolBeerDTO { + + private String beerCount; + + public String getBeerCount() { + return beerCount; + } + + public void setBeerCount(String beerCount) { + this.beerCount = beerCount; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/DirectMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/DirectMapper.java new file mode 100644 index 0000000000..1529c15eb1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/DirectMapper.java @@ -0,0 +1,20 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface DirectMapper { + + DirectMapper INSTANCE = Mappers.getMapper( DirectMapper.class ); + + @Mapping(target = "shelve", source = "shelve") + FridgeDTO map(FridgeDTO in); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousComplexMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousComplexMapper.java new file mode 100644 index 0000000000..71eaf227ae --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousComplexMapper.java @@ -0,0 +1,23 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper(mappingControl = UseDirect.class) +public interface ErroneousComplexMapper { + + ErroneousComplexMapper INSTANCE = Mappers.getMapper( ErroneousComplexMapper.class ); + + @Mapping(target = "beerCount", source = "shelve") + Fridge map(FridgeDTO in); + + default String toBeerCount(ShelveDTO in) { + return in.getCoolBeer().getBeerCount(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousComplexMapperWithConfig.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousComplexMapperWithConfig.java new file mode 100644 index 0000000000..ecb9c58837 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousComplexMapperWithConfig.java @@ -0,0 +1,23 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper(config = Config.class) +public interface ErroneousComplexMapperWithConfig { + + ErroneousComplexMapperWithConfig INSTANCE = Mappers.getMapper( ErroneousComplexMapperWithConfig.class ); + + @Mapping(target = "beerCount", source = "shelve") + Fridge map(FridgeDTO in); + + default String toBeerCount(ShelveDTO in) { + return in.getCoolBeer().getBeerCount(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousConversionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousConversionMapper.java new file mode 100644 index 0000000000..a7f09e40e2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousConversionMapper.java @@ -0,0 +1,18 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper(mappingControl = UseDirect.class) +public interface ErroneousConversionMapper { + + ErroneousConversionMapper INSTANCE = Mappers.getMapper( ErroneousConversionMapper.class ); + + Fridge map(CoolBeerDTO in); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousDirectMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousDirectMapper.java new file mode 100644 index 0000000000..672c9bd7c8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousDirectMapper.java @@ -0,0 +1,20 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper(mappingControl = UseComplex.class) +public interface ErroneousDirectMapper { + + ErroneousDirectMapper INSTANCE = Mappers.getMapper( ErroneousDirectMapper.class ); + + @Mapping(target = "shelve", source = "shelve") + FridgeDTO map(FridgeDTO in); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodMapper.java new file mode 100644 index 0000000000..276c37a6e7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodMapper.java @@ -0,0 +1,19 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper(mappingControl = UseDirect.class) +public interface ErroneousMethodMapper { + + ErroneousMethodMapper INSTANCE = Mappers.getMapper( ErroneousMethodMapper.class ); + + @Mapping(target = "beerCount", source = "shelve") + Fridge map(FridgeDTO in); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/Fridge.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/Fridge.java new file mode 100644 index 0000000000..52d0f0e376 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/Fridge.java @@ -0,0 +1,19 @@ +/* + * 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.mappingcontrol; + +public class Fridge { + + private int beerCount; + + public int getBeerCount() { + return beerCount; + } + + public void setBeerCount(int beerCount) { + this.beerCount = beerCount; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/FridgeDTO.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/FridgeDTO.java new file mode 100644 index 0000000000..0951278619 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/FridgeDTO.java @@ -0,0 +1,19 @@ +/* + * 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.mappingcontrol; + +public class FridgeDTO { + + private ShelveDTO shelve; + + public ShelveDTO getShelve() { + return shelve; + } + + public void setShelve(ShelveDTO shelve) { + this.shelve = shelve; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java new file mode 100644 index 0000000000..90f0957e68 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java @@ -0,0 +1,182 @@ +/* + * 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.mappingcontrol; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Sjaak Derksen + */ +@IssueKey("695") +@WithClasses({ + CoolBeerDTO.class, + ShelveDTO.class, + Fridge.class, + FridgeDTO.class, + UseDirect.class, + UseComplex.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class MappingControlTest { + + /** + * Baseline Test, normal, direct allowed + */ + @Test + @WithClasses(DirectMapper.class) + public void directSelectionAllowed() { + + FridgeDTO in = createFridgeDTO(); + FridgeDTO out = DirectMapper.INSTANCE.map( in ); + + assertThat( out ).isNotNull(); + assertThat( out.getShelve() ).isNotNull(); + assertThat( out.getShelve() ).isSameAs( in.getShelve() ); + } + + /** + * Test the deep cloning annotation + */ + @Test + @WithClasses(CloningMapper.class) + public void testDeepCloning() { + + FridgeDTO in = createFridgeDTO(); + FridgeDTO out = CloningMapper.INSTANCE.clone( in ); + + assertThat( out ).isNotNull(); + assertThat( out.getShelve() ).isNotNull(); + assertThat( out.getShelve() ).isNotSameAs( in.getShelve() ); + assertThat( out.getShelve().getCoolBeer() ).isNotSameAs( in.getShelve().getCoolBeer() ); + assertThat( out.getShelve().getCoolBeer().getBeerCount() ).isEqualTo( "5" ); + } + + /** + * This is a nice test. MapStruct looks for a way to map ShelveDto to ShelveDto. + *

    + * MapStruct gets too creative when we allow complex (2 step mappings) to convert if we also allow + * it to forge methods (which is contradiction with the fact that we do not allow methods on this mapper) + */ + @Test + @WithClasses(ErroneousDirectMapper.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousDirectMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \".*\\.ShelveDTO shelve\".*" + ) + }) + public void directSelectionNotAllowed() { + } + + /** + * Baseline Test, normal, method allowed + */ + @Test + @WithClasses(MethodMapper.class) + public void methodSelectionAllowed() { + Fridge fridge = MethodMapper.INSTANCE.map( createFridgeDTO() ); + + assertThat( fridge ).isNotNull(); + assertThat( fridge.getBeerCount() ).isEqualTo( 5 ); + } + + @Test + @WithClasses(ErroneousMethodMapper.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousMethodMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \"int beerCount\".*" + ) + }) + public void methodSelectionNotAllowed() { + } + + /** + * Baseline Test, normal, conversion allowed + */ + @Test + @WithClasses(ConversionMapper.class) + public void conversionSelectionAllowed() { + Fridge fridge = ConversionMapper.INSTANCE.map( createFridgeDTO().getShelve().getCoolBeer() ); + + assertThat( fridge ).isNotNull(); + assertThat( fridge.getBeerCount() ).isEqualTo( 5 ); + } + + @Test + @WithClasses(ErroneousConversionMapper.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousConversionMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 16, + messageRegExp = "Can't map property \".*\\.String beerCount\" to \"int beerCount\".*" + ) + }) + public void conversionSelectionNotAllowed() { + } + + /** + * Baseline Test, normal, complex mapping allowed + */ + @Test + @WithClasses(ComplexMapper.class) + public void complexSelectionAllowed() { + Fridge fridge = ComplexMapper.INSTANCE.map( createFridgeDTO() ); + + assertThat( fridge ).isNotNull(); + assertThat( fridge.getBeerCount() ).isEqualTo( 5 ); + } + + @Test + @WithClasses(ErroneousComplexMapper.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousComplexMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \"int beerCount\".*" + ) + }) + public void complexSelectionNotAllowed() { + } + + @Test + @WithClasses({ Config.class, ErroneousComplexMapperWithConfig.class }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousComplexMapperWithConfig.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \"int beerCount\".*" + ) + }) + public void complexSelectionNotAllowedWithConfig() { + } + + private FridgeDTO createFridgeDTO() { + FridgeDTO fridgeDTO = new FridgeDTO(); + ShelveDTO shelveDTO = new ShelveDTO(); + CoolBeerDTO coolBeerDTO = new CoolBeerDTO(); + fridgeDTO.setShelve( shelveDTO ); + shelveDTO.setCoolBeer( coolBeerDTO ); + coolBeerDTO.setBeerCount( "5" ); + return fridgeDTO; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MethodMapper.java new file mode 100644 index 0000000000..54e4515c52 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MethodMapper.java @@ -0,0 +1,23 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface MethodMapper { + + MethodMapper INSTANCE = Mappers.getMapper( MethodMapper.class ); + + @Mapping(target = "beerCount", source = "shelve") + Fridge map(FridgeDTO in); + + default int map(ShelveDTO in) { + return Integer.valueOf( in.getCoolBeer().getBeerCount() ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ShelveDTO.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ShelveDTO.java new file mode 100644 index 0000000000..0ddfeb7a2a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ShelveDTO.java @@ -0,0 +1,19 @@ +/* + * 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.mappingcontrol; + +public class ShelveDTO { + + private CoolBeerDTO coolBeer; + + public CoolBeerDTO getCoolBeer() { + return coolBeer; + } + + public void setCoolBeer(CoolBeerDTO coolBeer) { + this.coolBeer = coolBeer; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/UseComplex.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/UseComplex.java new file mode 100644 index 0000000000..2424d95300 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/UseComplex.java @@ -0,0 +1,18 @@ +/* + * 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.mappingcontrol; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.control.MappingControl; +import org.mapstruct.util.Experimental; + +@Retention(RetentionPolicy.CLASS) +@Experimental +@MappingControl( MappingControl.Use.COMPLEX_MAPPING ) +public @interface UseComplex { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/UseDirect.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/UseDirect.java new file mode 100644 index 0000000000..00151d71ed --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/UseDirect.java @@ -0,0 +1,18 @@ +/* + * 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.mappingcontrol; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.control.MappingControl; +import org.mapstruct.util.Experimental; + +@Retention(RetentionPolicy.CLASS) +@Experimental +@MappingControl( MappingControl.Use.DIRECT ) +public @interface UseDirect { +} From 95ceba1a1e123bd23c11fc9f103899205ff3f45d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 2 Feb 2020 00:48:45 +0100 Subject: [PATCH 024/597] #2016 Update Checkstyle to 8.29 Adapt checkstyle configuration with new changes: * Move cacheFile to Checker module * Move LineLength to Checker module * Use SuppressWithPlainTextCommentFilter --- .gitignore | 1 + .../src/main/resources/build-config/checkstyle.xml | 14 +++++++++----- parent/pom.xml | 4 ++-- .../ap/internal/conversion/SimpleConversion.java | 1 - .../mapstruct/ap/internal/model/HelperMethod.java | 2 -- .../ap/internal/model/LifecycleMethodResolver.java | 1 - .../model/ObjectFactoryMethodResolver.java | 2 -- .../ap/internal/model/common/TypeFactory.java | 2 -- .../AnnotationBasedComponentModelProcessor.java | 1 - .../ap/spi/DefaultAccessorNamingStrategy.java | 1 - .../referenced/a/ReferencedMapperDefaultOther.java | 1 - .../mapstruct/ap/test/bugs/_1170/PetMapper.java | 1 - .../org/mapstruct/ap/test/bugs/_375/Source.java | 1 - .../mapstruct/ap/test/bugs/_513/TargetElement.java | 1 - .../org/mapstruct/ap/test/bugs/_513/TargetKey.java | 1 - .../mapstruct/ap/test/bugs/_513/TargetValue.java | 1 - .../Issue913GetterMapperForCollectionsTest.java | 2 -- .../ap/test/collection/adder/PetMapper.java | 1 - .../adder/source/SingleElementSource.java | 1 - .../Source.java | 1 - .../AmbiguousBarFactory.java | 1 - .../ambiguousannotatedfactorymethod/Foo.java | 1 - .../ambiguousannotatedfactorymethod/Source.java | 1 - .../ambiguousannotatedfactorymethod/Target.java | 1 - .../test/erroneous/ambiguousfactorymethod/Foo.java | 1 - .../erroneous/ambiguousfactorymethod/Source.java | 1 - .../erroneous/ambiguousfactorymethod/Target.java | 1 - .../ambiguousfactorymethod/a/BarFactory.java | 1 - .../org/mapstruct/ap/test/generics/AbstractTo.java | 1 - .../org/mapstruct/ap/test/generics/TargetTo.java | 1 - .../_target/AdderUsageObserver.java | 1 - .../_target/ChartEntryLabel.java | 1 - .../test/nestedsourceproperties/source/Song.java | 1 - .../mapstruct/ap/test/nullcheck/MyLongWrapper.java | 1 - .../org/mapstruct/ap/test/references/Target.java | 1 - .../test/selection/primitives/PrimitiveMapper.java | 1 - .../ap/test/source/constants/Source1.java | 1 - .../ap/test/source/constants/Source2.java | 1 - .../ap/test/source/constants/Target2.java | 1 - .../org/mapstruct/ap/test/template/Source.java | 1 - .../org/mapstruct/ap/test/template/Target.java | 1 - .../ap/testutil/runner/GeneratedSource.java | 1 - .../resources/checkstyle-for-generated-sources.xml | 4 ++-- 43 files changed, 14 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 4fc554ede8..0363e0707e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ test-output # Misc. .DS_Store +checkstyle.cache diff --git a/build-config/src/main/resources/build-config/checkstyle.xml b/build-config/src/main/resources/build-config/checkstyle.xml index 9051361d18..213a0d21f5 100644 --- a/build-config/src/main/resources/build-config/checkstyle.xml +++ b/build-config/src/main/resources/build-config/checkstyle.xml @@ -15,6 +15,8 @@ --> + + @@ -60,8 +69,6 @@ - - @@ -115,9 +122,6 @@ - - - diff --git a/parent/pom.xml b/parent/pom.xml index 1960db3089..523a715112 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -28,7 +28,7 @@ 3.1.0 4.0.3.RELEASE 0.26.0 - 8.18 + 8.29 1 3.11.1 @@ -278,7 +278,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.0.0 + 3.1.0 build-config/checkstyle.xml true diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java index c143085bbc..f0e0ccd880 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java @@ -45,7 +45,6 @@ public List getRequiredHelperMethods(ConversionContext conversionC return Collections.emptyList(); } - /** * Returns the conversion string from source to target. The placeholder {@code } can be used to represent a * reference to the source value. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java index 697d01a736..fcf8068ed6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java @@ -177,8 +177,6 @@ public boolean doTypeVarsMatch(Type parameter, Type returnType) { return true; } - - /** * There's currently only one parameter foreseen instead of a list of parameter * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java index 40dbb74901..61610badf8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java @@ -73,7 +73,6 @@ public static List afterMappingMethods(Method existingVariableNames ); } - /** * @param method the method to obtain the beforeMapping methods for * @param selectionParameters method selectionParameters diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index b64dd88884..f4b39ab1a6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -50,8 +50,6 @@ public static MethodReference getFactoryMethod( Method method, return getFactoryMethod( method, method.getResultType(), selectionParameters, ctx ); } - - /** * returns a no arg factory method * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 8fc7fcdc5c..822ba0dd6d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -145,7 +145,6 @@ private Type getType(String canonicalName, boolean isLiteral) { return getType( typeElement, isLiteral ); } - /** * Determines if the type with the given full qualified name is part of the classpath * @@ -597,7 +596,6 @@ else if ( typeMirror.getKind() == TypeKind.TYPEVAR ) { return typeMirror; } - /** * Whether the given type is ready to be processed or not. It can be processed if it is not of kind * {@link TypeKind#ERROR} and all {@link AstModifyingAnnotationProcessor}s (if any) indicated that they've fully diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java index 4efb8011c3..d68936f545 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java @@ -176,7 +176,6 @@ private AnnotatedConstructor buildAnnotatedConstructorForDecorator(Decorator dec ); } - /** * Removes duplicate constructor parameter annotations. If an annotation is already present on the constructor, it * does not have be defined on the constructor parameter, too. For example, for CDI, the javax.inject.Inject diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java index c3c14deb00..82ecb4e17b 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java @@ -86,7 +86,6 @@ public boolean isGetterMethod(ExecutableElement method) { return isNonBooleanGetterName || ( isBooleanGetterName && returnTypeIsBoolean ); } - /** * Returns {@code true} when the {@link ExecutableElement} is a setter method. A setter starts with 'set'. The * remainder of the name is supposed to reflect the property name. diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/a/ReferencedMapperDefaultOther.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/a/ReferencedMapperDefaultOther.java index 81dcc306f1..7d3ab7014f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/a/ReferencedMapperDefaultOther.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/a/ReferencedMapperDefaultOther.java @@ -8,7 +8,6 @@ import org.mapstruct.ap.test.accessibility.referenced.ReferencedSource; import org.mapstruct.ap.test.accessibility.referenced.ReferencedTarget; - /** * * @author Sjaak Derksen diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1170/PetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1170/PetMapper.java index 7dd9ff50a8..96b7e8b72f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1170/PetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1170/PetMapper.java @@ -29,7 +29,6 @@ public class PetMapper { .put( 3L, "cat" ) .put( 4L, "dog" ).build(); - /** * method to be used when using an adder * diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_375/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_375/Source.java index 6f9b154033..2418fb7fdf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_375/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_375/Source.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.bugs._375; - /** * * @author Sjaak Derksen diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetElement.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetElement.java index dc794a62f3..532c44052b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetElement.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetElement.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.bugs._513; - /** * * @author Sjaak Derksen diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetKey.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetKey.java index e697afc20a..aef8b8ece1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetKey.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetKey.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.bugs._513; - /** * * @author Sjaak Derksen diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetValue.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetValue.java index d999de75a1..beda1c1389 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetValue.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/TargetValue.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.bugs._513; - /** * * @author Sjaak Derksen diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913GetterMapperForCollectionsTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913GetterMapperForCollectionsTest.java index c9c3182edc..e63d91b5e7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913GetterMapperForCollectionsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913GetterMapperForCollectionsTest.java @@ -198,8 +198,6 @@ public void shouldReturnNullForUpdateWithReturnWithPresenceChecker() { assertThat( domain2.getLongs() ).isEmpty(); } - - /** * These assert check if non-null and default mapping is working as expected. * diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/PetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/PetMapper.java index c2cf92ed6c..ef80c4b6c0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/PetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/PetMapper.java @@ -31,7 +31,6 @@ public class PetMapper { .put( 3L, "cat" ) .put( 4L, "dog" ).build(); - /** * method to be used when using an adder * diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/source/SingleElementSource.java b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/source/SingleElementSource.java index 91e277a209..01ab35a91d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/source/SingleElementSource.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/source/SingleElementSource.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.collection.adder.source; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/localdatetoxmlgregoriancalendarconversion/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/localdatetoxmlgregoriancalendarconversion/Source.java index 23fc7a5ed4..081fc1edb9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/localdatetoxmlgregoriancalendarconversion/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/localdatetoxmlgregoriancalendarconversion/Source.java @@ -7,7 +7,6 @@ import javax.xml.datatype.XMLGregorianCalendar; - /** * @author Andreas Gudian */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousBarFactory.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousBarFactory.java index cb1c30a2d6..2c6c070402 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousBarFactory.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousBarFactory.java @@ -7,7 +7,6 @@ import org.mapstruct.ObjectFactory; - /** * @author Remo Meier */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Foo.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Foo.java index 796dd0fae7..679efa8358 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Foo.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Foo.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod; - /** * @author Remo Meier */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Source.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Source.java index 8fdd2ad52e..0a4e64dc54 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Source.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod; - /** * @author Remo Meier */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Target.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Target.java index 71b5880add..f15dc0e8d6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/Target.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod; - /** * @author Remo Meier */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Foo.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Foo.java index 2373a6549f..7d13089921 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Foo.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Foo.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.erroneous.ambiguousfactorymethod; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Source.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Source.java index ea0c9a247e..2aabe7e06d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Source.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.erroneous.ambiguousfactorymethod; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Target.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Target.java index 2342d880eb..025ae810f8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/Target.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.erroneous.ambiguousfactorymethod; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/a/BarFactory.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/a/BarFactory.java index 35cb5c4462..b0e2a46e48 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/a/BarFactory.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/a/BarFactory.java @@ -7,7 +7,6 @@ import org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/generics/AbstractTo.java b/processor/src/test/java/org/mapstruct/ap/test/generics/AbstractTo.java index 5d7f0ba82f..c0f4c423ef 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/generics/AbstractTo.java +++ b/processor/src/test/java/org/mapstruct/ap/test/generics/AbstractTo.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.generics; - /** * @author Andreas Gudian */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/generics/TargetTo.java b/processor/src/test/java/org/mapstruct/ap/test/generics/TargetTo.java index 799dc2509e..cac761f14b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/generics/TargetTo.java +++ b/processor/src/test/java/org/mapstruct/ap/test/generics/TargetTo.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.generics; - /** * @author Andreas Gudian * diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/_target/AdderUsageObserver.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/_target/AdderUsageObserver.java index 96de5cd1d0..e7cae29460 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/_target/AdderUsageObserver.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/_target/AdderUsageObserver.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.nestedsourceproperties._target; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/_target/ChartEntryLabel.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/_target/ChartEntryLabel.java index bd13f1fcb1..77d2e68655 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/_target/ChartEntryLabel.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/_target/ChartEntryLabel.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.nestedsourceproperties._target; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/source/Song.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/source/Song.java index f3c2856260..f1ebe50745 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/source/Song.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/source/Song.java @@ -7,7 +7,6 @@ import java.util.List; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullcheck/MyLongWrapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullcheck/MyLongWrapper.java index d070c64941..2836f7b3dc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullcheck/MyLongWrapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullcheck/MyLongWrapper.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.nullcheck; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/Target.java b/processor/src/test/java/org/mapstruct/ap/test/references/Target.java index 53feca7fc2..78fd2df3a4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/references/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/references/Target.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.references; - /** * @author Andreas Gudian * diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/primitives/PrimitiveMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/primitives/PrimitiveMapper.java index 4ab200f9f9..aaee7eafee 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/primitives/PrimitiveMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/primitives/PrimitiveMapper.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.selection.primitives; - /** * @author Sjaak Derksen * diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/Source1.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/Source1.java index e9797fdecf..00f9d9fc45 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/Source1.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/Source1.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.source.constants; - /** * * @author Sjaak Derksen diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/Source2.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/Source2.java index eb5cc07a2e..aaad0dd735 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/Source2.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/Source2.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.source.constants; - /** * * @author Sjaak Derksen diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/Target2.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/Target2.java index e64b3f7e7e..56a1570cbf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/Target2.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/Target2.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.source.constants; - /** * * @author Sjaak Derksen diff --git a/processor/src/test/java/org/mapstruct/ap/test/template/Source.java b/processor/src/test/java/org/mapstruct/ap/test/template/Source.java index a5013e5acb..b330d98c21 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/template/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/template/Source.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.template; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/template/Target.java b/processor/src/test/java/org/mapstruct/ap/test/template/Target.java index 3d8c63faf4..5ef24faafd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/template/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/template/Target.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.test.template; - /** * @author Sjaak Derksen */ diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java index 35e3ddb0e7..f5a98bfee7 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java @@ -57,7 +57,6 @@ static void clearCompilingStatement() { GeneratedSource.compilingStatement.remove(); } - /** * Adds more mappers that need to be compared. * diff --git a/processor/src/test/resources/checkstyle-for-generated-sources.xml b/processor/src/test/resources/checkstyle-for-generated-sources.xml index eecb42111a..295fbeeffa 100644 --- a/processor/src/test/resources/checkstyle-for-generated-sources.xml +++ b/processor/src/test/resources/checkstyle-for-generated-sources.xml @@ -7,6 +7,8 @@ + + - - junit - junit - test - org.assertj assertj-core @@ -73,6 +68,17 @@ 2.6 test + + org.junit.jupiter + junit-jupiter + test + + + org.junit.jupiter + junit-jupiter-engine + test + + diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/AutoValueBuilderTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/AutoValueBuilderTest.java deleted file mode 100644 index 823a530c09..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/AutoValueBuilderTest.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Filip Hrisafov - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite(baseDir = "autoValueBuilderTest", - processorTypes = ProcessorSuite.ProcessorType.ALL_WITHOUT_PROCESSOR_PLUGIN) -public class AutoValueBuilderTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/CdiTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/CdiTest.java deleted file mode 100644 index 216d2afdfa..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/CdiTest.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Andreas Gudian - * - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite( baseDir = "cdiTest", processorTypes = ProcessorType.ALL ) -public class CdiTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/ExternalBeanJarTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/ExternalBeanJarTest.java deleted file mode 100644 index bcfb0c399c..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/ExternalBeanJarTest.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * - * See: https://github.com/mapstruct/mapstruct/issues/1121 - * - * @author Sjaak Derksen - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite(baseDir = "externalbeanjar", processorTypes = ProcessorSuite.ProcessorType.ORACLE_JAVA_8) -public class ExternalBeanJarTest { - -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FreeBuilderBuilderTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FreeBuilderBuilderTest.java deleted file mode 100644 index 250f4ce856..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FreeBuilderBuilderTest.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Filip Hrisafov - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite( baseDir = "freeBuilderBuilderTest", - processorTypes = ProcessorSuite.ProcessorType.ALL_WITHOUT_PROCESSOR_PLUGIN) -public class FreeBuilderBuilderTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java new file mode 100644 index 0000000000..c8bac6208c --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -0,0 +1,45 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.tests; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.jupiter.api.condition.JRE; +import org.mapstruct.itest.testutil.extension.ProcessorTest; + +/** + * Adds explicit exclusions of test mappers that are known or expected to not work with specific compilers. + * + * @author Andreas Gudian + */ +public final class FullFeatureCompilationExclusionCliEnhancer implements ProcessorTest.CommandLineEnhancer { + @Override + public Collection getAdditionalCommandLineArguments(ProcessorTest.ProcessorType processorType, + JRE currentJreVersion) { + List additionalExcludes = new ArrayList<>(); + + // SPI not working correctly here.. (not picked up) + additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1596/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1801/*.java" ); + + switch ( currentJreVersion ) { + case JAVA_9: + // TODO find out why this fails: + additionalExcludes.add( "org/mapstruct/ap/test/collection/wildcard/BeanMapper.java" ); + break; + default: + } + + Collection result = new ArrayList( additionalExcludes.size() ); + for ( int i = 0; i < additionalExcludes.size(); i++ ) { + result.add( "-DadditionalExclude" + i + "=" + additionalExcludes.get( i ) ); + } + + return result; + } +} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationTest.java deleted file mode 100644 index 45802ff38b..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.tests.FullFeatureCompilationTest.CompilationExclusionCliEnhancer; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.CommandLineEnhancer; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * Integration test that compiles all test mappers in the processor-module, excluding all classes that contain one of - * the following in their path/file name: - *

      - *
    • {@code /erronerous/}
    • - *
    • {@code *Erroneous*}
    • - *
    • {@code *Test.java}
    • - *
    • {@code /testutil/}
    • - *
    • possibly more, depending on the processor type - see {@link CompilationExclusionCliEnhancer}
    • - *
    - * - * @author Andreas Gudian - */ -@RunWith(ProcessorSuiteRunner.class) -@ProcessorSuite( - baseDir = "fullFeatureTest", - commandLineEnhancer = CompilationExclusionCliEnhancer.class, - processorTypes = { - ProcessorType.ORACLE_JAVA_8, - ProcessorType.ORACLE_JAVA_9, - ProcessorType.ECLIPSE_JDT_JAVA_8 -}) -public class FullFeatureCompilationTest { - /** - * Adds explicit exclusions of test mappers that are known or expected to not work with specific compilers. - * - * @author Andreas Gudian - */ - public static final class CompilationExclusionCliEnhancer implements CommandLineEnhancer { - @Override - public Collection getAdditionalCommandLineArguments(ProcessorType processorType) { - List additionalExcludes = new ArrayList<>(); - - // SPI not working correctly here.. (not picked up) - additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1596/*.java" ); - additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1801/*.java" ); - - switch ( processorType ) { - case ORACLE_JAVA_9: - // TODO find out why this fails: - additionalExcludes.add( "org/mapstruct/ap/test/collection/wildcard/BeanMapper.java" ); - break; - default: - } - - Collection result = new ArrayList( additionalExcludes.size() ); - for ( int i = 0; i < additionalExcludes.size(); i++ ) { - result.add( "-DadditionalExclude" + i + "=" + additionalExcludes.get( i ) ); - } - - return result; - } - } -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java index bb0c0f4ce6..0162582468 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java @@ -5,12 +5,6 @@ */ package org.mapstruct.itest.tests; -import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; -import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE; -import static org.hamcrest.core.StringContains.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; - import java.io.File; import java.io.IOException; import java.nio.charset.Charset; @@ -24,40 +18,40 @@ import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; import org.gradle.testkit.runner.TaskOutcome; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.condition.DisabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.runners.Parameterized.Parameters; +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE; +import static org.hamcrest.core.StringContains.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + /** *

    This is supposed to be run from the mapstruct root project folder. * Otherwise, use -Dmapstruct_root=path_to_project. */ -@RunWith( Parameterized.class ) +@DisabledForJreRange(min = JRE.JAVA_11) public class GradleIncrementalCompilationTest { private static Path rootPath; private static String projectDir = "integrationtest/src/test/resources/gradleIncrementalCompilationTest"; private static String compileTaskName = "compileJava"; - @Rule - public final TemporaryFolder testBuildDir = new TemporaryFolder(); - @Rule - public final TemporaryFolder testProjectDir = new TemporaryFolder(); + @TempDir + File testBuildDir; + @TempDir + File testProjectDir; - private String gradleVersion; private GradleRunner runner; private File sourceDirectory; private List compileArgs; // Gradle compile task arguments - public GradleIncrementalCompilationTest(String gradleVersion) { - this.gradleVersion = gradleVersion; - } - - @Parameters( name = "Gradle {0}" ) + @Parameters(name = "Gradle {0}") public static List gradleVersions() { return Arrays.asList( "5.0", "6.0" ); } @@ -81,49 +75,62 @@ private void assertRecompiled(BuildResult result, int recompiledCount) { assertCompileOutcome( result, recompiledCount > 0 ? SUCCESS : UP_TO_DATE ); assertThat( result.getOutput(), - containsString( String.format( "Incremental compilation of %d classes completed", recompiledCount ) ) ); + containsString( String.format( "Incremental compilation of %d classes completed", recompiledCount ) ) + ); } private List buildCompileArgs() { // Make Gradle use the temporary build folder by overriding the buildDir property - String buildDirPropertyArg = "-PbuildDir=" + testBuildDir.getRoot().getAbsolutePath(); + String buildDirPropertyArg = "-PbuildDir=" + testBuildDir.getAbsolutePath(); // Inject the path to the folder containing the mapstruct-processor JAR String jarDirectoryArg = "-PmapstructRootPath=" + rootPath.toString(); return Arrays.asList( compileTaskName, buildDirPropertyArg, jarDirectoryArg ); } - @BeforeClass + @BeforeAll public static void setupClass() throws Exception { rootPath = Paths.get( System.getProperty( "mapstruct_root", "." ) ).toAbsolutePath(); } - @Before - public void setup() throws IOException { + public void setup(String gradleVersion) throws IOException { + if ( !testBuildDir.exists() ) { + testBuildDir.mkdirs(); + } + + if ( !testProjectDir.exists() ) { + testProjectDir.mkdirs(); + } // Copy test project files to the temp dir Path gradleProjectPath = rootPath.resolve( projectDir ); - FileUtils.copyDirectory( gradleProjectPath.toFile(), testProjectDir.getRoot() ); + FileUtils.copyDirectory( gradleProjectPath.toFile(), testProjectDir ); compileArgs = buildCompileArgs(); - sourceDirectory = new File( testProjectDir.getRoot(), "src/main/java" ); - runner = GradleRunner.create().withGradleVersion( gradleVersion ).withProjectDir( testProjectDir.getRoot() ); + sourceDirectory = new File( testProjectDir, "src/main/java" ); + runner = GradleRunner.create().withGradleVersion( gradleVersion ).withProjectDir( testProjectDir ); } - @Test - public void testBuildSucceeds() throws IOException { + @ParameterizedTest + @MethodSource("gradleVersions") + public void testBuildSucceeds(String gradleVersion) throws IOException { + setup( gradleVersion ); // Make sure the test build setup actually compiles BuildResult buildResult = getRunner().build(); assertCompileOutcome( buildResult, SUCCESS ); } - @Test - public void testUpToDate() throws IOException { + @ParameterizedTest + @MethodSource("gradleVersions") + public void testUpToDate(String gradleVersion) throws IOException { + setup( gradleVersion ); getRunner().build(); BuildResult secondBuildResult = getRunner().build(); assertCompileOutcome( secondBuildResult, UP_TO_DATE ); } - @Test - public void testChangeConstant() throws IOException { + @ParameterizedTest + @MethodSource("gradleVersions") + public void testChangeConstant(String gradleVersion) throws IOException { + setup( gradleVersion ); getRunner().build(); // Change return value in class Target File targetFile = new File( sourceDirectory, "org/mapstruct/itest/gradle/model/Target.java" ); @@ -134,8 +141,10 @@ public void testChangeConstant() throws IOException { assertRecompiled( secondBuildResult, 3 ); } - @Test - public void testChangeTargetField() throws IOException { + @ParameterizedTest + @MethodSource("gradleVersions") + public void testChangeTargetField(String gradleVersion) throws IOException { + setup( gradleVersion ); getRunner().build(); // Change target field in mapper interface File mapperFile = new File( sourceDirectory, "org/mapstruct/itest/gradle/lib/TestMapper.java" ); @@ -146,8 +155,10 @@ public void testChangeTargetField() throws IOException { assertRecompiled( secondBuildResult, 2 ); } - @Test - public void testChangeUnrelatedFile() throws IOException { + @ParameterizedTest + @MethodSource("gradleVersions") + public void testChangeUnrelatedFile(String gradleVersion) throws IOException { + setup( gradleVersion ); getRunner().build(); File unrelatedFile = new File( sourceDirectory, "org/mapstruct/itest/gradle/lib/UnrelatedComponent.java" ); replaceInFile( unrelatedFile, "true", "false" ); diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/ImmutablesBuilderTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/ImmutablesBuilderTest.java deleted file mode 100644 index b6f6970d38..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/ImmutablesBuilderTest.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Filip Hrisafov - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite( baseDir = "immutablesBuilderTest", - processorTypes = ProcessorSuite.ProcessorType.ALL_WITHOUT_PROCESSOR_PLUGIN) -public class ImmutablesBuilderTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/Java8Test.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/Java8Test.java deleted file mode 100644 index ce0b191f65..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/Java8Test.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Andreas Gudian - * - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite( baseDir = "java8Test", processorTypes = ProcessorType.ALL_JAVA_8 ) -public class Java8Test { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/JaxbTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/JaxbTest.java deleted file mode 100644 index 840f5d9337..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/JaxbTest.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Andreas Gudian - * - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite( baseDir = "jaxbTest", processorTypes = ProcessorType.ALL ) -public class JaxbTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/Jsr330Test.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/Jsr330Test.java deleted file mode 100644 index 65cd76f0d3..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/Jsr330Test.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Andreas Gudian - * - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite( baseDir = "jsr330Test", processorTypes = ProcessorType.ALL ) -public class Jsr330Test { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/LombokBuilderTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/LombokBuilderTest.java deleted file mode 100644 index 7199cb1f34..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/LombokBuilderTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Eric Martineau - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite( baseDir = "lombokBuilderTest", - processorTypes = { - ProcessorSuite.ProcessorType.ORACLE_JAVA_8, - ProcessorSuite.ProcessorType.ORACLE_JAVA_9, - } -) -public class LombokBuilderTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java new file mode 100644 index 0000000000..16298179a0 --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -0,0 +1,124 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.tests; + +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.mapstruct.itest.testutil.extension.ProcessorTest; + +/** + * @author Filip Hrisafov + */ +@Execution( ExecutionMode.CONCURRENT ) +public class MavenIntegrationTest { + + @ProcessorTest(baseDir = "autoValueBuilderTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC, + ProcessorTest.ProcessorType.ECLIPSE_JDT + }) + void autoValueBuilderTest() { + } + + @ProcessorTest(baseDir = "cdiTest") + void cdiTest() { + } + + /** + * See: https://github.com/mapstruct/mapstruct/issues/1121 + */ + @ProcessorTest(baseDir = "externalbeanjar", processorTypes = ProcessorTest.ProcessorType.JAVAC) + void externalBeanJarTest() { + } + + @ProcessorTest(baseDir = "freeBuilderBuilderTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC, + ProcessorTest.ProcessorType.ECLIPSE_JDT + }) + void freeBuilderBuilderTest() { + } + + /** + * Integration test that compiles all test mappers in the processor-module, excluding all classes that contain + * one of + * the following in their path/file name: + *

      + *
    • {@code /erronerous/}
    • + *
    • {@code *Erroneous*}
    • + *
    • {@code *Test.java}
    • + *
    • {@code /testutil/}
    • + *
    • possibly more, depending on the processor type - see {@link FullFeatureCompilationExclusionCliEnhancer}
    • + *
    + */ + @ProcessorTest(baseDir = "fullFeatureTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC, + ProcessorTest.ProcessorType.ECLIPSE_JDT + }, commandLineEnhancer = FullFeatureCompilationExclusionCliEnhancer.class) + void fullFeatureTest() { + } + + @ProcessorTest(baseDir = "immutablesBuilderTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC, + ProcessorTest.ProcessorType.ECLIPSE_JDT + }) + void immutablesBuilderTest() { + } + + @ProcessorTest(baseDir = "java8Test") + void java8Test() { + } + + @ProcessorTest(baseDir = "jaxbTest") + void jaxbTest() { + } + + @ProcessorTest(baseDir = "jsr330Test") + void jsr330Test() { + } + + @ProcessorTest(baseDir = "lombokBuilderTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC + }) + void lombokBuilderTest() { + } + + @ProcessorTest(baseDir = "namingStrategyTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC + }) + void namingStrategyTest() { + } + + /** + * ECLIPSE_JDT is not working with Protobuf. Use all other available processor types. + */ + @ProcessorTest(baseDir = "protobufBuilderTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC + }) + void protobufBuilderTest() { + } + + @ProcessorTest(baseDir = "simpleTest") + void simpleTest() { + } + + @ProcessorTest(baseDir = "springTest") + void springTest() { + } + + /** + * Tests usage of MapStruct with another processor that generates supertypes of mapping source/target types. + */ + @ProcessorTest(baseDir = "superTypeGenerationTest", processorTypes = ProcessorTest.ProcessorType.JAVAC) + void superTypeGenerationTest() { + } + + /** + * Tests usage of MapStruct with another processor that generates the target type of a mapping method. + */ + @ProcessorTest(baseDir = "targetTypeGenerationTest", processorTypes = ProcessorTest.ProcessorType.JAVAC) + void targetTypeGenerationTest() { + } + +} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/NamingStrategyTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/NamingStrategyTest.java deleted file mode 100644 index 55d06a3f14..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/NamingStrategyTest.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Andreas Gudian - * - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite(baseDir = "namingStrategyTest", processorTypes = ProcessorType.ORACLE_JAVA_8) -public class NamingStrategyTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/ProtobufBuilderTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/ProtobufBuilderTest.java deleted file mode 100644 index cf648ca3c8..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/ProtobufBuilderTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * ECLIPSE_JDT_JAVA_8 is not working with Protobuf. Use all other available processor types. - * - * @author Christian Bandowski - */ -@RunWith(ProcessorSuiteRunner.class) -@ProcessorSuite(baseDir = "protobufBuilderTest", - processorTypes = { - ProcessorSuite.ProcessorType.ORACLE_JAVA_8, - ProcessorSuite.ProcessorType.ORACLE_JAVA_9, - }) -public class ProtobufBuilderTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/SimpleTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/SimpleTest.java deleted file mode 100644 index bbcd7a529d..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/SimpleTest.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Andreas Gudian - * - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite( baseDir = "simpleTest", processorTypes = ProcessorType.ALL ) -public class SimpleTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/SpringTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/SpringTest.java deleted file mode 100644 index 1eca9383a2..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/SpringTest.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * @author Andreas Gudian - * - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite( baseDir = "springTest", processorTypes = ProcessorType.ALL ) -public class SpringTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/SuperTypeGenerationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/SuperTypeGenerationTest.java deleted file mode 100644 index b39f8e93d9..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/SuperTypeGenerationTest.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * Tests usage of MapStruct with another processor that generates supertypes of mapping source/target types. - * - * @author Gunnar Morling - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite(baseDir = "superTypeGenerationTest", processorTypes = ProcessorType.ORACLE_JAVA_8) -public class SuperTypeGenerationTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/TargetTypeGenerationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/TargetTypeGenerationTest.java deleted file mode 100644 index 2cbd78f0f2..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/TargetTypeGenerationTest.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.tests; - -import org.junit.runner.RunWith; -import org.mapstruct.itest.testutil.runner.ProcessorSuite; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; - -/** - * Tests usage of MapStruct with another processor that generates the target type of a mapping method. - * - * @author Gunnar Morling - */ -@RunWith( ProcessorSuiteRunner.class ) -@ProcessorSuite(baseDir = "targetTypeGenerationTest", processorTypes = ProcessorType.ORACLE_JAVA_8) -public class TargetTypeGenerationTest { -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorEnabledOnJreCondition.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorEnabledOnJreCondition.java new file mode 100644 index 0000000000..a651e55f93 --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorEnabledOnJreCondition.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.itest.testutil.extension; + +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; + +import static org.mapstruct.itest.testutil.extension.ProcessorTestTemplateInvocationContext.CURRENT_VERSION; + +/** + * @author Filip Hrisafov + */ +public class ProcessorEnabledOnJreCondition implements ExecutionCondition { + + static final ConditionEvaluationResult ENABLED_ON_CURRENT_JRE = + ConditionEvaluationResult.enabled( "Enabled on JRE version: " + System.getProperty( "java.version" ) ); + + static final ConditionEvaluationResult DISABLED_ON_CURRENT_JRE = + ConditionEvaluationResult.disabled( "Disabled on JRE version: " + System.getProperty( "java.version" ) ); + + public ProcessorEnabledOnJreCondition(ProcessorTest.ProcessorType processorType) { + this.processorType = processorType; + } + + protected final ProcessorTest.ProcessorType processorType; + + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + // If the max JRE is greater or equal to the current version the test is enabled + return processorType.maxJre().compareTo( CURRENT_VERSION ) >= 0 ? ENABLED_ON_CURRENT_JRE : + DISABLED_ON_CURRENT_JRE; + } +} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java new file mode 100644 index 0000000000..4cf0738eb1 --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java @@ -0,0 +1,207 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.testutil.extension; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.maven.it.Verifier; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.InvocationInterceptor; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; +import org.junit.platform.commons.util.ReflectionUtils; + +import static org.apache.maven.it.util.ResourceExtractor.extractResourceToDestination; +import static org.apache.maven.shared.utils.io.FileUtils.copyURLToFile; +import static org.apache.maven.shared.utils.io.FileUtils.deleteDirectory; +import static org.mapstruct.itest.testutil.extension.ProcessorTestTemplateInvocationContext.CURRENT_VERSION; + +/** + * @author Filip Hrisafov + * @author Andreas Gudian + */ +public class ProcessorInvocationInterceptor implements InvocationInterceptor { + + /** + * System property to enable remote debugging of the processor execution in the integration test + */ + public static final String SYS_PROP_DEBUG = "processorIntegrationTest.debug"; + + private final ProcessorTestContext processorTestContext; + + public ProcessorInvocationInterceptor(ProcessorTestContext processorTestContext) { + this.processorTestContext = processorTestContext; + } + + @Override + public void interceptTestTemplateMethod(Invocation invocation, + ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { + try { + doExecute( extensionContext ); + invocation.proceed(); + } + catch ( Exception e ) { + invocation.skip(); + throw e; + } + } + + private void doExecute(ExtensionContext extensionContext) throws Exception { + File destination = extractTest( extensionContext ); + PrintStream originalOut = System.out; + + final Verifier verifier; + if ( Boolean.getBoolean( SYS_PROP_DEBUG ) ) { + // the compiler is executed within the Maven JVM. So make + // sure we fork a new JVM for that, and let that new JVM use the command 'mvnDebug' instead of 'mvn' + verifier = new Verifier( destination.getCanonicalPath(), null, true, true ); + verifier.setDebugJvm( true ); + } + else { + verifier = new Verifier( destination.getCanonicalPath() ); + } + + List goals = new ArrayList<>( 3 ); + + goals.add( "clean" ); + + try { + configureProcessor( verifier ); + + verifier.addCliOption( "-Dcompiler-source-target-version=" + sourceTargetVersion() ); + + if ( Boolean.getBoolean( SYS_PROP_DEBUG ) ) { + originalOut.print( "Processor Integration Test: " ); + originalOut.println( "Listening for transport dt_socket at address: 8000 (in some seconds)" ); + } + + goals.add( "test" ); + + addAdditionalCliArguments( verifier ); + + originalOut.println( extensionContext.getRequiredTestClass().getSimpleName() + "." + + extensionContext.getRequiredTestMethod().getName() + " executing " + + processorTestContext.getProcessor().name().toLowerCase() ); + + verifier.executeGoals( goals ); + verifier.verifyErrorFreeLog(); + } + finally { + verifier.resetStreams(); + } + } + + private void addAdditionalCliArguments(Verifier verifier) + throws Exception { + Class cliEnhancerClass = + processorTestContext.getCliEnhancerClass(); + + Constructor cliEnhancerConstructor = null; + if ( cliEnhancerClass != ProcessorTest.CommandLineEnhancer.class ) { + try { + cliEnhancerConstructor = cliEnhancerClass.getConstructor(); + ProcessorTest.CommandLineEnhancer enhancer = cliEnhancerConstructor.newInstance(); + Collection additionalArgs = enhancer.getAdditionalCommandLineArguments( + processorTestContext.getProcessor(), CURRENT_VERSION ); + + for ( String arg : additionalArgs ) { + verifier.addCliOption( arg ); + } + + } + catch ( NoSuchMethodException e ) { + throw new RuntimeException( cliEnhancerClass + " does not have a default constructor." ); + } + catch ( SecurityException e ) { + throw new RuntimeException( e ); + } + } + } + + private void configureProcessor(Verifier verifier) { + String compilerId = processorTestContext.getProcessor().getCompilerId(); + if ( compilerId != null ) { + verifier.addCliOption( "-Pgenerate-via-compiler-plugin" ); + verifier.addCliOption( "-Dcompiler-id=" + compilerId ); + } + else { + verifier.addCliOption( "-Pgenerate-via-processor-plugin" ); + } + } + + private File extractTest(ExtensionContext extensionContext) throws IOException { + String tmpDir = getTmpDir(); + + String tempDirName = extensionContext.getRequiredTestClass().getPackage().getName() + "." + + extensionContext.getRequiredTestMethod().getName(); + File tempDirBase = new File( tmpDir, tempDirName ).getCanonicalFile(); + + if ( !tempDirBase.exists() ) { + tempDirBase.mkdirs(); + } + + File parentPom = new File( tempDirBase, "pom.xml" ); + copyURLToFile( getClass().getResource( "/pom.xml" ), parentPom ); + + ProcessorTest.ProcessorType processorType = processorTestContext.getProcessor(); + File tempDir = new File( tempDirBase, processorType.name().toLowerCase() ); + deleteDirectory( tempDir ); + + return extractResourceToDestination( getClass(), "/" + processorTestContext.getBaseDir(), tempDir, true ); + } + + private String getTmpDir() { + if ( CURRENT_VERSION == JRE.JAVA_8 ) { + // On Java 8 the tmp dir is always + // no matter we run from the aggregator or not + return "target/tmp"; + } + + // On Java 11+ we need to do it base on the location relative + String tmpDir; + if ( Files.exists( Paths.get( "integrationtest" ) ) ) { + // If it exists then we are running from the main aggregator + tmpDir = "integrationtest/target/tmp"; + } + else { + tmpDir = "target/tmp"; + } + return tmpDir; + } + + private String sourceTargetVersion() { + if ( CURRENT_VERSION == JRE.JAVA_8 ) { + return "1.8"; + } + else if ( CURRENT_VERSION == JRE.OTHER ) { + try { + // java.lang.Runtime.version() is a static method available on Java 9+ + // that returns an instance of java.lang.Runtime.Version which has the + // following method: public int major() + Method versionMethod = null; + versionMethod = Runtime.class.getMethod( "version" ); + Object version = ReflectionUtils.invokeMethod( versionMethod, null ); + Method majorMethod = version.getClass().getMethod( "major" ); + return String.valueOf( (int) ReflectionUtils.invokeMethod( majorMethod, version ) ); + } + catch ( NoSuchMethodException e ) { + throw new RuntimeException( "Failed to get Java Version" ); + } + } + else { + return CURRENT_VERSION.name().substring( 5 ); + } + } +} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java new file mode 100644 index 0000000000..1db77ae6b2 --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java @@ -0,0 +1,109 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.testutil.extension; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Collection; + +import org.apache.maven.it.Verifier; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Declares the content of the integration test. + *

    + * {@link #baseDir()} must be a path in the classpath that contains the maven module to run as integration test. The + * integration test module should contain at least one test class. The integration test passes, if + * {@code mvn clean test} finishes successfully. + *

    + * {@link #processorTypes()} configures the variants to execute the integration tests with. See + * {@link ProcessorType}. + * + * @author Filip Hrisafov + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@TestTemplate +@ExtendWith(ProcessorTestTemplateInvocationContextProvider.class) +public @interface ProcessorTest { + + /** + * Describes the type of the processing variant(s) to use when executing the integration test. + * + * @author Filip Hrisafov + */ + enum ProcessorType { + + JAVAC( "javac" ), + + ECLIPSE_JDT( "jdt", JRE.JAVA_8 ), + + PROCESSOR_PLUGIN( null, JRE.JAVA_8 ); + + private final String compilerId; + private final JRE max; + + ProcessorType(String compilerId) { + this( compilerId, JRE.OTHER ); + } + + ProcessorType(String compilerId, JRE max) { + this.compilerId = compilerId; + this.max = max; + } + + public String getCompilerId() { + return compilerId; + } + + public JRE maxJre() { + return max; + } + } + + /** + * Can be configured to provide additional command line arguments for the invoked Maven process, depending on the + * {@link ProcessorType} the test is executed for. + * + * @author Andreas Gudian + */ + interface CommandLineEnhancer { + /** + * @param processorType the processor type for which the test is executed. + * @param currentJreVersion the current JRE version + * + * @return additional command line arguments to be passed to the Maven {@link Verifier}. + */ + Collection getAdditionalCommandLineArguments(ProcessorType processorType, + JRE currentJreVersion); + } + + + /** + * @return a path in the classpath that contains the maven module to run as integration test: {@code mvn clean test} + */ + String baseDir(); + + /** + * @return the variants to execute the integration tests with. See {@link ProcessorType}. + */ + ProcessorType[] processorTypes() default { + ProcessorType.JAVAC, + ProcessorType.ECLIPSE_JDT, + ProcessorType.PROCESSOR_PLUGIN + }; + + /** + * @return the {@link CommandLineEnhancer} implementation. Must have a default constructor. + */ + Class commandLineEnhancer() default CommandLineEnhancer.class; +} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestContext.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestContext.java new file mode 100644 index 0000000000..be1e4647c7 --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestContext.java @@ -0,0 +1,36 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.testutil.extension; + +/** + * @author Filip Hrisafov + */ +public class ProcessorTestContext { + + private final String baseDir; + private final ProcessorTest.ProcessorType processor; + private final Class cliEnhancerClass; + + public ProcessorTestContext(String baseDir, + ProcessorTest.ProcessorType processor, + Class cliEnhancerClass) { + this.baseDir = baseDir; + this.processor = processor; + this.cliEnhancerClass = cliEnhancerClass; + } + + public String getBaseDir() { + return baseDir; + } + + public ProcessorTest.ProcessorType getProcessor() { + return processor; + } + + public Class getCliEnhancerClass() { + return cliEnhancerClass; + } +} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContext.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContext.java new file mode 100644 index 0000000000..47e692988d --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContext.java @@ -0,0 +1,52 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.testutil.extension; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; + +/** + * @author Filip Hrisafov + */ +public class ProcessorTestTemplateInvocationContext implements TestTemplateInvocationContext { + + static final JRE CURRENT_VERSION; + + static { + JRE currentVersion = JRE.OTHER; + for ( JRE jre : JRE.values() ) { + if ( jre.isCurrentVersion() ) { + currentVersion = jre; + break; + } + } + + CURRENT_VERSION = currentVersion; + } + + private final ProcessorTestContext processorTestContext; + + public ProcessorTestTemplateInvocationContext(ProcessorTestContext processorTestContext) { + this.processorTestContext = processorTestContext; + } + + @Override + public String getDisplayName(int invocationIndex) { + return processorTestContext.getProcessor().name().toLowerCase(); + } + + @Override + public List getAdditionalExtensions() { + List extensions = new ArrayList<>(); + extensions.add( new ProcessorEnabledOnJreCondition( processorTestContext.getProcessor() ) ); + extensions.add( new ProcessorInvocationInterceptor( processorTestContext ) ); + return extensions; + } +} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContextProvider.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContextProvider.java new file mode 100644 index 0000000000..b7a4f331cf --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContextProvider.java @@ -0,0 +1,40 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.testutil.extension; + +import java.lang.reflect.Method; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; +import org.junit.platform.commons.support.AnnotationSupport; + +/** + * @author Filip Hrisafov + */ +public class ProcessorTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider { + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return AnnotationSupport.isAnnotated( context.getTestMethod(), ProcessorTest.class ); + } + + @Override + public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { + + Method testMethod = context.getRequiredTestMethod(); + ProcessorTest processorTest = AnnotationSupport.findAnnotation( testMethod, ProcessorTest.class ) + .orElseThrow( () -> new RuntimeException( "Failed to get ProcessorTest on " + testMethod ) ); + + + return Stream.of( processorTest.processorTypes() ) + .map( processorType -> new ProcessorTestTemplateInvocationContext( new ProcessorTestContext( + processorTest.baseDir(), + processorType, + processorTest.commandLineEnhancer() + ) ) ); + } +} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/ProcessorSuite.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/ProcessorSuite.java deleted file mode 100644 index 916d513954..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/ProcessorSuite.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.testutil.runner; - -import org.apache.maven.it.Verifier; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Collection; - -/** - * Declares the content of the integration test. - *

    - * {@link #baseDir()} must be a path in the classpath that contains the maven module to run as integration test. The - * integration test module should contain at least one test class. The integration test passes, if - * {@code mvn clean test} finishes successfully. - *

    - * {@link #processorTypes()} configures the variants to execute the integration tests with. See {@link ProcessorType}. - * - * @author Andreas Gudian - */ -@Retention( RetentionPolicy.RUNTIME ) -@Documented -@Target( ElementType.TYPE ) -public @interface ProcessorSuite { - /** - * Describes the type of the processing variant(s) to use when executing the integration test. - *

    - * Types that require toolchains, will - * need the toolchains.xml file to be either installed in ~/m2, or alternatively passed to the mvn process using - * {@code mvn -DprocessorIntegrationTest.toolchainsFile=/path/to/toolchains.xml ...} - * - * @author Andreas Gudian - */ - enum ProcessorType { - - /** - * Use the same JDK that runs the mvn build to perform the processing - */ - ORACLE_JAVA_8( null, "javac", "1.8" ), - - /** - * Use an Oracle JDK 1.9 (or 1.9.x) via toolchain support to perform the processing - */ - ORACLE_JAVA_9( new Toolchain( "oracle", "9", "10" ), "javac", "1.9" ), - - /** - * Use the eclipse compiler with 1.8 source/target level from tycho-compiler-jdt to perform the build and - * processing - */ - ECLIPSE_JDT_JAVA_8( null, "jdt", "1.8" ), - - /** - * Use the maven-processor-plugin with 1.8 source/target level with the same JDK that runs the mvn build to - * perform the processing - */ - PROCESSOR_PLUGIN_JAVA_8( null, null, "1.8" ), - - /** - * Use all processing variants, but without the maven-procesor-plugin - */ - ALL_WITHOUT_PROCESSOR_PLUGIN( ORACLE_JAVA_8, ORACLE_JAVA_9, ECLIPSE_JDT_JAVA_8), - - /** - * Use all available processing variants - */ - ALL( ORACLE_JAVA_8, ORACLE_JAVA_9, ECLIPSE_JDT_JAVA_8, PROCESSOR_PLUGIN_JAVA_8 ), - - /** - * Use all JDK8 compatible processing variants - */ - ALL_JAVA_8( ORACLE_JAVA_8, ECLIPSE_JDT_JAVA_8, PROCESSOR_PLUGIN_JAVA_8 ); - - private ProcessorType[] included = { }; - - private Toolchain toolchain; - private String compilerId; - private String sourceTargetVersion; - - ProcessorType(Toolchain toolchain, String compilerId, String sourceTargetVersion) { - this.toolchain = toolchain; - this.compilerId = compilerId; - this.sourceTargetVersion = sourceTargetVersion; - } - - ProcessorType(ProcessorType... included) { - this.included = included; - } - - /** - * @return the processor types that are grouped by this type - */ - public ProcessorType[] getIncluded() { - return included; - } - - /** - * @return the toolchain - */ - public Toolchain getToolchain() { - return toolchain; - } - - /** - * @return the compilerId - */ - public String getCompilerId() { - return compilerId; - } - - /** - * @return the sourceTargetVersion - */ - public String getSourceTargetVersion() { - return sourceTargetVersion; - } - } - - /** - * Can be configured to provide additional command line arguments for the invoked Maven process, depending on the - * {@link ProcessorType} the test is executed for. - * - * @author Andreas Gudian - */ - interface CommandLineEnhancer { - /** - * @param processorType the processor type for which the test is executed. - * @return additional command line arguments to be passed to the Maven {@link Verifier}. - */ - Collection getAdditionalCommandLineArguments(ProcessorType processorType); - } - - /** - * @return a path in the classpath that contains the maven module to run as integration test: {@code mvn clean test} - */ - String baseDir(); - - /** - * @return the variants to execute the integration tests with. See {@link ProcessorType}. - */ - ProcessorType[] processorTypes() default { ProcessorType.ALL }; - - /** - * @return the {@link CommandLineEnhancer} implementation. Must have a default constructor. - */ - Class commandLineEnhancer() default CommandLineEnhancer.class; -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/ProcessorSuiteRunner.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/ProcessorSuiteRunner.java deleted file mode 100644 index cb3f601b21..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/ProcessorSuiteRunner.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.testutil.runner; - -import static org.apache.maven.it.util.ResourceExtractor.extractResourceToDestination; -import static org.apache.maven.shared.utils.io.FileUtils.copyURLToFile; -import static org.apache.maven.shared.utils.io.FileUtils.deleteDirectory; - -import java.io.File; -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; - -import org.apache.maven.it.Verifier; -import org.junit.internal.AssumptionViolatedException; -import org.junit.internal.runners.model.EachTestNotifier; -import org.junit.runner.Description; -import org.junit.runner.notification.RunNotifier; -import org.junit.runner.notification.StoppedByUserException; -import org.junit.runners.ParentRunner; -import org.junit.runners.model.InitializationError; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.CommandLineEnhancer; -import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; -import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner.ProcessorTestCase; -import org.mapstruct.itest.testutil.runner.xml.Toolchains; -import org.mapstruct.itest.testutil.runner.xml.Toolchains.ProviderDescription; - -/** - * Runner for processor integration tests. Requires the annotation {@link ProcessorSuite} on the test class. - * - * @author Andreas Gudian - */ -public class ProcessorSuiteRunner extends ParentRunner { - - /** - * System property for specifying the location of the toolchains.xml file - */ - public static final String SYS_PROP_TOOLCHAINS_FILE = "processorIntegrationTest.toolchainsFile"; - - /** - * System property to enable remote debugging of the processor execution in the integration test - */ - public static final String SYS_PROP_DEBUG = "processorIntegrationTest.debug"; - - private static final File TOOLCHAINS_FILE = getToolchainsFile(); - - private static final Collection ENABLED_PROCESSOR_TYPES = detectSupportedTypes( TOOLCHAINS_FILE ); - - public static final class ProcessorTestCase { - private final String baseDir; - private final ProcessorType processor; - private final boolean ignored; - private final Constructor cliEnhancerConstructor; - - public ProcessorTestCase(String baseDir, ProcessorType processor, - Constructor cliEnhancerConstructor) { - this.baseDir = baseDir; - this.processor = processor; - this.cliEnhancerConstructor = cliEnhancerConstructor; - this.ignored = !ENABLED_PROCESSOR_TYPES.contains( processor ); - } - } - - private final List methods; - - /** - * @param clazz the test class - * @throws InitializationError in case the initialization fails - */ - public ProcessorSuiteRunner(Class clazz) throws InitializationError { - super( clazz ); - - ProcessorSuite suite = clazz.getAnnotation( ProcessorSuite.class ); - - if ( null == suite ) { - throw new InitializationError( "The test class must be annotated with " + ProcessorSuite.class.getName() ); - } - - if ( suite.processorTypes().length == 0 ) { - throw new InitializationError( "ProcessorSuite#processorTypes must not be empty" ); - } - - Constructor cliEnhancerConstructor = null; - if ( suite.commandLineEnhancer() != CommandLineEnhancer.class ) { - try { - cliEnhancerConstructor = suite.commandLineEnhancer().getConstructor(); - } - catch ( NoSuchMethodException e ) { - throw new InitializationError( - suite.commandLineEnhancer().getName() + " does not have a default constructor." ); - } - catch ( SecurityException e ) { - throw new InitializationError( e ); - } - } - - methods = initializeTestCases( suite, cliEnhancerConstructor ); - } - - private List initializeTestCases(ProcessorSuite suite, - Constructor cliEnhancerConstructor) { - List types = new ArrayList<>(); - - for ( ProcessorType compiler : suite.processorTypes() ) { - if ( compiler.getIncluded().length > 0 ) { - types.addAll( Arrays.asList( compiler.getIncluded() ) ); - } - else { - types.add( compiler ); - } - } - - List result = new ArrayList( types.size() ); - - for ( ProcessorType type : types ) { - result.add( new ProcessorTestCase( suite.baseDir(), type, cliEnhancerConstructor ) ); - } - - return result; - } - - @Override - protected List getChildren() { - return methods; - } - - @Override - protected Description describeChild(ProcessorTestCase child) { - return Description.createTestDescription( getTestClass().getJavaClass(), child.processor.name().toLowerCase() ); - } - - @Override - protected void runChild(ProcessorTestCase child, RunNotifier notifier) { - Description description = describeChild( child ); - EachTestNotifier testNotifier = new EachTestNotifier( notifier, description ); - - if ( child.ignored ) { - testNotifier.fireTestIgnored(); - } - else { - try { - testNotifier.fireTestStarted(); - doExecute( child, description ); - } - catch ( AssumptionViolatedException e ) { - testNotifier.fireTestIgnored(); - } - catch ( StoppedByUserException e ) { - throw e; - } - catch ( Throwable e ) { - testNotifier.addFailure( e ); - } - finally { - testNotifier.fireTestFinished(); - } - } - } - - private void doExecute(ProcessorTestCase child, Description description) throws Exception { - File destination = extractTest( child, description ); - PrintStream originalOut = System.out; - - final Verifier verifier; - if ( Boolean.getBoolean( SYS_PROP_DEBUG ) ) { - if ( child.processor.getToolchain() == null ) { - // when not using toolchains for a test, then the compiler is executed within the Maven JVM. So make - // sure we fork a new JVM for that, and let that new JVM use the command 'mvnDebug' instead of 'mvn' - verifier = new Verifier( destination.getCanonicalPath(), null, true, true ); - verifier.setDebugJvm( true ); - } - else { - verifier = new Verifier( destination.getCanonicalPath() ); - verifier.addCliOption( "-Pdebug-forked-javac" ); - } - } - else { - verifier = new Verifier( destination.getCanonicalPath() ); - } - - List goals = new ArrayList<>( 3 ); - - goals.add( "clean" ); - - try { - configureToolchains( child, verifier, goals, originalOut ); - configureProcessor( child, verifier ); - - verifier.addCliOption( "-Dcompiler-source-target-version=" + child.processor.getSourceTargetVersion() ); - - verifier.addCliOption( "-Dmapstruct-artifact-id=mapstruct" ); - - if ( Boolean.getBoolean( SYS_PROP_DEBUG ) ) { - originalOut.print( "Processor Integration Test: " ); - originalOut.println( "Listening for transport dt_socket at address: 8000 (in some seconds)" ); - } - - goals.add( "test" ); - - addAdditionalCliArguments( child, verifier ); - - originalOut.println( "executing " + child.processor.name().toLowerCase() ); - - verifier.executeGoals( goals ); - verifier.verifyErrorFreeLog(); - } - finally { - verifier.resetStreams(); - } - } - - private void addAdditionalCliArguments(ProcessorTestCase child, final Verifier verifier) throws Exception { - if ( child.cliEnhancerConstructor != null ) { - CommandLineEnhancer enhancer = child.cliEnhancerConstructor.newInstance(); - Collection additionalArgs = enhancer.getAdditionalCommandLineArguments( child.processor ); - - if ( additionalArgs != null ) { - for ( String arg : additionalArgs ) { - verifier.addCliOption( arg ); - } - } - } - } - - private void configureProcessor(ProcessorTestCase child, Verifier verifier) { - if ( child.processor.getCompilerId() != null ) { - verifier.addCliOption( "-Pgenerate-via-compiler-plugin" ); - verifier.addCliOption( "-Dcompiler-id=" + child.processor.getCompilerId() ); - } - else { - verifier.addCliOption( "-Pgenerate-via-processor-plugin" ); - } - } - - private void configureToolchains(ProcessorTestCase child, Verifier verifier, List goals, - PrintStream originalOut) { - if ( child.processor.getToolchain() != null ) { - verifier.addCliOption( "--toolchains" ); - verifier.addCliOption( TOOLCHAINS_FILE.getPath().replace( '\\', '/' ) ); - - Toolchain toolchain = child.processor.getToolchain(); - - verifier.addCliOption( "-Dtoolchain-jdk-vendor=" + toolchain.getVendor() ); - verifier.addCliOption( "-Dtoolchain-jdk-version=" + toolchain.getVersionRangeString() ); - - goals.add( "toolchains:toolchain" ); - } - } - - private File extractTest(ProcessorTestCase child, Description description) throws IOException { - File tempDirBase = new File( "target/tmp", description.getClassName() ).getCanonicalFile(); - - if ( !tempDirBase.exists() ) { - tempDirBase.mkdirs(); - } - - File parentPom = new File( tempDirBase, "pom.xml" ); - copyURLToFile( getClass().getResource( "/pom.xml" ), parentPom ); - - File tempDir = new File( tempDirBase, description.getMethodName() ); - deleteDirectory( tempDir ); - - return extractResourceToDestination( getClass(), "/" + child.baseDir, tempDir, true ); - } - - private static File getToolchainsFile() { - String specifiedToolchainsFile = System.getProperty( SYS_PROP_TOOLCHAINS_FILE ); - if ( null != specifiedToolchainsFile ) { - try { - File canonical = new File( specifiedToolchainsFile ).getCanonicalFile(); - if ( canonical.exists() ) { - return canonical; - } - - // check the path relative to the parent directory (allows specifying a path relative to the top-level - // aggregator module) - canonical = new File( "..", specifiedToolchainsFile ).getCanonicalFile(); - if ( canonical.exists() ) { - return canonical; - } - } - catch ( IOException e ) { - return null; - } - } - - // use default location - String defaultPath = System.getProperty( "user.home" ) + System.getProperty( "file.separator" ) + ".m2"; - return new File( defaultPath, "toolchains.xml" ); - } - - private static Collection detectSupportedTypes(File toolchainsFile) { - Toolchains toolchains; - try { - if ( toolchainsFile.exists() ) { - Unmarshaller unmarshaller = JAXBContext.newInstance( Toolchains.class ).createUnmarshaller(); - toolchains = (Toolchains) unmarshaller.unmarshal( toolchainsFile ); - } - else { - toolchains = null; - } - } - catch ( JAXBException e ) { - toolchains = null; - } - - Collection supported = new HashSet<>(); - for ( ProcessorType type : ProcessorType.values() ) { - if ( isSupported( type, toolchains ) ) { - supported.add( type ); - } - } - - return supported; - } - - private static boolean isSupported(ProcessorType type, Toolchains toolchains) { - if ( type.getToolchain() == null ) { - return true; - } - - if ( toolchains == null ) { - return false; - } - - Toolchain required = type.getToolchain(); - - for ( Toolchains.Toolchain tc : toolchains.getToolchains() ) { - if ( "jdk".equals( tc.getType() ) ) { - ProviderDescription desc = tc.getProviderDescription(); - if ( desc != null - && required.getVendor().equals( desc.getVendor() ) - && desc.getVersion() != null - && desc.getVersion().startsWith( required.getVersionMinInclusive() ) ) { - return true; - } - } - } - - return false; - } -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/Toolchain.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/Toolchain.java deleted file mode 100644 index 83b7f7f666..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/Toolchain.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.testutil.runner; - -/** - * Describes a required entry in the Maven toolchains.xml file. - * - * @author Andreas Gudian - */ -class Toolchain { - private final String vendor; - private final String versionMinInclusive; - private final String versionMaxExclusive; - - Toolchain(String vendor, String versionMinInclusive, String versionMaxExclusive) { - this.vendor = vendor; - this.versionMinInclusive = versionMinInclusive; - this.versionMaxExclusive = versionMaxExclusive; - } - - String getVendor() { - return vendor; - } - - String getVersionMinInclusive() { - return versionMinInclusive; - } - - /** - * @return the version range string to be used in the Maven execution to select the toolchain - */ - String getVersionRangeString() { - return "[" + versionMinInclusive + "," + versionMaxExclusive + ")"; - } -} diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/xml/Toolchains.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/xml/Toolchains.java deleted file mode 100644 index 4f9b0c7e60..0000000000 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/runner/xml/Toolchains.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ -package org.mapstruct.itest.testutil.runner.xml; - -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -/** - * JAXB representation of some of the parts in the Maven toolchains.xml file. - * - * @author Andreas Gudian - */ -@XmlRootElement -public class Toolchains { - @XmlElement(name = "toolchain") - private List toolchains = new ArrayList<>(); - - public List getToolchains() { - return toolchains; - } - - @Override - public String toString() { - return "Toolchains [toolchains=" + toolchains + "]"; - } - - public static class Toolchain { - @XmlElement - private String type; - - @XmlElement(name = "provides") - private ProviderDescription providerDescription; - - public String getType() { - return type; - } - - public ProviderDescription getProviderDescription() { - return providerDescription; - } - - @Override - public String toString() { - return "Toolchain [type=" + type + ", providerDescription=" + providerDescription + "]"; - } - } - - public static class ProviderDescription { - @XmlElement - private String version; - - @XmlElement - private String vendor; - - public String getVersion() { - return version; - } - - public String getVendor() { - return vendor; - } - - @Override - public String toString() { - return "ProviderDescription [version=" + version + ", vendor=" + vendor + "]"; - } - } -} diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index 9a7b85426f..ebf4590d30 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -76,4 +76,25 @@ joda-time + + + + jdk-11-or-newer + + [11 + + + + jakarta.xml.bind + jakarta.xml.bind-api + 2.3.2 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.2 + + + + diff --git a/integrationtest/src/test/resources/jaxbTest/pom.xml b/integrationtest/src/test/resources/jaxbTest/pom.xml index 49f6c28cef..8b3e4c7921 100644 --- a/integrationtest/src/test/resources/jaxbTest/pom.xml +++ b/integrationtest/src/test/resources/jaxbTest/pom.xml @@ -50,4 +50,26 @@ + + + + jdk-11-or-newer + + [11 + + + + jakarta.xml.bind + jakarta.xml.bind-api + 2.3.2 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.2 + + + + + diff --git a/integrationtest/src/test/resources/pom.xml b/integrationtest/src/test/resources/pom.xml index d2ed9acdca..b386ce6e27 100644 --- a/integrationtest/src/test/resources/pom.xml +++ b/integrationtest/src/test/resources/pom.xml @@ -24,9 +24,6 @@ ${mapstruct.version} - mapstruct - - @@ -137,7 +134,7 @@ ${project.groupId} - \${mapstruct-artifact-id} + mapstruct ${mapstruct.version} provided @@ -165,19 +162,6 @@ \${compiler-source-target-version} - - org.apache.maven.plugins - maven-toolchains-plugin - 1.0 - - - - \${toolchain-jdk-version} - \${toolchain-jdk-vendor} - - - - org.apache.maven.plugins maven-enforcer-plugin diff --git a/parent/pom.xml b/parent/pom.xml index 523a715112..3103f1c1ce 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -29,6 +29,7 @@ 4.0.3.RELEASE 0.26.0 8.29 + 5.6.0 1 3.11.1 @@ -129,6 +130,14 @@ ${com.puppycrawl.tools.checkstyle.version} + + org.junit + junit-bom + ${org.junit.jupiter.version} + pom + import + + javax.enterprise @@ -189,7 +198,7 @@ org.projectlombok lombok - 1.16.18 + 1.18.10 org.immutables @@ -573,12 +582,12 @@ copyright.txt **/LICENSE.txt **/mapstruct.xml - **/toolchains-*.xml **/travis-settings.xml **/eclipse-formatter-config.xml **/forbidden-apis.txt **/checkstyle-for-generated-sources.xml **/nb-configuration.xml + **/junit-platform.properties maven-settings.xml readme.md CONTRIBUTING.md From f382903bc689a7e7e043fc72a9ec9d4686f69db1 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Tue, 4 Feb 2020 13:05:53 +0100 Subject: [PATCH 026/597] Add proper attribution for the code for generating the sourceTargetVersion for other JRE --- .../testutil/extension/ProcessorInvocationInterceptor.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java index 4cf0738eb1..0861f690cb 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java @@ -187,6 +187,9 @@ private String sourceTargetVersion() { } else if ( CURRENT_VERSION == JRE.OTHER ) { try { + // Extracting the major version is done with code from + // org.junit.jupiter.api.condition.JRE when determing the current version + // java.lang.Runtime.version() is a static method available on Java 9+ // that returns an instance of java.lang.Runtime.Version which has the // following method: public int major() From c64e03468e51b0e55a1bc2f6ee965445e9dffb07 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 7 Feb 2020 17:35:51 +0100 Subject: [PATCH 027/597] Fix Checkstyle error --- .../testutil/extension/ProcessorInvocationInterceptor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java index 0861f690cb..f6f9186ba6 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java @@ -187,8 +187,8 @@ private String sourceTargetVersion() { } else if ( CURRENT_VERSION == JRE.OTHER ) { try { - // Extracting the major version is done with code from - // org.junit.jupiter.api.condition.JRE when determing the current version + // Extracting the major version is done with code from + // org.junit.jupiter.api.condition.JRE when determining the current version // java.lang.Runtime.version() is a static method available on Java 9+ // that returns an instance of java.lang.Runtime.Version which has the From 015468b46122d9f0c2e3b43d51a280a255e36a37 Mon Sep 17 00:00:00 2001 From: Robin Clarke Date: Thu, 6 Feb 2020 10:27:57 +0000 Subject: [PATCH 028/597] Updated versions on readme.md to 1.3.1.Final --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index 37376c8714..3c002f9a85 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.3.0.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.3.1.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.3.0.Final + 1.3.1.Final ... @@ -119,10 +119,10 @@ apply plugin: 'net.ltgt.apt-eclipse' dependencies { ... - compile 'org.mapstruct:mapstruct:1.3.0.Final' + compile 'org.mapstruct:mapstruct:1.3.1.Final' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.3.0.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.3.0.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.3.1.Final' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.3.1.Final' // if you are using mapstruct in test code } ... ``` From a6b3cc364a7824a4584fdd690c82c6695447dd79 Mon Sep 17 00:00:00 2001 From: "Tim J. Baumann" Date: Mon, 3 Feb 2020 22:56:42 +0100 Subject: [PATCH 029/597] Fix Javadoc of resolveViaMethodAndConversion and fix some smaller Javadoc typos. --- .../processor/creation/MappingResolverImpl.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 39d661f528..47e3e57f62 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -433,7 +433,7 @@ private Assignment resolveViaMethodAndMethod(Type sourceType, Type targetType) { * * then this method tries to resolve this combination and make a mapping methodY( conversionX ( parameter ) ) * - * In stead of directly using a built in method candidate all the return types as 'B' of all available built-in + * Instead of directly using a built in method candidate, all the return types as 'B' of all available built-in * methods are used to resolve a mapping (assignment) from result type to 'B'. If a match is found, an attempt * is done to find a matching type conversion. */ @@ -476,13 +476,13 @@ private Assignment resolveViaConversionAndMethod(Type sourceType, Type targetTyp /** * Suppose mapping required from A to C and: *

      - *
    • there is a conversion from A to B, conversionX
    • - *
    • there is a method from B to C, methodY
    • + *
    • there is a method from A to B, methodX
    • + *
    • there is a conversion from B to C, conversionY
    • *
    * then this method tries to resolve this combination and make a mapping conversionY( methodX ( parameter ) ) * - * In stead of directly using a built in method candidate all the return types as 'B' of all available built-in - * methods are used to resolve a mapping (assignment) from source type to 'B'. If a match is found, an attempt + * Instead of directly using a built in method candidate, all the return types as 'B' of all available built-in + * methods are used to resolve a mapping (assignment) from source type to 'B'. If a match is found, an attempt * is done to find a matching type conversion. */ private ConversionAssignment resolveViaMethodAndConversion(Type sourceType, Type targetType) { From 273487f152f0299a2633f0b2970e28c58c6c50cb Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Mon, 17 Feb 2020 15:07:00 -0800 Subject: [PATCH 030/597] maven-compiler-plugin 3.8.1 --- documentation/src/main/asciidoc/chapter-2-set-up.asciidoc | 2 +- parent/pom.xml | 2 +- readme.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 16183b0001..fb954ac58f 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -35,7 +35,7 @@ For Maven based projects add the following to your POM file in order to use MapS org.apache.maven.plugins maven-compiler-plugin - 3.5.1 + 3.8.1 1.8 1.8 diff --git a/parent/pom.xml b/parent/pom.xml index 3103f1c1ce..9b920c2122 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -324,7 +324,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + 3.8.1 1.8 1.8 diff --git a/readme.md b/readme.md index 3c002f9a85..f8247297e2 100644 --- a/readme.md +++ b/readme.md @@ -84,7 +84,7 @@ For Maven-based projects, add the following to your POM file in order to use Map org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + 3.8.1 1.8 1.8 From f5771c4177c203fbc6276def5380f5fcec17f9fe Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 22 Feb 2020 17:51:10 +0100 Subject: [PATCH 031/597] #2014 Add support for mapping from Java 14 records --- .travis.yml | 3 + .../itest/tests/MavenIntegrationTest.java | 9 ++ .../src/test/resources/recordsTest/pom.xml | 102 ++++++++++++++++++ .../mapstruct/itest/records/CustomerDto.java | 13 +++ .../itest/records/CustomerEntity.java | 31 ++++++ .../itest/records/CustomerMapper.java | 23 ++++ .../mapstruct/itest/records/RecordsTest.java | 25 +++++ parent/pom.xml | 2 +- .../ap/internal/model/common/Type.java | 5 + .../mapstruct/ap/internal/util/Filters.java | 52 +++++++++ 10 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 integrationtest/src/test/resources/recordsTest/pom.xml create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerDto.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerEntity.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java diff --git a/.travis.yml b/.travis.yml index 6a370d1b46..b86d33d312 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,9 @@ matrix: - jdk: openjdk13 # There is an issue with the documentation so skip it script: mvn -B -V clean install -DskipDistribution=true + - jdk: openjdk14 + # There is an issue with the documentation so skip it + script: mvn -B -V clean install -DskipDistribution=true # There is an issue with the documentation so skip it - jdk: openjdk-ea script: mvn -B -V clean install -DskipDistribution=true diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 16298179a0..e579f031f0 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -5,6 +5,8 @@ */ package org.mapstruct.itest.tests; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; import org.mapstruct.itest.testutil.extension.ProcessorTest; @@ -99,6 +101,13 @@ void namingStrategyTest() { void protobufBuilderTest() { } + @ProcessorTest(baseDir = "recordsTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC + }) + @EnabledForJreRange(min = JRE.JAVA_14) + void recordsTest() { + } + @ProcessorTest(baseDir = "simpleTest") void simpleTest() { } diff --git a/integrationtest/src/test/resources/recordsTest/pom.xml b/integrationtest/src/test/resources/recordsTest/pom.xml new file mode 100644 index 0000000000..c74469a7ba --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/pom.xml @@ -0,0 +1,102 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + recordsTest + jar + + + + generate-via-compiler-plugin + + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + \${compiler-id} + --enable-preview + + + + org.eclipse.tycho + tycho-compiler-jdt + ${org.eclipse.tycho.compiler-jdt.version} + + + + + + + + ${project.groupId} + mapstruct-processor + ${mapstruct.version} + provided + + + + + debug-forked-javac + + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + + true + + --enable-preview + -J-Xdebug + -J-Xnoagent + -J-Djava.compiler=NONE + -J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 + + + + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 1 + --enable-preview + + + + + + diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerDto.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerDto.java new file mode 100644 index 0000000000..69b2547bc3 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerDto.java @@ -0,0 +1,13 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +/** + * @author Filip Hrisafov + */ +public record CustomerDto(String name, String email) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerEntity.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerEntity.java new file mode 100644 index 0000000000..1663bbac5a --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerEntity.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +/** + * @author Filip Hrisafov + */ +public class CustomerEntity { + + private String name; + private String mail; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java new file mode 100644 index 0000000000..0e8a0d2567 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java @@ -0,0 +1,23 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CustomerMapper { + + CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class ); + + @Mapping(target = "mail", source = "email") + CustomerEntity fromRecord(CustomerDto record); + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java new file mode 100644 index 0000000000..5a7a62f780 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.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.itest.records; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.mapstruct.itest.records.CustomerDto; +import org.mapstruct.itest.records.CustomerEntity; +import org.mapstruct.itest.records.CustomerMapper; + +public class RecordsTest { + + @Test + public void shouldMapRecord() { + CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) ); + + assertThat( customer ).isNotNull(); + assertThat( customer.getName() ).isEqualTo( "Kermit" ); + assertThat( customer.getMail() ).isEqualTo( "kermit@test.com" ); + } +} diff --git a/parent/pom.xml b/parent/pom.xml index 9b920c2122..83a59bf2cd 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -510,7 +510,7 @@ org.jacoco jacoco-maven-plugin - 0.8.3 + 0.8.5 org.jvnet.jaxb2.maven2 diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index fe9d5e7e97..6f3a7b79c3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -498,6 +498,11 @@ public Map getPropertyReadAccessors() { } } + Map recordAccessors = filters.recordsIn( typeElement ); + for ( Map.Entry recordEntry : recordAccessors.entrySet() ) { + modifiableGetters.putIfAbsent( recordEntry.getKey(), recordEntry.getValue() ); + } + List fieldsList = filters.fieldsIn( getAllFields() ); for ( Accessor field : fieldsList ) { String propertyName = getPropertyName( field ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java index b673b3f162..6981a1ebb5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java @@ -5,11 +5,16 @@ */ package org.mapstruct.ap.internal.util; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; @@ -30,9 +35,29 @@ * Filter methods for working with {@link Element} collections. * * @author Gunnar Morling + * @author Filip Hrisafov */ public class Filters { + private static final Method RECORD_COMPONENTS_METHOD; + private static final Method RECORD_COMPONENT_ACCESSOR_METHOD; + + static { + Method recordComponentsMethod; + Method recordComponentAccessorMethod; + try { + recordComponentsMethod = TypeElement.class.getMethod( "getRecordComponents" ); + recordComponentAccessorMethod = Class.forName( "javax.lang.model.element.RecordComponentElement" ) + .getMethod( "getAccessor" ); + } + catch ( NoSuchMethodException | ClassNotFoundException e ) { + recordComponentsMethod = null; + recordComponentAccessorMethod = null; + } + RECORD_COMPONENTS_METHOD = recordComponentsMethod; + RECORD_COMPONENT_ACCESSOR_METHOD = recordComponentAccessorMethod; + } + private final AccessorNamingUtils accessorNaming; private final Types typeUtils; private final TypeMirror typeMirror; @@ -50,6 +75,33 @@ public List getterMethodsIn(List elements) { .collect( Collectors.toCollection( LinkedList::new ) ); } + public Map recordsIn(TypeElement typeElement) { + if ( RECORD_COMPONENTS_METHOD == null || RECORD_COMPONENT_ACCESSOR_METHOD == null ) { + return java.util.Collections.emptyMap(); + } + try { + @SuppressWarnings("unchecked") + List recordComponents = (List) RECORD_COMPONENTS_METHOD.invoke( typeElement ); + Map recordAccessors = new LinkedHashMap<>(); + for ( Element recordComponent : recordComponents ) { + ExecutableElement recordExecutableElement = + (ExecutableElement) RECORD_COMPONENT_ACCESSOR_METHOD.invoke( recordComponent ); + recordAccessors.put( + recordComponent.getSimpleName().toString(), + new ExecutableElementAccessor( recordExecutableElement, + getReturnType( recordExecutableElement ), + GETTER + ) + ); + } + + return recordAccessors; + } + catch ( IllegalAccessException | InvocationTargetException e ) { + return java.util.Collections.emptyMap(); + } + } + private TypeMirror getReturnType(ExecutableElement executableElement) { return getWithinContext( executableElement ).getReturnType(); } From b9f86fe6ac05a3a11e6c05dece8cc08da9c50094 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 23 Feb 2020 17:10:00 +0100 Subject: [PATCH 032/597] #2018 Add test case with properties with underscore verifying that it is working as expected --- .../ap/test/bugs/_2018/Issue2018Mapper.java | 22 +++++++++++ .../ap/test/bugs/_2018/Issue2018Test.java | 38 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2018/Source.java | 22 +++++++++++ .../mapstruct/ap/test/bugs/_2018/Target.java | 22 +++++++++++ 4 files changed, 104 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Target.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Mapper.java new file mode 100644 index 0000000000..b75450ce25 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Mapper.java @@ -0,0 +1,22 @@ +/* + * 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._2018; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2018Mapper { + + Issue2018Mapper INSTANCE = Mappers.getMapper( Issue2018Mapper.class ); + + @Mapping(target = "some_value", source = "someValue") + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Test.java new file mode 100644 index 0000000000..21893e0676 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Test.java @@ -0,0 +1,38 @@ +/* + * 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._2018; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2018") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2018Mapper.class, + Source.class, + Target.class +}) +public class Issue2018Test { + + @Test + public void shouldGenerateCorrectCode() { + Source source = new Source(); + source.setSomeValue( "value" ); + + Target target = Issue2018Mapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getSome_value() ).isEqualTo( "value" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Source.java new file mode 100644 index 0000000000..767952da1a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Source.java @@ -0,0 +1,22 @@ +/* + * 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._2018; + +/** + * @author Filip Hrisafov + */ +public class Source { + + private String someValue; + + public String getSomeValue() { + return someValue; + } + + public void setSomeValue(String someValue) { + this.someValue = someValue; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Target.java new file mode 100644 index 0000000000..582acfbebb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Target.java @@ -0,0 +1,22 @@ +/* + * 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._2018; + +/** + * @author Filip Hrisafov + */ +public class Target { + + private String some_value; + + public String getSome_value() { + return some_value; + } + + public void setSome_value(String some_value) { + this.some_value = some_value; + } +} From fe91c6d523f02d999195b04bed4dff0ae2366401 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 23 Feb 2020 19:06:46 +0100 Subject: [PATCH 033/597] Disable Checkstyle for Target for #2018 test --- .../src/test/java/org/mapstruct/ap/test/bugs/_2018/Target.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Target.java index 582acfbebb..1d8c1ae3b2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Target.java @@ -10,6 +10,7 @@ */ public class Target { + // CHECKSTYLE:OFF private String some_value; public String getSome_value() { @@ -19,4 +20,5 @@ public String getSome_value() { public void setSome_value(String some_value) { this.some_value = some_value; } + // CHECKSTYLE:ON } From 551c104295139f30e39c2e605364b42695f567d6 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 22 Feb 2020 20:24:57 +0100 Subject: [PATCH 034/597] #2019 Setup GitHub Actions Add Maven Wrapper for using in GitHub Actions Remove Travis --- .github/workflows/main.yml | 66 ++++ .mvn/wrapper/MavenWrapperDownloader.java | 117 +++++++ .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 50710 bytes .mvn/wrapper/maven-wrapper.properties | 2 + .travis.yml | 37 --- CONTRIBUTING.md | 2 +- etc/{travis-settings.xml => ci-settings.xml} | 0 mvnw | 310 +++++++++++++++++++ mvnw.cmd | 182 +++++++++++ parent/pom.xml | 9 +- readme.md | 4 +- 11 files changed, 685 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 .mvn/wrapper/MavenWrapperDownloader.java create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties delete mode 100644 .travis.yml rename etc/{travis-settings.xml => ci-settings.xml} (100%) create mode 100755 mvnw create mode 100644 mvnw.cmd diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..5596cadad9 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,66 @@ +name: CI + +on: [push, pull_request] + +env: + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + +jobs: + test_jdk: + strategy: + fail-fast: false + matrix: + java: [11, 12, 13, 14-ea, 15-ea] + name: 'Linux JDK ${{ matrix.java }}' + runs-on: ubuntu-latest + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + - name: 'Set up JDK' + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + - name: 'Test' + run: ./mvnw -V -B --no-transfer-progress install -DskipDistribution=true + linux: + name: 'Linux JDK 8' + runs-on: ubuntu-latest + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + - name: 'Set up JDK 8' + uses: actions/setup-java@v1 + with: + java-version: 8 + - name: 'Test' + run: ./mvnw -V -B --no-transfer-progress install + - name: 'Generate coverage report' + run: ./mvnw jacoco:report + - name: 'Upload coverage to Codecov' + uses: codecov/codecov-action@v1 + - name: 'Publish Snapshots' + if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'mapstruct/mapstruct' + run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy + windows: + name: 'Windows' + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: 'Set up JDK 8' + uses: actions/setup-java@v1 + with: + java-version: 8 + - name: 'Test' + run: ./mvnw -V -B --no-transfer-progress install + mac: + name: 'Mac OS' + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: 'Set up JDK 8' + uses: actions/setup-java@v1 + with: + java-version: 8 + - name: 'Test' + run: ./mvnw -V -B --no-transfer-progress install diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000000..b901097f2d --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054 GIT binary patch literal 50710 zcmbTd1CVCTmM+|7+wQV$+qP}n>auOywyU~q+qUhh+uxis_~*a##hm*_WW?9E7Pb7N%LRFiwbEGCJ0XP=%-6oeT$XZcYgtzC2~q zk(K08IQL8oTl}>>+hE5YRgXTB@fZ4TH9>7=79e`%%tw*SQUa9~$xKD5rS!;ZG@ocK zQdcH}JX?W|0_Afv?y`-NgLum62B&WSD$-w;O6G0Sm;SMX65z)l%m1e-g8Q$QTI;(Q z+x$xth4KFvH@Bs6(zn!iF#nenk^Y^ce;XIItAoCsow38eq?Y-Auh!1in#Rt-_D>H^ z=EjbclGGGa6VnaMGmMLj`x3NcwA43Jb(0gzl;RUIRAUDcR1~99l2SAPkVhoRMMtN} zXvC<tOmX83grD8GSo_Lo?%lNfhD#EBgPo z*nf@ppMC#B!T)Ae0RG$mlJWmGl7CkuU~B8-==5i;rS;8i6rJ=PoQxf446XDX9g|c> zU64ePyMlsI^V5Jq5A+BPe#e73+kpc_r1tv#B)~EZ;7^67F0*QiYfrk0uVW;Qb=NsG zN>gsuCwvb?s-KQIppEaeXtEMdc9dy6Dfduz-tMTms+i01{eD9JE&h?Kht*$eOl#&L zJdM_-vXs(V#$Ed;5wyNWJdPNh+Z$+;$|%qR(t`4W@kDhd*{(7-33BOS6L$UPDeE_53j${QfKN-0v-HG z(QfyvFNbwPK%^!eIo4ac1;b>c0vyf9}Xby@YY!lkz-UvNp zwj#Gg|4B~?n?G^{;(W;|{SNoJbHTMpQJ*Wq5b{l9c8(%?Kd^1?H1om1de0Da9M;Q=n zUfn{f87iVb^>Exl*nZ0hs(Yt>&V9$Pg`zX`AI%`+0SWQ4Zc(8lUDcTluS z5a_KerZWe}a-MF9#Cd^fi!y3%@RFmg&~YnYZ6<=L`UJ0v={zr)>$A;x#MCHZy1st7 ztT+N07NR+vOwSV2pvWuN1%lO!K#Pj0Fr>Q~R40{bwdL%u9i`DSM4RdtEH#cW)6}+I-eE< z&tZs+(Ogu(H_;$a$!7w`MH0r%h&@KM+<>gJL@O~2K2?VrSYUBbhCn#yy?P)uF3qWU z0o09mIik+kvzV6w>vEZy@&Mr)SgxPzUiDA&%07m17udz9usD82afQEps3$pe!7fUf z0eiidkJ)m3qhOjVHC_M(RYCBO%CZKZXFb8}s0-+}@CIn&EF(rRWUX2g^yZCvl0bI} zbP;1S)iXnRC&}5-Tl(hASKqdSnO?ASGJ*MIhOXIblmEudj(M|W!+I3eDc}7t`^mtg z)PKlaXe(OH+q-)qcQ8a@!llRrpGI8DsjhoKvw9T;TEH&?s=LH0w$EzI>%u;oD@x83 zJL7+ncjI9nn!TlS_KYu5vn%f*@qa5F;| zEFxY&B?g=IVlaF3XNm_03PA)=3|{n-UCgJoTr;|;1AU9|kPE_if8!Zvb}0q$5okF$ zHaJdmO&gg!9oN|M{!qGE=tb|3pVQ8PbL$}e;NgXz<6ZEggI}wO@aBP**2Wo=yN#ZC z4G$m^yaM9g=|&!^ft8jOLuzc3Psca*;7`;gnHm}tS0%f4{|VGEwu45KptfNmwxlE~ z^=r30gi@?cOm8kAz!EylA4G~7kbEiRlRIzwrb~{_2(x^$-?|#e6Bi_**(vyr_~9Of z!n>Gqf+Qwiu!xhi9f53=PM3`3tNF}pCOiPU|H4;pzjcsqbwg*{{kyrTxk<;mx~(;; z1NMrpaQ`57yn34>Jo3b|HROE(UNcQash!0p2-!Cz;{IRv#Vp5!3o$P8!%SgV~k&Hnqhp`5eLjTcy93cK!3Hm-$`@yGnaE=?;*2uSpiZTs_dDd51U%i z{|Zd9ou-;laGS_x=O}a+ zB||za<795A?_~Q=r=coQ+ZK@@ zId~hWQL<%)fI_WDIX#=(WNl!Dm$a&ROfLTd&B$vatq!M-2Jcs;N2vps$b6P1(N}=oI3<3luMTmC|0*{ zm1w8bt7vgX($!0@V0A}XIK)w!AzUn7vH=pZEp0RU0p?}ch2XC-7r#LK&vyc2=-#Q2 z^L%8)JbbcZ%g0Du;|8=q8B>X=mIQirpE=&Ox{TiuNDnOPd-FLI^KfEF729!!0x#Es z@>3ursjFSpu%C-8WL^Zw!7a0O-#cnf`HjI+AjVCFitK}GXO`ME&on|^=~Zc}^LBp9 zj=-vlN;Uc;IDjtK38l7}5xxQF&sRtfn4^TNtnzXv4M{r&ek*(eNbIu!u$>Ed%` z5x7+&)2P&4>0J`N&ZP8$vcR+@FS0126s6+Jx_{{`3ZrIMwaJo6jdrRwE$>IU_JTZ} z(||hyyQ)4Z1@wSlT94(-QKqkAatMmkT7pCycEB1U8KQbFX&?%|4$yyxCtm3=W`$4fiG0WU3yI@c zx{wfmkZAYE_5M%4{J-ygbpH|(|GD$2f$3o_Vti#&zfSGZMQ5_f3xt6~+{RX=$H8at z?GFG1Tmp}}lmm-R->ve*Iv+XJ@58p|1_jRvfEgz$XozU8#iJS})UM6VNI!3RUU!{5 zXB(+Eqd-E;cHQ>)`h0(HO_zLmzR3Tu-UGp;08YntWwMY-9i^w_u#wR?JxR2bky5j9 z3Sl-dQQU$xrO0xa&>vsiK`QN<$Yd%YXXM7*WOhnRdSFt5$aJux8QceC?lA0_if|s> ze{ad*opH_kb%M&~(~&UcX0nFGq^MqjxW?HJIP462v9XG>j(5Gat_)#SiNfahq2Mz2 zU`4uV8m$S~o9(W>mu*=h%Gs(Wz+%>h;R9Sg)jZ$q8vT1HxX3iQnh6&2rJ1u|j>^Qf`A76K%_ubL`Zu?h4`b=IyL>1!=*%!_K)=XC z6d}4R5L+sI50Q4P3upXQ3Z!~1ZXLlh!^UNcK6#QpYt-YC=^H=EPg3)z*wXo*024Q4b2sBCG4I# zlTFFY=kQ>xvR+LsuDUAk)q%5pEcqr(O_|^spjhtpb1#aC& zghXzGkGDC_XDa%t(X`E+kvKQ4zrQ*uuQoj>7@@ykWvF332)RO?%AA&Fsn&MNzmFa$ zWk&&^=NNjxLjrli_8ESU)}U|N{%j&TQmvY~lk!~Jh}*=^INA~&QB9em!in_X%Rl1&Kd~Z(u z9mra#<@vZQlOY+JYUwCrgoea4C8^(xv4ceCXcejq84TQ#sF~IU2V}LKc~Xlr_P=ry zl&Hh0exdCbVd^NPCqNNlxM3vA13EI8XvZ1H9#bT7y*U8Y{H8nwGpOR!e!!}*g;mJ#}T{ekSb}5zIPmye*If(}}_=PcuAW#yidAa^9-`<8Gr0 z)Fz=NiZ{)HAvw{Pl5uu)?)&i&Us$Cx4gE}cIJ}B4Xz~-q7)R_%owbP!z_V2=Aq%Rj z{V;7#kV1dNT9-6R+H}}(ED*_!F=~uz>&nR3gb^Ce%+0s#u|vWl<~JD3MvS0T9thdF zioIG3c#Sdsv;LdtRv3ml7%o$6LTVL>(H`^@TNg`2KPIk*8-IB}X!MT0`hN9Ddf7yN z?J=GxPL!uJ7lqwowsl?iRrh@#5C$%E&h~Z>XQcvFC*5%0RN-Opq|=IwX(dq(*sjs+ zqy99+v~m|6T#zR*e1AVxZ8djd5>eIeCi(b8sUk)OGjAsKSOg^-ugwl2WSL@d#?mdl zib0v*{u-?cq}dDGyZ%$XRY=UkQwt2oGu`zQneZh$=^! zj;!pCBWQNtvAcwcWIBM2y9!*W|8LmQy$H~5BEx)78J`4Z0(FJO2P^!YyQU{*Al+fs z){!4JvT1iLrJ8aU3k0t|P}{RN)_^v%$$r;+p0DY7N8CXzmS*HB*=?qaaF9D@#_$SN zSz{moAK<*RH->%r7xX~9gVW$l7?b|_SYI)gcjf0VAUJ%FcQP(TpBs; zg$25D!Ry_`8xpS_OJdeo$qh#7U+cepZ??TII7_%AXsT$B z=e)Bx#v%J0j``00Zk5hsvv6%T^*xGNx%KN-=pocSoqE5_R)OK%-Pbu^1MNzfds)mL zxz^F4lDKV9D&lEY;I+A)ui{TznB*CE$=9(wgE{m}`^<--OzV-5V4X2w9j(_!+jpTr zJvD*y6;39&T+==$F&tsRKM_lqa1HC}aGL0o`%c9mO=fts?36@8MGm7Vi{Y z^<7m$(EtdSr#22<(rm_(l_(`j!*Pu~Y>>xc>I9M#DJYDJNHO&4=HM%YLIp?;iR&$m z#_$ZWYLfGLt5FJZhr3jpYb`*%9S!zCG6ivNHYzNHcI%khtgHBliM^Ou}ZVD7ehU9 zS+W@AV=?Ro!=%AJ>Kcy9aU3%VX3|XM_K0A+ZaknKDyIS3S-Hw1C7&BSW5)sqj5Ye_ z4OSW7Yu-;bCyYKHFUk}<*<(@TH?YZPHr~~Iy%9@GR2Yd}J2!N9K&CN7Eq{Ka!jdu; zQNB*Y;i(7)OxZK%IHGt#Rt?z`I|A{q_BmoF!f^G}XVeTbe1Wnzh%1g>j}>DqFf;Rp zz7>xIs12@Ke0gr+4-!pmFP84vCIaTjqFNg{V`5}Rdt~xE^I;Bxp4)|cs8=f)1YwHz zqI`G~s2~qqDV+h02b`PQpUE#^^Aq8l%y2|ByQeXSADg5*qMprEAE3WFg0Q39`O+i1 z!J@iV!`Y~C$wJ!5Z+j5$i<1`+@)tBG$JL=!*uk=2k;T<@{|s1$YL079FvK%mPhyHV zP8^KGZnp`(hVMZ;s=n~3r2y;LTwcJwoBW-(ndU-$03{RD zh+Qn$ja_Z^OuMf3Ub|JTY74s&Am*(n{J3~@#OJNYuEVVJd9*H%)oFoRBkySGm`hx! zT3tG|+aAkXcx-2Apy)h^BkOyFTWQVeZ%e2@;*0DtlG9I3Et=PKaPt&K zw?WI7S;P)TWED7aSH$3hL@Qde?H#tzo^<(o_sv_2ci<7M?F$|oCFWc?7@KBj-;N$P zB;q!8@bW-WJY9do&y|6~mEruZAVe$!?{)N9rZZxD-|oltkhW9~nR8bLBGXw<632!l z*TYQn^NnUy%Ds}$f^=yQ+BM-a5X4^GHF=%PDrRfm_uqC zh{sKwIu|O0&jWb27;wzg4w5uA@TO_j(1X?8E>5Zfma|Ly7Bklq|s z9)H`zoAGY3n-+&JPrT!>u^qg9Evx4y@GI4$n-Uk_5wttU1_t?6><>}cZ-U+&+~JE) zPlDbO_j;MoxdLzMd~Ew|1o^a5q_1R*JZ=#XXMzg?6Zy!^hop}qoLQlJ{(%!KYt`MK z8umEN@Z4w!2=q_oe=;QttPCQy3Nm4F@x>@v4sz_jo{4m*0r%J(w1cSo;D_hQtJs7W z><$QrmG^+<$4{d2bgGo&3-FV}avg9zI|Rr(k{wTyl3!M1q+a zD9W{pCd%il*j&Ft z5H$nENf>>k$;SONGW`qo6`&qKs*T z2^RS)pXk9b@(_Fw1bkb)-oqK|v}r$L!W&aXA>IpcdNZ_vWE#XO8X`#Yp1+?RshVcd zknG%rPd*4ECEI0wD#@d+3NbHKxl}n^Sgkx==Iu%}HvNliOqVBqG?P2va zQ;kRJ$J6j;+wP9cS za#m;#GUT!qAV%+rdWolk+)6kkz4@Yh5LXP+LSvo9_T+MmiaP-eq6_k;)i6_@WSJ zlT@wK$zqHu<83U2V*yJ|XJU4farT#pAA&@qu)(PO^8PxEmPD4;Txpio+2)#!9 z>&=i7*#tc0`?!==vk>s7V+PL#S1;PwSY?NIXN2=Gu89x(cToFm))7L;< z+bhAbVD*bD=}iU`+PU+SBobTQ%S!=VL!>q$rfWsaaV}Smz>lO9JXT#`CcH_mRCSf4%YQAw`$^yY z3Y*^Nzk_g$xn7a_NO(2Eb*I=^;4f!Ra#Oo~LLjlcjke*k*o$~U#0ZXOQ5@HQ&T46l z7504MUgZkz2gNP1QFN8Y?nSEnEai^Rgyvl}xZfMUV6QrJcXp;jKGqB=D*tj{8(_pV zqyB*DK$2lgYGejmJUW)*s_Cv65sFf&pb(Yz8oWgDtQ0~k^0-wdF|tj}MOXaN@ydF8 zNr={U?=;&Z?wr^VC+`)S2xl}QFagy;$mG=TUs7Vi2wws5zEke4hTa2)>O0U?$WYsZ z<8bN2bB_N4AWd%+kncgknZ&}bM~eDtj#C5uRkp21hWW5gxWvc6b*4+dn<{c?w9Rmf zIVZKsPl{W2vQAlYO3yh}-{Os=YBnL8?uN5(RqfQ=-1cOiUnJu>KcLA*tQK3FU`_bM zM^T28w;nAj5EdAXFi&Kk1Nnl2)D!M{@+D-}bIEe+Lc4{s;YJc-{F#``iS2uk;2!Zp zF9#myUmO!wCeJIoi^A+T^e~20c+c2C}XltaR!|U-HfDA=^xF97ev}$l6#oY z&-&T{egB)&aV$3_aVA51XGiU07$s9vubh_kQG?F$FycvS6|IO!6q zq^>9|3U^*!X_C~SxX&pqUkUjz%!j=VlXDo$!2VLH!rKj@61mDpSr~7B2yy{>X~_nc zRI+7g2V&k zd**H++P9dg!-AOs3;GM`(g<+GRV$+&DdMVpUxY9I1@uK28$az=6oaa+PutlO9?6#? zf-OsgT>^@8KK>ggkUQRPPgC7zjKFR5spqQb3ojCHzj^(UH~v+!y*`Smv)VpVoPwa6 zWG18WJaPKMi*F6Zdk*kU^`i~NNTfn3BkJniC`yN98L-Awd)Z&mY? zprBW$!qL-OL7h@O#kvYnLsfff@kDIegt~?{-*5A7JrA;#TmTe?jICJqhub-G@e??D zqiV#g{)M!kW1-4SDel7TO{;@*h2=_76g3NUD@|c*WO#>MfYq6_YVUP+&8e4|%4T`w zXzhmVNziAHazWO2qXcaOu@R1MrPP{t)`N)}-1&~mq=ZH=w=;-E$IOk=y$dOls{6sRR`I5>|X zpq~XYW4sd;J^6OwOf**J>a7u$S>WTFPRkjY;BfVgQst)u4aMLR1|6%)CB^18XCz+r ztkYQ}G43j~Q&1em(_EkMv0|WEiKu;z2zhb(L%$F&xWwzOmk;VLBYAZ8lOCziNoPw1 zv2BOyXA`A8z^WH!nXhKXM`t0;6D*-uGds3TYGrm8SPnJJOQ^fJU#}@aIy@MYWz**H zvkp?7I5PE{$$|~{-ZaFxr6ZolP^nL##mHOErB^AqJqn^hFA=)HWj!m3WDaHW$C)i^ z9@6G$SzB=>jbe>4kqr#sF7#K}W*Cg-5y6kun3u&0L7BpXF9=#7IN8FOjWrWwUBZiU zT_se3ih-GBKx+Uw0N|CwP3D@-C=5(9T#BH@M`F2!Goiqx+Js5xC92|Sy0%WWWp={$(am!#l~f^W_oz78HX<0X#7 zp)p1u~M*o9W@O8P{0Qkg@Wa# z2{Heb&oX^CQSZWSFBXKOfE|tsAm#^U-WkDnU;IowZ`Ok4!mwHwH=s|AqZ^YD4!5!@ zPxJj+Bd-q6w_YG`z_+r;S86zwXb+EO&qogOq8h-Ect5(M2+>(O7n7)^dP*ws_3U6v zVsh)sk^@*c>)3EML|0<-YROho{lz@Nd4;R9gL{9|64xVL`n!m$-Jjrx?-Bacp!=^5 z1^T^eB{_)Y<9)y{-4Rz@9_>;_7h;5D+@QcbF4Wv7hu)s0&==&6u)33 zHRj+&Woq-vDvjwJCYES@$C4{$?f$Ibi4G()UeN11rgjF+^;YE^5nYprYoJNoudNj= zm1pXSeG64dcWHObUetodRn1Fw|1nI$D9z}dVEYT0lQnsf_E1x2vBLql7NrHH!n&Sq z6lc*mvU=WS6=v9Lrl}&zRiu_6u;6g%_DU{9b+R z#YHqX7`m9eydf?KlKu6Sb%j$%_jmydig`B*TN`cZL-g!R)iE?+Q5oOqBFKhx z%MW>BC^(F_JuG(ayE(MT{S3eI{cKiwOtPwLc0XO*{*|(JOx;uQOfq@lp_^cZo=FZj z4#}@e@dJ>Bn%2`2_WPeSN7si^{U#H=7N4o%Dq3NdGybrZgEU$oSm$hC)uNDC_M9xc zGzwh5Sg?mpBIE8lT2XsqTt3j3?We8}3bzLBTQd639vyg^$0#1epq8snlDJP2(BF)K zSx30RM+{f+b$g{9usIL8H!hCO117Xgv}ttPJm9wVRjPk;ePH@zxv%j9k5`TzdXLeT zFgFX`V7cYIcBls5WN0Pf6SMBN+;CrQ(|EsFd*xtwr#$R{Z9FP`OWtyNsq#mCgZ7+P z^Yn$haBJ)r96{ZJd8vlMl?IBxrgh=fdq_NF!1{jARCVz>jNdC)H^wfy?R94#MPdUjcYX>#wEx+LB#P-#4S-%YH>t-j+w zOFTI8gX$ard6fAh&g=u&56%3^-6E2tpk*wx3HSCQ+t7+*iOs zPk5ysqE}i*cQocFvA68xHfL|iX(C4h*67@3|5Qwle(8wT&!&{8*{f%0(5gH+m>$tq zp;AqrP7?XTEooYG1Dzfxc>W%*CyL16q|fQ0_jp%%Bk^k!i#Nbi(N9&T>#M{gez_Ws zYK=l}adalV(nH}I_!hNeb;tQFk3BHX7N}}R8%pek^E`X}%ou=cx8InPU1EE0|Hen- zyw8MoJqB5=)Z%JXlrdTXAE)eqLAdVE-=>wGHrkRet}>3Yu^lt$Kzu%$3#(ioY}@Gu zjk3BZuQH&~7H+C*uX^4}F*|P89JX;Hg2U!pt>rDi(n(Qe-c}tzb0#6_ItoR0->LSt zR~UT<-|@TO%O`M+_e_J4wx7^)5_%%u+J=yF_S#2Xd?C;Ss3N7KY^#-vx+|;bJX&8r zD?|MetfhdC;^2WG`7MCgs>TKKN=^=!x&Q~BzmQio_^l~LboTNT=I zC5pme^P@ER``p$2md9>4!K#vV-Fc1an7pl>_|&>aqP}+zqR?+~Z;f2^`a+-!Te%V? z;H2SbF>jP^GE(R1@%C==XQ@J=G9lKX+Z<@5}PO(EYkJh=GCv#)Nj{DkWJM2}F&oAZ6xu8&g7pn1ps2U5srwQ7CAK zN&*~@t{`31lUf`O;2w^)M3B@o)_mbRu{-`PrfNpF!R^q>yTR&ETS7^-b2*{-tZAZz zw@q5x9B5V8Qd7dZ!Ai$9hk%Q!wqbE1F1c96&zwBBaRW}(^axoPpN^4Aw}&a5dMe+*Gomky_l^54*rzXro$ z>LL)U5Ry>~FJi=*{JDc)_**c)-&faPz`6v`YU3HQa}pLtb5K)u%K+BOqXP0)rj5Au$zB zW1?vr?mDv7Fsxtsr+S6ucp2l#(4dnr9sD*v+@*>g#M4b|U?~s93>Pg{{a5|rm2xfI z`>E}?9S@|IoUX{Q1zjm5YJT|3S>&09D}|2~BiMo=z4YEjXlWh)V&qs;*C{`UMxp$9 zX)QB?G$fPD6z5_pNs>Jeh{^&U^)Wbr?2D6-q?)`*1k@!UvwQgl8eG$r+)NnFoT)L6 zg7lEh+E6J17krfYJCSjWzm67hEth24pomhz71|Qodn#oAILN)*Vwu2qpJirG)4Wnv}9GWOFrQg%Je+gNrPl8mw7ykE8{ z=|B4+uwC&bpp%eFcRU6{mxRV32VeH8XxX>v$du<$(DfinaaWxP<+Y97Z#n#U~V zVEu-GoPD=9$}P;xv+S~Ob#mmi$JQmE;Iz4(){y*9pFyW-jjgdk#oG$fl4o9E8bo|L zWjo4l%n51@Kz-n%zeSCD`uB?T%FVk+KBI}=ve zvlcS#wt`U6wrJo}6I6Rwb=1GzZfwE=I&Ne@p7*pH84XShXYJRgvK)UjQL%R9Zbm(m zxzTQsLTON$WO7vM)*vl%Pc0JH7WhP;$z@j=y#avW4X8iqy6mEYr@-}PW?H)xfP6fQ z&tI$F{NNct4rRMSHhaelo<5kTYq+(?pY)Ieh8*sa83EQfMrFupMM@nfEV@EmdHUv9 z35uzIrIuo4#WnF^_jcpC@uNNaYTQ~uZWOE6P@LFT^1@$o&q+9Qr8YR+ObBkpP9=F+$s5+B!mX2~T zAuQ6RenX?O{IlLMl1%)OK{S7oL}X%;!XUxU~xJN8xk z`xywS*naF(J#?vOpB(K=o~lE;m$zhgPWDB@=p#dQIW>xe_p1OLoWInJRKbEuoncf; zmS1!u-ycc1qWnDg5Nk2D)BY%jmOwCLC+Ny>`f&UxFowIsHnOXfR^S;&F(KXd{ODlm z$6#1ccqt-HIH9)|@fHnrKudu!6B$_R{fbCIkSIb#aUN|3RM>zuO>dpMbROZ`^hvS@ z$FU-;e4W}!ubzKrU@R*dW*($tFZ>}dd*4_mv)#O>X{U@zSzQt*83l9mI zI$8O<5AIDx`wo0}f2fsPC_l>ONx_`E7kdXu{YIZbp1$(^oBAH({T~&oQ&1{X951QW zmhHUxd)t%GQ9#ak5fTjk-cahWC;>^Rg7(`TVlvy0W@Y!Jc%QL3Ozu# zDPIqBCy&T2PWBj+d-JA-pxZlM=9ja2ce|3B(^VCF+a*MMp`(rH>Rt6W1$;r{n1(VK zLs>UtkT43LR2G$AOYHVailiqk7naz2yZGLo*xQs!T9VN5Q>eE(w zw$4&)&6xIV$IO^>1N-jrEUg>O8G4^@y+-hQv6@OmF@gy^nL_n1P1-Rtyy$Bl;|VcV zF=p*&41-qI5gG9UhKmmnjs932!6hceXa#-qfK;3d*a{)BrwNFeKU|ge?N!;zk+kB! zMD_uHJR#%b54c2tr~uGPLTRLg$`fupo}cRJeTwK;~}A>(Acy4k-Xk&Aa1&eWYS1ULWUj@fhBiWY$pdfy+F z@G{OG{*v*mYtH3OdUjwEr6%_ZPZ3P{@rfbNPQG!BZ7lRyC^xlMpWH`@YRar`tr}d> z#wz87t?#2FsH-jM6m{U=gp6WPrZ%*w0bFm(T#7m#v^;f%Z!kCeB5oiF`W33W5Srdt zdU?YeOdPG@98H7NpI{(uN{FJdu14r(URPH^F6tOpXuhU7T9a{3G3_#Ldfx_nT(Hec zo<1dyhsVsTw;ZkVcJ_0-h-T3G1W@q)_Q30LNv)W?FbMH+XJ* zy=$@39Op|kZv`Rt>X`zg&at(?PO^I=X8d9&myFEx#S`dYTg1W+iE?vt#b47QwoHI9 zNP+|3WjtXo{u}VG(lLUaW0&@yD|O?4TS4dfJI`HC-^q;M(b3r2;7|FONXphw-%7~* z&;2!X17|05+kZOpQ3~3!Nb>O94b&ZSs%p)TK)n3m=4eiblVtSx@KNFgBY_xV6ts;NF;GcGxMP8OKV^h6LmSb2E#Qnw ze!6Mnz7>lE9u{AgQ~8u2zM8CYD5US8dMDX-5iMlgpE9m*s+Lh~A#P1er*rF}GHV3h z=`STo?kIXw8I<`W0^*@mB1$}pj60R{aJ7>C2m=oghKyxMbFNq#EVLgP0cH3q7H z%0?L93-z6|+jiN|@v>ix?tRBU(v-4RV`}cQH*fp|)vd3)8i9hJ3hkuh^8dz{F5-~_ zUUr1T3cP%cCaTooM8dj|4*M=e6flH0&8ve32Q)0dyisl))XkZ7Wg~N}6y`+Qi2l+e zUd#F!nJp{#KIjbQdI`%oZ`?h=5G^kZ_uN`<(`3;a!~EMsWV|j-o>c?x#;zR2ktiB! z);5rrHl?GPtr6-o!tYd|uK;Vbsp4P{v_4??=^a>>U4_aUXPWQ$FPLE4PK$T^3Gkf$ zHo&9$U&G`d(Os6xt1r?sg14n)G8HNyWa^q8#nf0lbr4A-Fi;q6t-`pAx1T*$eKM*$ z|CX|gDrk#&1}>5H+`EjV$9Bm)Njw&7-ZR{1!CJTaXuP!$Pcg69`{w5BRHysB$(tWUes@@6aM69kb|Lx$%BRY^-o6bjH#0!7b;5~{6J+jKxU!Kmi# zndh@+?}WKSRY2gZ?Q`{(Uj|kb1%VWmRryOH0T)f3cKtG4oIF=F7RaRnH0Rc_&372={_3lRNsr95%ZO{IX{p@YJ^EI%+gvvKes5cY+PE@unghjdY5#9A!G z70u6}?zmd?v+{`vCu-53_v5@z)X{oPC@P)iA3jK$`r zSA2a7&!^zmUiZ82R2=1cumBQwOJUPz5Ay`RLfY(EiwKkrx%@YN^^XuET;tE zmr-6~I7j!R!KrHu5CWGSChO6deaLWa*9LLJbcAJsFd%Dy>a!>J`N)Z&oiU4OEP-!Ti^_!p}O?7`}i7Lsf$-gBkuY*`Zb z7=!nTT;5z$_5$=J=Ko+Cp|Q0J=%oFr>hBgnL3!tvFoLNhf#D0O=X^h+x08iB;@8pXdRHxX}6R4k@i6%vmsQwu^5z zk1ip`#^N)^#Lg#HOW3sPI33xqFB4#bOPVnY%d6prwxf;Y-w9{ky4{O6&94Ra8VN@K zb-lY;&`HtxW@sF!doT5T$2&lIvJpbKGMuDAFM#!QPXW87>}=Q4J3JeXlwHys?!1^#37q_k?N@+u&Ns20pEoBeZC*np;i;M{2C0Z4_br2gsh6eL z#8`#sn41+$iD?^GL%5?cbRcaa-Nx0vE(D=*WY%rXy3B%gNz0l?#noGJGP728RMY#q z=2&aJf@DcR?QbMmN)ItUe+VM_U!ryqA@1VVt$^*xYt~-qvW!J4Tp<-3>jT=7Zow5M z8mSKp0v4b%a8bxFr>3MwZHSWD73D@+$5?nZAqGM#>H@`)mIeC#->B)P8T$zh-Pxnc z8)~Zx?TWF4(YfKuF3WN_ckpCe5;x4V4AA3(i$pm|78{%!q?|~*eH0f=?j6i)n~Hso zmTo>vqEtB)`%hP55INf7HM@taH)v`Fw40Ayc*R!T?O{ziUpYmP)AH`euTK!zg9*6Z z!>M=$3pd0!&TzU=hc_@@^Yd3eUQpX4-33}b{?~5t5lgW=ldJ@dUAH%`l5US1y_`40 zs(X`Qk}vvMDYYq+@Rm+~IyCX;iD~pMgq^KY)T*aBz@DYEB={PxA>)mI6tM*sx-DmGQHEaHwRrAmNjO!ZLHO4b;;5mf@zzlPhkP($JeZGE7 z?^XN}Gf_feGoG~BjUgVa*)O`>lX=$BSR2)uD<9 z>o^|nb1^oVDhQbfW>>!;8-7<}nL6L^V*4pB=>wwW+RXAeRvKED(n1;R`A6v$6gy0I(;Vf?!4;&sgn7F%LpM}6PQ?0%2Z@b{It<(G1CZ|>913E0nR2r^Pa*Bp z@tFGi*CQ~@Yc-?{cwu1 zsilf=k^+Qs>&WZG(3WDixisHpR>`+ihiRwkL(3T|=xsoNP*@XX3BU8hr57l3k;pni zI``=3Nl4xh4oDj<%>Q1zYXHr%Xg_xrK3Nq?vKX3|^Hb(Bj+lONTz>4yhU-UdXt2>j z<>S4NB&!iE+ao{0Tx^N*^|EZU;0kJkx@zh}S^P{ieQjGl468CbC`SWnwLRYYiStXm zOxt~Rb3D{dz=nHMcY)#r^kF8|q8KZHVb9FCX2m^X*(|L9FZg!5a7((!J8%MjT$#Fs)M1Pb zq6hBGp%O1A+&%2>l0mpaIzbo&jc^!oN^3zxap3V2dNj3x<=TwZ&0eKX5PIso9j1;e zwUg+C&}FJ`k(M|%%}p=6RPUq4sT3-Y;k-<68ciZ~_j|bt>&9ZLHNVrp#+pk}XvM{8 z`?k}o-!if>hVlCP9j%&WI2V`5SW)BCeR5>MQhF)po=p~AYN%cNa_BbV6EEh_kk^@a zD>4&>uCGCUmyA-c)%DIcF4R6!>?6T~Mj_m{Hpq`*(wj>foHL;;%;?(((YOxGt)Bhx zuS+K{{CUsaC++%}S6~CJ=|vr(iIs-je)e9uJEU8ZJAz)w166q)R^2XI?@E2vUQ!R% zn@dxS!JcOimXkWJBz8Y?2JKQr>`~SmE2F2SL38$SyR1^yqj8_mkBp)o$@+3BQ~Mid z9U$XVqxX3P=XCKj0*W>}L0~Em`(vG<>srF8+*kPrw z20{z(=^w+ybdGe~Oo_i|hYJ@kZl*(9sHw#Chi&OIc?w`nBODp?ia$uF%Hs(X>xm?j zqZQ`Ybf@g#wli`!-al~3GWiE$K+LCe=Ndi!#CVjzUZ z!sD2O*;d28zkl))m)YN7HDi^z5IuNo3^w(zy8 zszJG#mp#Cj)Q@E@r-=NP2FVxxEAeOI2e=|KshybNB6HgE^(r>HD{*}S}mO>LuRGJT{*tfTzw_#+er-0${}%YPe@CMJ1Ng#j#)i)SnY@ss3gL;g zg2D~#Kpdfu#G;q1qz_TwSz1VJT(b3zby$Vk&;Y#1(A)|xj`_?i5YQ;TR%jice5E;0 zYHg;`zS5{S*9xI6o^j>rE8Ua*XhIw{_-*&@(R|C(am8__>+Ws&Q^ymy*X4~hR2b5r zm^p3sw}yv=tdyncy_Ui7{BQS732et~Z_@{-IhHDXAV`(Wlay<#hb>%H%WDi+K$862nA@BDtM#UCKMu+kM`!JHyWSi?&)A7_ z3{cyNG%a~nnH_!+;g&JxEMAmh-Z}rC!o7>OVzW&PoMyTA_g{hqXG)SLraA^OP**<7 zjWbr7z!o2n3hnx7A=2O=WL;`@9N{vQIM@&|G-ljrPvIuJHYtss0Er0fT5cMXNUf1B z7FAwBDixt0X7C3S)mPe5g`YtME23wAnbU)+AtV}z+e8G;0BP=bI;?(#|Ep!vVfDbK zvx+|CKF>yt0hWQ3drchU#XBU+HiuG*V^snFAPUp-5<#R&BUAzoB!aZ+e*KIxa26V}s6?nBK(U-7REa573wg-jqCg>H8~>O{ z*C0JL-?X-k_y%hpUFL?I>0WV{oV`Nb)nZbJG01R~AG>flIJf)3O*oB2i8~;!P?Wo_ z0|QEB*fifiL6E6%>tlAYHm2cjTFE@*<);#>689Z6S#BySQ@VTMhf9vYQyLeDg1*F} zjq>i1*x>5|CGKN{l9br3kB0EHY|k4{%^t7-uhjd#NVipUZa=EUuE5kS1_~qYX?>hJ z$}!jc9$O$>J&wnu0SgfYods^z?J4X;X7c77Me0kS-dO_VUQ39T(Kv(Y#s}Qqz-0AH z^?WRL(4RzpkD+T5FG_0NyPq-a-B7A5LHOCqwObRJi&oRi(<;OuIN7SV5PeHU$<@Zh zPozEV`dYmu0Z&Tqd>t>8JVde9#Pt+l95iHe$4Xwfy1AhI zDM4XJ;bBTTvRFtW>E+GzkN)9k!hA5z;xUOL2 zq4}zn-DP{qc^i|Y%rvi|^5k-*8;JZ~9a;>-+q_EOX+p1Wz;>i7c}M6Nv`^NY&{J-> z`(mzDJDM}QPu5i44**2Qbo(XzZ-ZDu%6vm8w@DUarqXj41VqP~ zs&4Y8F^Waik3y1fQo`bVUH;b=!^QrWb)3Gl=QVKr+6sxc=ygauUG|cm?|X=;Q)kQ8 zM(xrICifa2p``I7>g2R~?a{hmw@{!NS5`VhH8+;cV(F>B94M*S;5#O`YzZH1Z%yD? zZ61w(M`#aS-*~Fj;x|J!KM|^o;MI#Xkh0ULJcA?o4u~f%Z^16ViA27FxU5GM*rKq( z7cS~MrZ=f>_OWx8j#-Q3%!aEU2hVuTu(7`TQk-Bi6*!<}0WQi;_FpO;fhpL4`DcWp zGOw9vx0N~6#}lz(r+dxIGZM3ah-8qrqMmeRh%{z@dbUD2w15*_4P?I~UZr^anP}DB zU9CCrNiy9I3~d#&!$DX9e?A});BjBtQ7oGAyoI$8YQrkLBIH@2;lt4E^)|d6Jwj}z z&2_E}Y;H#6I4<10d_&P0{4|EUacwFHauvrjAnAm6yeR#}f}Rk27CN)vhgRqEyPMMS7zvunj2?`f;%?alsJ+-K+IzjJx>h8 zu~m_y$!J5RWAh|C<6+uiCNsOKu)E72M3xKK(a9Okw3e_*O&}7llNV!=P87VM2DkAk zci!YXS2&=P0}Hx|wwSc9JP%m8dMJA*q&VFB0yMI@5vWoAGraygwn){R+Cj6B1a2Px z5)u(K5{+;z2n*_XD!+Auv#LJEM)(~Hx{$Yb^ldQmcYF2zNH1V30*)CN_|1$v2|`LnFUT$%-tO0Eg|c5$BB~yDfzS zcOXJ$wpzVK0MfTjBJ0b$r#_OvAJ3WRt+YOLlJPYMx~qp>^$$$h#bc|`g0pF-Ao43? z>*A+8lx>}L{p(Tni2Vvk)dtzg$hUKjSjXRagj)$h#8=KV>5s)J4vGtRn5kP|AXIz! zPgbbVxW{2o4s-UM;c#We8P&mPN|DW7_uLF!a|^0S=wr6Esx9Z$2|c1?GaupU6$tb| zY_KU`(_29O_%k(;>^|6*pZURH3`@%EuKS;Ns z1lujmf;r{qAN&Q0&m{wJSZ8MeE7RM5+Sq;ul_ z`+ADrd_Um+G37js6tKsArNB}n{p*zTUxQr>3@wA;{EUbjNjlNd6$Mx zg0|MyU)v`sa~tEY5$en7^PkC=S<2@!nEdG6L=h(vT__0F=S8Y&eM=hal#7eM(o^Lu z2?^;05&|CNliYrq6gUv;|i!(W{0N)LWd*@{2q*u)}u*> z7MQgk6t9OqqXMln?zoMAJcc zMKaof_Up})q#DzdF?w^%tTI7STI^@8=Wk#enR*)&%8yje>+tKvUYbW8UAPg55xb70 zEn5&Ba~NmOJlgI#iS8W3-@N%>V!#z-ZRwfPO1)dQdQkaHsiqG|~we2ALqG7Ruup(DqSOft2RFg_X%3w?6VqvV1uzX_@F(diNVp z4{I|}35=11u$;?|JFBEE*gb;T`dy+8gWJ9~pNsecrO`t#V9jW-6mnfO@ff9od}b(3s4>p0i30gbGIv~1@a^F2kl7YO;DxmF3? zWi-RoXhzRJV0&XE@ACc?+@6?)LQ2XNm4KfalMtsc%4!Fn0rl zpHTrHwR>t>7W?t!Yc{*-^xN%9P0cs0kr=`?bQ5T*oOo&VRRu+1chM!qj%2I!@+1XF z4GWJ=7ix9;Wa@xoZ0RP`NCWw0*8247Y4jIZ>GEW7zuoCFXl6xIvz$ezsWgKdVMBH> z{o!A7f;R-@eK9Vj7R40xx)T<2$?F2E<>Jy3F;;=Yt}WE59J!1WN367 zA^6pu_zLoZIf*x031CcwotS{L8bJE(<_F%j_KJ2P_IusaZXwN$&^t716W{M6X2r_~ zaiMwdISX7Y&Qi&Uh0upS3TyEIXNDICQlT5fHXC`aji-c{U(J@qh-mWl-uMN|T&435 z5)a1dvB|oe%b2mefc=Vpm0C%IUYYh7HI*;3UdgNIz}R##(#{(_>82|zB0L*1i4B5j-xi9O4x10rs_J6*gdRBX=@VJ+==sWb&_Qc6tSOowM{BX@(zawtjl zdU!F4OYw2@Tk1L^%~JCwb|e#3CC>srRHQ*(N%!7$Mu_sKh@|*XtR>)BmWw!;8-mq7 zBBnbjwx8Kyv|hd*`5}84flTHR1Y@@uqjG`UG+jN_YK&RYTt7DVwfEDXDW4U+iO{>K zw1hr{_XE*S*K9TzzUlJH2rh^hUm2v7_XjwTuYap|>zeEDY$HOq3X4Tz^X}E9z)x4F zs+T?Ed+Hj<#jY-`Va~fT2C$=qFT-5q$@p9~0{G&eeL~tiIAHXA!f6C(rAlS^)&k<- zXU|ZVs}XQ>s5iONo~t!XXZgtaP$Iau;JT%h)>}v54yut~pykaNye4axEK#5@?TSsQ zE;Jvf9I$GVb|S`7$pG)4vgo9NXsKr?u=F!GnA%VS2z$@Z(!MR9?EPcAqi5ft)Iz6sNl`%kj+_H-X`R<>BFrBW=fSlD|{`D%@Rcbu2?%>t7i34k?Ujb)2@J-`j#4 zLK<69qcUuniIan-$A1+fR=?@+thwDIXtF1Tks@Br-xY zfB+zblrR(ke`U;6U~-;p1Kg8Lh6v~LjW@9l2P6s+?$2!ZRPX`(ZkRGe7~q(4&gEi<$ch`5kQ?*1=GSqkeV z{SA1EaW_A!t{@^UY2D^YO0(H@+kFVzZaAh0_`A`f(}G~EP~?B|%gtxu&g%^x{EYSz zk+T;_c@d;+n@$<>V%P=nk36?L!}?*=vK4>nJSm+1%a}9UlmTJTrfX4{Lb7smNQn@T zw9p2%(Zjl^bWGo1;DuMHN(djsEm)P8mEC2sL@KyPjwD@d%QnZ$ zMJ3cnn!_!iP{MzWk%PI&D?m?C(y2d|2VChluN^yHya(b`h>~GkI1y;}O_E57zOs!{ zt2C@M$^PR2U#(dZmA-sNreB@z-yb0Bf7j*yONhZG=onhx>t4)RB`r6&TP$n zgmN*)eCqvgriBO-abHQ8ECN0bw?z5Bxpx z=jF@?zFdVn?@gD5egM4o$m`}lV(CWrOKKq(sv*`mNcHcvw&Xryfw<{ch{O&qc#WCTXX6=#{MV@q#iHYba!OUY+MGeNTjP%Fj!WgM&`&RlI^=AWTOqy-o zHo9YFt!gQ*p7{Fl86>#-JLZo(b^O`LdFK~OsZBRR@6P?ad^Ujbqm_j^XycM4ZHFyg ziUbIFW#2tj`65~#2V!4z7DM8Z;fG0|APaQ{a2VNYpNotB7eZ5kp+tPDz&Lqs0j%Y4tA*URpcfi z_M(FD=fRGdqf430j}1z`O0I=;tLu81bwJXdYiN7_&a-?ly|-j*+=--XGvCq#32Gh(=|qj5F?kmihk{%M&$}udW5)DHK zF_>}5R8&&API}o0osZJRL3n~>76nUZ&L&iy^s>PMnNcYZ|9*1$v-bzbT3rpWsJ+y{ zPrg>5Zlery96Um?lc6L|)}&{992{_$J&=4%nRp9BAC6!IB=A&=tF>r8S*O-=!G(_( zwXbX_rGZgeiK*&n5E;f=k{ktyA1(;x_kiMEt0*gpp_4&(twlS2e5C?NoD{n>X2AT# zY@Zp?#!b1zNq96MQqeO*M1MMBin5v#RH52&Xd~DO6-BZLnA6xO1$sou(YJ1Dlc{WF zVa%2DyYm`V#81jP@70IJ;DX@y*iUt$MLm)ByAD$eUuji|5{ptFYq(q)mE(5bOpxjM z^Q`AHWq44SG3`_LxC9fwR)XRVIp=B%<(-lOC3jI#bb@dK(*vjom!=t|#<@dZql%>O z15y^{4tQoeW9Lu%G&V$90x6F)xN6y_oIn;!Q zs)8jT$;&;u%Y>=T3hg34A-+Y*na=|glcStr5D;&5*t5*DmD~x;zQAV5{}Ya`?RRGa zT*t9@$a~!co;pD^!J5bo?lDOWFx%)Y=-fJ+PDGc0>;=q=s?P4aHForSB+)v0WY2JH z?*`O;RHum6j%#LG)Vu#ciO#+jRC3!>T(9fr+XE7T2B7Z|0nR5jw@WG)kDDzTJ=o4~ zUpeyt7}_nd`t}j9BKqryOha{34erm)RmST)_9Aw)@ zHbiyg5n&E{_CQR@h<}34d7WM{s{%5wdty1l+KX8*?+-YkNK2Be*6&jc>@{Fd;Ps|| z26LqdI3#9le?;}risDq$K5G3yoqK}C^@-8z^wj%tdgw-6@F#Ju{Sg7+y)L?)U$ez> zoOaP$UFZ?y5BiFycir*pnaAaY+|%1%8&|(@VB)zweR%?IidwJyK5J!STzw&2RFx zZV@qeaCB01Hu#U9|1#=Msc8Pgz5P*4Lrp!Q+~(G!OiNR{qa7|r^H?FC6gVhkk3y7=uW#Sh;&>78bZ}aK*C#NH$9rX@M3f{nckYI+5QG?Aj1DM)@~z_ zw!UAD@gedTlePB*%4+55naJ8ak_;))#S;4ji!LOqY5VRI){GMwHR~}6t4g>5C_#U# ztYC!tjKjrKvRy=GAsJVK++~$|+s!w9z3H4G^mACv=EErXNSmH7qN}%PKcN|8%9=i)qS5+$L zu&ya~HW%RMVJi4T^pv?>mw*Gf<)-7gf#Qj|e#w2|v4#t!%Jk{&xlf;$_?jW*n!Pyx zkG$<18kiLOAUPuFfyu-EfWX%4jYnjBYc~~*9JEz6oa)_R|8wjZA|RNrAp%}14L7fW zi7A5Wym*K+V8pkqqO-X#3ft{0qs?KVt^)?kS>AicmeO&q+~J~ zp0YJ_P~_a8j= zsAs~G=8F=M{4GZL{|B__UorX@MRNQLn?*_gym4aW(~+i13knnk1P=khoC-ViMZk+x zLW(l}oAg1H`dU+Fv**;qw|ANDSRs>cGqL!Yw^`; zv;{E&8CNJcc)GHzTYM}f&NPw<6j{C3gaeelU#y!M)w-utYEHOCCJo|Vgp7K6C_$14 zqIrLUB0bsgz^D%V%fbo2f9#yb#CntTX?55Xy|Kps&Xek*4_r=KDZ z+`TQuv|$l}MWLzA5Ay6Cvsa^7xvwXpy?`w(6vx4XJ zWuf1bVSb#U8{xlY4+wlZ$9jjPk)X_;NFMqdgq>m&W=!KtP+6NL57`AMljW+es zzqjUjgz;V*kktJI?!NOg^s_)ph45>4UDA!Vo0hn>KZ+h-3=?Y3*R=#!fOX zP$Y~+14$f66ix?UWB_6r#fMcC^~X4R-<&OD1CSDNuX~y^YwJ>sW0j`T<2+3F9>cLo z#!j57$ll2K9(%$4>eA7(>FJX5e)pR5&EZK!IMQzOfik#FU*o*LGz~7u(8}XzIQRy- z!U7AlMTIe|DgQFmc%cHy_9^{o`eD%ja_L>ckU6$O4*U**o5uR7`FzqkU8k4gxtI=o z^P^oGFPm5jwZMI{;nH}$?p@uV8FT4r=|#GziKXK07bHJLtK}X%I0TON$uj(iJ`SY^ zc$b2CoxCQ>7LH@nxcdW&_C#fMYBtTxcg46dL{vf%EFCZ~eErMvZq&Z%Lhumnkn^4A zsx$ay(FnN7kYah}tZ@0?-0Niroa~13`?hVi6`ndno`G+E8;$<6^gsE-K3)TxyoJ4M zb6pj5=I8^FD5H@`^V#Qb2^0cx7wUz&cruA5g>6>qR5)O^t1(-qqP&1g=qvY#s&{bx zq8Hc%LsbK1*%n|Y=FfojpE;w~)G0-X4i*K3{o|J7`krhIOd*c*$y{WIKz2n2*EXEH zT{oml3Th5k*vkswuFXdGDlcLj15Nec5pFfZ*0?XHaF_lVuiB%Pv&p7z)%38}%$Gup zVTa~C8=cw%6BKn_|4E?bPNW4PT7}jZQLhDJhvf4z;~L)506IE0 zX!tWXX(QOQPRj-p80QG79t8T2^az4Zp2hOHziQlvT!|H)jv{Ixodabzv6lBj)6WRB z{)Kg@$~~(7$-az?lw$4@L%I&DI0Lo)PEJJziWP33a3azb?jyXt1v0N>2kxwA6b%l> zZqRpAo)Npi&loWbjFWtEV)783BbeIAhqyuc+~>i7aQ8shIXt)bjCWT6$~ro^>99G} z2XfmT0(|l!)XJb^E!#3z4oEGIsL(xd; zYX1`1I(cG|u#4R4T&C|m*9KB1`UzKvho5R@1eYtUL9B72{i(ir&ls8g!pD ztR|25xGaF!4z5M+U@@lQf(12?xGy`!|3E}7pI$k`jOIFjiDr{tqf0va&3pOn6Pu)% z@xtG2zjYuJXrV)DUrIF*y<1O1<$#54kZ#2;=X51J^F#0nZ0(;S$OZDt_U2bx{RZ=Q zMMdd$fH|!s{ zXq#l;{`xfV`gp&C>A`WrQU?d{!Ey5(1u*VLJt>i27aZ-^&2IIk=zP5p+{$q(K?2(b z8?9h)kvj9SF!Dr zoyF}?V|9;6abHxWk2cEvGs$-}Pg}D+ZzgkaN&$Snp%;5m%zh1E#?Wac-}x?BYlGN#U#Mek*}kek#I9XaHt?mz3*fDrRTQ#&#~xyeqJk1QJ~E$7qsw6 z?sV;|?*=-{M<1+hXoj?@-$y+(^BJ1H~wQ9G8C0#^aEAyhDduNX@haoa=PuPp zYsGv8UBfQaRHgBgLjmP^eh>fLMeh{8ic)?xz?#3kX-D#Z{;W#cd_`9OMFIaJg-=t`_3*!YDgtNQ2+QUEAJB9M{~AvT$H`E)IKmCR21H532+ata8_i_MR@ z2Xj<3w<`isF~Ah$W{|9;51ub*f4#9ziKrOR&jM{x7I_7()O@`F*5o$KtZ?fxU~g`t zUovNEVKYn$U~VX8eR)qb`7;D8pn*Pp$(otYTqL)5KH$lUS-jf}PGBjy$weoceAcPp z&5ZYB$r&P$MN{0H0AxCe4Qmd3T%M*5d4i%#!nmBCN-WU-4m4Tjxn-%j3HagwTxCZ9 z)j5vO-C7%s%D!&UfO>bi2oXiCw<-w{vVTK^rVbv#W=WjdADJy8$khnU!`ZWCIU`># zyjc^1W~pcu>@lDZ{zr6gv%)2X4n27~Ve+cQqcND%0?IFSP4sH#yIaXXYAq^z3|cg` z`I3$m%jra>e2W-=DiD@84T!cb%||k)nPmEE09NC%@PS_OLhkrX*U!cgD*;;&gIaA(DyVT4QD+q_xu z>r`tg{hiGY&DvD-)B*h+YEd+Zn)WylQl}<4>(_NlsKXCRV;a)Rcw!wtelM2_rWX`j zTh5A|i6=2BA(iMCnj_fob@*eA;V?oa4Z1kRBGaU07O70fb6-qmA$Hg$ps@^ka1=RO zTbE_2#)1bndC3VuK@e!Sftxq4=Uux}fDxXE#Q5_x=E1h>T5`DPHz zbH<_OjWx$wy7=%0!mo*qH*7N4tySm+R0~(rbus`7;+wGh;C0O%x~fEMkt!eV>U$`i z5>Q(o z=t$gPjgGh0&I7KY#k50V7DJRX<%^X z>6+ebc9efB3@eE2Tr){;?_w`vhgF>`-GDY(YkR{9RH(MiCnyRtd!LxXJ75z+?2 zGi@m^+2hKJ5sB1@Xi@s_@p_Kwbc<*LQ_`mr^Y%j}(sV_$`J(?_FWP)4NW*BIL~sR>t6 zM;qTJZ~GoY36&{h-Pf}L#y2UtR}>ZaI%A6VkU>vG4~}9^i$5WP2Tj?Cc}5oQxe2=q z8BeLa$hwCg_psjZyC2+?yX4*hJ58Wu^w9}}7X*+i5Rjqu5^@GzXiw#SUir1G1`jY% zOL=GE_ENYxhcyUrEt9XlMNP6kx6h&%6^u3@zB8KUCAa18T(R2J`%JjWZ z!{7cXaEW+Qu*iJPu+m>QqW}Lo$4Z+!I)0JNzZ&_M%=|B1yejFRM04bGAvu{=lNPd+ zJRI^DRQ(?FcVUD+bgEcAi@o(msqys9RTCG#)TjI!9~3-dc`>gW;HSJuQvH~d`MQs86R$|SKXHh zqS9Qy)u;T`>>a!$LuaE2keJV%;8g)tr&Nnc;EkvA-RanHXsy)D@XN0a>h}z2j81R; zsUNJf&g&rKpuD0WD@=dDrPHdBoK42WoBU|nMo17o(5^;M|dB4?|FsAGVrSyWcI`+FVw^vTVC`y}f(BwJl zrw3Sp151^9=}B})6@H*i4-dIN_o^br+BkcLa^H56|^2XsT0dESw2 zMX>(KqNl=x2K5=zIKg}2JpGAZu{I_IO}0$EQ5P{4zol**PCt3F4`GX}2@vr8#Y)~J zKb)gJeHcFnR@4SSh%b;c%J`l=W*40UPjF#q{<}ywv-=vHRFmDjv)NtmC zQx9qm)d%0zH&qG7AFa3VAU1S^(n8VFTC~Hb+HjYMjX8r#&_0MzlNR*mnLH5hi}`@{ zK$8qiDDvS_(L9_2vHgzEQ${DYSE;DqB!g*jhJghE&=LTnbgl&Xepo<*uRtV{2wDHN z)l;Kg$TA>Y|K8Lc&LjWGj<+bp4Hiye_@BfU(y#nF{fpR&|Ltbye?e^j0}8JC4#xi% zv29ZR%8%hk=3ZDvO-@1u8KmQ@6p%E|dlHuy#H1&MiC<*$YdLkHmR#F3ae;bKd;@*i z2_VfELG=B}JMLCO-6UQy^>RDE%K4b>c%9ki`f~Z2Qu8hO7C#t%Aeg8E%+}6P7Twtg z-)dj(w}_zFK&86KR@q9MHicUAucLVshUdmz_2@32(V`y3`&Kf8Q2I)+!n0mR=rrDU zXvv^$ho;yh*kNqJ#r1}b0|i|xRUF6;lhx$M*uG3SNLUTC@|htC z-=fsw^F%$qqz4%QdjBrS+ov}Qv!z00E+JWas>p?z@=t!WWU3K*?Z(0meTuTOC7OTx zU|kFLE0bLZ+WGcL$u4E}5dB0g`h|uwv3=H6f+{5z9oLv-=Q45+n~V4WwgO=CabjM% zBAN+RjM65(-}>Q2V#i1Na@a0`08g&y;W#@sBiX6Tpy8r}*+{RnyGUT`?XeHSqo#|J z^ww~c;ou|iyzpErDtlVU=`8N7JSu>4M z_pr9=tX0edVn9B}YFO2y(88j#S{w%E8vVOpAboK*27a7e4Ekjt0)hIX99*1oE;vex z7#%jhY=bPijA=Ce@9rRO(Vl_vnd00!^TAc<+wVvRM9{;hP*rqEL_(RzfK$er_^SN; z)1a8vo8~Dr5?;0X0J62Cusw$A*c^Sx1)dom`-)Pl7hsW4i(r*^Mw`z5K>!2ixB_mu z*Ddqjh}zceRFdmuX1akM1$3>G=#~|y?eYv(e-`Qy?bRHIq=fMaN~fB zUa6I8Rt=)jnplP>yuS+P&PxeWpJ#1$F`iqRl|jF$WL_aZFZl@kLo&d$VJtu&w?Q0O zzuXK>6gmygq(yXJy0C1SL}T8AplK|AGNUOhzlGeK_oo|haD@)5PxF}rV+5`-w{Aag zus45t=FU*{LguJ11Sr-28EZkq;!mJO7AQGih1L4rEyUmp>B!%X0YemsrV3QFvlgt* z5kwlPzaiJ+kZ^PMd-RRbl(Y?F*m`4*UIhIuf#8q>H_M=fM*L_Op-<_r zBZagV=4B|EW+KTja?srADTZXCd3Yv%^Chfpi)cg{ED${SI>InNpRj5!euKv?=Xn92 zsS&FH(*w`qLIy$doc>RE&A5R?u zzkl1sxX|{*fLpXvIW>9d<$ePROttn3oc6R!sN{&Y+>Jr@yeQN$sFR z;w6A<2-0%UA?c8Qf;sX7>>uKRBv3Ni)E9pI{uVzX|6Bb0U)`lhLE3hK58ivfRs1}d zNjlGK0hdq0qjV@q1qI%ZFMLgcpWSY~mB^LK)4GZ^h_@H+3?dAe_a~k*;9P_d7%NEFP6+ zgV(oGr*?W(ql?6SQ~`lUsjLb%MbfC4V$)1E0Y_b|OIYxz4?O|!kRb?BGrgiH5+(>s zoqM}v*;OBfg-D1l`M6T6{K`LG+0dJ1)!??G5g(2*vlNkm%Q(MPABT$r13q?|+kL4- zf)Mi5r$sn;u41aK(K#!m+goyd$c!KPl~-&-({j#D4^7hQkV3W|&>l_b!}!z?4($OA z5IrkfuT#F&S1(`?modY&I40%gtroig{YMvF{K{>5u^I51k8RriGd${z)=5k2tG zM|&Bp5kDTfb#vfuTTd?)a=>bX=lokw^y9+2LS?kwHQIWI~pYgy7 zb?A-RKVm_vM5!9?C%qYdfRAw& zAU7`up~%g=p@}pg#b7E)BFYx3g%(J36Nw(Dij!b>cMl@CSNbrW!DBDbTD4OXk!G4x zi}JBKc8HBYx$J~31PXH+4^x|UxK~(<@I;^3pWN$E=sYma@JP|8YL`L(zI6Y#c%Q{6 z*APf`DU$S4pr#_!60BH$FGViP14iJmbrzSrOkR;f3YZa{#E7Wpd@^4E-zH8EgPc-# zKWFPvh%WbqU_%ZEt`=Q?odKHc7@SUmY{GK`?40VuL~o)bS|is$Hn=<=KGHOsEC5tB zFb|q}gGlL97NUf$G$>^1b^3E18PZ~Pm9kX%*ftnolljiEt@2#F2R5ah$zbXd%V_Ev zyDd{1o_uuoBga$fB@Fw!V5F3jIr=a-ykqrK?WWZ#a(bglI_-8pq74RK*KfQ z0~Dzus7_l;pMJYf>Bk`)`S8gF!To-BdMnVw5M-pyu+aCiC5dwNH|6fgRsIKZcF&)g zr}1|?VOp}I3)IR@m1&HX1~#wsS!4iYqES zK}4J{Ei>;e3>LB#Oly>EZkW14^@YmpbgxCDi#0RgdM${&wxR+LiX}B+iRioOB0(pDKpVEI;ND?wNx>%e|m{RsqR_{(nmQ z3ZS}@t!p4a(BKx_-CYwrcyJ5u1TO9bcXti$8sy>xcLKqKCc#~UOZYD{llKTSFEjJ~ zyNWt>tLU}*>^`TvPxtP%F`ZJQw@W0^>x;!^@?k_)9#bF$j0)S3;mH-IR5y82l|%=F z2lR8zhP?XNP-ucZZ6A+o$xOyF!w;RaLHGh57GZ|TCXhJqY~GCh)aXEV$1O&$c}La1 zjuJxkY9SM4av^Hb;i7efiYaMwI%jGy`3NdY)+mcJhF(3XEiSlU3c|jMBi|;m-c?~T z+x0_@;SxcoY=(6xNgO$bBt~Pj8`-<1S|;Bsjrzw3@zSjt^JC3X3*$HI79i~!$RmTz zsblZsLYs7L$|=1CB$8qS!tXrWs!F@BVuh?kN(PvE5Av-*r^iYu+L^j^m9JG^#=m>@ z=1soa)H*w6KzoR$B8mBCXoU;f5^bVuwQ3~2LKg!yxomG1#XPmn(?YH@E~_ED+W6mxs%x{%Z<$pW`~ON1~2XjP5v(0{C{+6Dm$00tsd3w=f=ZENy zOgb-=f}|Hb*LQ$YdWg<(u7x3`PKF)B7ZfZ6;1FrNM63 z?O6tE%EiU@6%rVuwIQjvGtOofZBGZT1Sh(xLIYt9c4VI8`!=UJd2BfLjdRI#SbVAX ziT(f*RI^T!IL5Ac>ql7uduF#nuCRJ1)2bdvAyMxp-5^Ww5p#X{rb5)(X|fEhDHHW{ zw(Lfc$g;+Q`B0AiPGtmK%*aWfQQ$d!*U<|-@n2HZvCWSiw^I>#vh+LyC;aaVWGbmkENr z&kl*8o^_FW$T?rDYLO1Pyi%>@&kJKQoH2E0F`HjcN}Zlnx1ddoDA>G4Xu_jyp6vuT zPvC}pT&Owx+qB`zUeR|4G;OH(<<^_bzkjln0k40t`PQxc$7h(T8Ya~X+9gDc8Z9{Z z&y0RAU}#_kQGrM;__MK9vwIwK^aoqFhk~dK!ARf1zJqHMxF2?7-8|~yoO@_~Ed;_wvT%Vs{9RK$6uUQ|&@#6vyBsFK9eZW1Ft#D2)VpQRwpR(;x^ zdoTgMqfF9iBl%{`QDv7B0~8{8`8k`C4@cbZAXBu00v#kYl!#_Wug{)2PwD5cNp?K^ z9+|d-4z|gZ!L{57>!Ogfbzchm>J1)Y%?NThxIS8frAw@z>Zb9v%3_3~F@<=LG%r*U zaTov}{{^z~SeX!qgSYow`_5)ij*QtGp4lvF`aIGQ>@3ZTkDmsl#@^5*NGjOuu82}o zzLF~Q9SW+mP=>88%eSA1W4_W7-Q>rdq^?t=m6}^tDPaBRGFLg%ak93W!kOp#EO{6& zP%}Iff5HZQ9VW$~+9r=|Quj#z*=YwcnssS~9|ub2>v|u1JXP47vZ1&L1O%Z1DsOrDfSIMHU{VT>&>H=9}G3i@2rP+rx@eU@uE8rJNec zij~#FmuEBj03F1~ct@C@$>y)zB+tVyjV3*n`mtAhIM0$58vM9jOQC}JJOem|EpwqeMuYPxu3sv}oMS?S#o6GGK@8PN59)m&K4Dc&X% z(;XL_kKeYkafzS3Wn5DD>Yiw{LACy_#jY4op(>9q>>-*9@C0M+=b#bknAWZ37^(Ij zq>H%<@>o4a#6NydoF{_M4i4zB_KG)#PSye9bk0Ou8h%1Dtl7Q_y#7*n%g)?m>xF~( zjqvOwC;*qvN_3(*a+w2|ao0D?@okOvg8JskUw(l7n`0fncglavwKd?~l_ryKJ^Ky! zKCHkIC-o7%fFvPa$)YNh022lakMar^dgL=t#@XLyNHHw!b?%WlM)R@^!)I!smZL@k zBi=6wE5)2v&!UNV(&)oOYW(6Qa!nUjDKKBf-~Da=#^HE4(@mWk)LPvhyN3i4goB$3K8iV7uh zsv+a?#c4&NWeK(3AH;ETrMOIFgu{_@%XRwCZ;L=^8Ts)hix4Pf3yJRQ<8xb^CkdmC z?c_gB)XmRsk`9ch#tx4*hO=#qS7={~Vb4*tTf<5P%*-XMfUUYkI9T1cEF;ObfxxI-yNuA=I$dCtz3ey znVkctYD*`fUuZ(57+^B*R=Q}~{1z#2!ca?)+YsRQb+lt^LmEvZt_`=j^wqig+wz@n@ z`LIMQJT3bxMzuKg8EGBU+Q-6cs5(@5W?N>JpZL{$9VF)veF`L5%DSYTNQEypW%6$u zm_~}T{HeHj1bAlKl8ii92l9~$dm=UM21kLemA&b$;^!wB7#IKWGnF$TVq!!lBlG4 z{?Rjz?P(uvid+|i$VH?`-C&Gcb3{(~Vpg`w+O);Wk1|Mrjxrht0GfRUnZqz2MhrXa zqgVC9nemD5)H$to=~hp)c=l9?#~Z_7i~=U-`FZxb-|TR9@YCxx;Zjo-WpMNOn2)z) zFPGGVl%3N$f`gp$gPnWC+f4(rmts%fidpo^BJx72zAd7|*Xi{2VXmbOm)1`w^tm9% znM=0Fg4bDxH5PxPEm{P3#A(mxqlM7SIARP?|2&+c7qmU8kP&iApzL|F>Dz)Ixp_`O zP%xrP1M6@oYhgo$ZWwrAsYLa4 z|I;DAvJxno9HkQrhLPQk-8}=De{9U3U%)dJ$955?_AOms!9gia%)0E$Mp}$+0er@< zq7J&_SzvShM?e%V?_zUu{niL@gt5UFOjFJUJ}L?$f%eU%jUSoujr{^O=?=^{19`ON zlRIy8Uo_nqcPa6@yyz`CM?pMJ^^SN^Fqtt`GQ8Q#W4kE7`V9^LT}j#pMChl!j#g#J zr-=CCaV%xyFeQ9SK+mG(cTwW*)xa(eK;_Z(jy)woZp~> zA(4}-&VH+TEeLzPTqw&FOoK(ZjD~m{KW05fiGLe@E3Z2`rLukIDahE*`u!ubU)9`o zn^-lyht#E#-dt~S>}4y$-mSbR8{T@}22cn^refuQ08NjLOv?JiEWjyOnzk<^R5%gO zhUH_B{oz~u#IYwVnUg8?3P*#DqD8#X;%q%HY**=I>>-S|!X*-!x1{^l#OnR56O>iD zc;i;KS+t$koh)E3)w0OjWJl_aW2;xF=9D9Kr>)(5}4FqUbk# zI#$N8o0w;IChL49m9CJTzoC!|u{Ljd%ECgBOf$}&jA^$(V#P#~)`&g`H8E{uv52pp zwto`xUL-L&WTAVREEm$0g_gYPL(^vHq(*t1WCH_6alhkeW&GCZ3hL)|{O-jiFOBrF z!EW=Jej|dqQitT6!B-7&io2K)WIm~Q)v@yq%U|VpV+I?{y0@Yd%n8~-NuuM*pM~KA z85YB};IS~M(c<}4Hxx>qRK0cdl&e?t253N%vefkgds>Ubn8X}j6Vpgs>a#nFq$osY z1ZRwLqFv=+BTb=i%D2Wv>_yE0z}+niZ4?rE|*a3d7^kndWGwnFqt+iZ(7+aln<}jzbAQ(#Z2SS}3S$%Bd}^ zc9ghB%O)Z_mTZMRC&H#)I#fiLuIkGa^`4e~9oM5zKPx?zjkC&Xy0~r{;S?FS%c7w< zWbMpzc(xSw?9tGxG~_l}Acq}zjt5ClaB7-!vzqnlrX;}$#+PyQ9oU)_DfePh2E1<7 ztok6g6K^k^DuHR*iJ?jw?bs_whk|bx`dxu^nC6#e{1*m~z1eq7m}Cf$*^Eua(oi_I zAL+3opNhJteu&mWQ@kQWPucmiP)4|nFG`b2tpC;h{-PI@`+h?9v=9mn|0R-n8#t=+Z*FD(c5 zjj79Jxkgck*DV=wpFgRZuwr%}KTm+dx?RT@aUHJdaX-ODh~gByS?WGx&czAkvkg;x zrf92l8$Or_zOwJVwh>5rB`Q5_5}ef6DjS*$x30nZbuO3dijS*wvNEqTY5p1_A0gWr znH<(Qvb!os14|R)n2Ost>jS2;d1zyLHu`Svm|&dZD+PpP{Bh>U&`Md;gRl64q;>{8MJJM$?UNUd`aC>BiLe>*{ zJY15->yW+<3rLgYeTruFDtk1ovU<$(_y7#HgUq>)r0{^}Xbth}V#6?%5jeFYt;SG^ z3qF)=uWRU;Jj)Q}cpY8-H+l_n$2$6{ZR?&*IGr{>ek!69ZH0ZoJ*Ji+ezzlJ^%qL3 zO5a`6gwFw(moEzqxh=yJ9M1FTn!eo&qD#y5AZXErHs%22?A+JmS&GIolml!)rZTnUDM3YgzYfT#;OXn)`PWv3Ta z!-i|-Wojv*k&bC}_JJDjiAK(Ba|YZgUI{f}TdEOFT2+}nPmttytw7j%@bQZDV1vvj z^rp{gRkCDmYJHGrE1~e~AE!-&6B6`7UxVQuvRrfdFkGX8H~SNP_X4EodVd;lXd^>eV1jN+Tt4}Rsn)R0LxBz0c=NXU|pUe!MQQFkGBWbR3&(jLm z%RSLc#p}5_dO{GD=DEFr=Fc% z85CBF>*t!6ugI?soX(*JNxBp+-DdZ4X0LldiK}+WWGvXV(C(Ht|!3$psR=&c*HIM=BmX;pRIpz@Ale{9dhGe(U2|Giv;# zOc|;?p67J=Q(kamB*aus=|XP|m{jN^6@V*Bpm?ye56Njh#vyJqE=DweC;?Rv7faX~ zde03n^I~0B2vUmr;w^X37tVxUK?4}ifsSH5_kpKZIzpYu0;Kv}SBGfI2AKNp+VN#z`nI{UNDRbo-wqa4NEls zICRJpu)??cj^*WcZ^MAv+;bDbh~gpN$1Cor<{Y2oyIDws^JsfW^5AL$azE(T0p&pP z1Mv~6Q44R&RHoH95&OuGx2srIr<@zYJTOMKiVs;Bx3py89I87LOb@%mr`0)#;7_~Z zzcZj8?w=)>%5@HoCHE_&hnu(n_yQ-L(~VjpjjkbT7e)Dk5??fApg(d>vwLRJ-x{um z*Nt?DqTSxh_MIyogY!vf1mU1`Gld-&L)*43f6dilz`Q@HEz;+>MDDYv9u!s;WXeao zUq=TaL$P*IFgJzrGc>j1dDOd zed+=ZBo?w4mr$2)Ya}?vedDopomhW1`#P<%YOJ_j=WwClX0xJH-f@s?^tmzs_j7t!k zK@j^zS0Q|mM4tVP5Ram$VbS6|YDY&y?Q1r1joe9dj08#CM{RSMTU}(RCh`hp_Rkl- zGd|Cv~G@F{DLhCizAm9AN!^{rNs8hu!G@8RpnGx7e`-+K$ffN<0qjR zGq^$dj_Tv!n*?zOSyk5skI7JVKJ)3jysnjIu-@VSzQiP8r6MzudCU=~?v-U8yzo^7 zGf~SUTvEp+S*!X9uX!sq=o}lH;r{pzk~M*VA(uyQ`3C8!{C;)&6)95fv(cK!%Cuz$ z_Zal57H6kPN>25KNiI6z6F)jzEkh#%OqU#-__Xzy)KyH};81#N6OfX$$IXWzOn`Q& z4f$Z1t>)8&8PcYfEwY5UadU1yg+U*(1m2ZlHoC-!2?gB!!fLhmTl))D@dhvkx#+Yj z1O=LV{(T%{^IeCuFK>%QR!VZ4GnO5tK8a+thWE zg4VytZrwcS?7^ zuZfhYnB8dwd%VLO?DK7pV5Wi<(`~DYqOXn8#jUIL^)12*Dbhk4GmL_E2`WX&iT16o zk(t|hok(Y|v-wzn?4x34T)|+SfZP>fiq!><*%vnxGN~ypST-FtC+@TPv*vYv@iU!_ z@2gf|PrgQ?Ktf*9^CnJ(x*CtZVB8!OBfg0%!wL;Z8(tYYre0vcnPGlyCc$V(Ipl*P z_(J!a=o@vp^%Efme!K74(Ke7A>Y}|sxV+JL^aYa{~m%5#$$+R1? zGaQhZTTX!#s#=Xtpegqero$RNt&`4xn3g$)=y*;=N=Qai)}~`xtxI_N*#MMCIq#HFifT zz(-*m;pVH&+4bixL&Bbg)W5FN^bH87pAHp)zPkWNMfTFqS=l~AC$3FX3kQUSh_C?-ZftyClgM)o_D7cX$RGlEYblux0jv5 zTr|i-I3@ZPCGheCl~BGhImF)K4!9@?pC(gi3ozX=a!|r1)LFxy_8c&wY0<^{2cm|P zv6Y`QktY*;I)IUd5y3ne1CqpVanlY45z8hf4&$EUBnucDj16pDa4&GI&TArYhf*xh zdj>*%APH8(h~c>o@l#%T>R$e>rwVx_WUB|~V`p^JHsg*y12lzj&zF}w6W09HwB2yb z%Q~`es&(;7#*DUC_w-Dmt7|$*?TA_m;zB+-u{2;Bg{O}nV7G_@7~<)Bv8fH^G$XG8$(&{A zwXJK5LRK%M34(t$&NI~MHT{UQ9qN-V_yn|%PqC81EIiSzmMM=2zb`mIwiP_b)x+2M z7Gd`83h79j#SItpQ}luuf2uOU`my_rY5T{6P#BNlb%h%<#MZb=m@y5aW;#o1^2Z)SWo+b`y0gV^iRcZtz5!-05vF z7wNo=hc6h4hc&s@uL^jqRvD6thVYtbErDK9k!;+a0xoE0WL7zLixjn5;$fXvT=O3I zT6jI&^A7k6R{&5#lVjz#8%_RiAa2{di{`kx79K+j72$H(!ass|B%@l%KeeKchYLe_ z>!(JC2fxsv>XVen+Y42GeYPxMWqm`6F$(E<6^s|g(slNk!lL*6v^W2>f6hh^mE$s= z3D$)}{V5(Qm&A6bp%2Q}*GZ5Qrf}n7*Hr51?bJOyA-?B4vg6y_EX<*-e20h{=0Mxs zbuQGZ$fLyO5v$nQ&^kuH+mNq9O#MWSfThtH|0q1i!NrWj^S}_P;Q1OkYLW6U^?_7G zx2wg?CULj7))QU(n{$0JE%1t2dWrMi2g-Os{v|8^wK{@qlj%+1b^?NI z$}l2tjp0g>K3O+p%yK<9!XqmQ?E9>z&(|^Pi~aSRwI5x$jaA62GFz9%fmO3t3a>cq zK8Xbv=5Ps~4mKN5+Eqw12(!PEyedFXv~VLxMB~HwT1Vfo51pQ#D8e$e4pFZ{&RC2P z5gTIzl{3!&(tor^BwZfR8j4k{7Rq#`riKXP2O-Bh66#WWK2w=z;iD9GLl+3 zpHIaI4#lQ&S-xBK8PiQ%dwOh?%BO~DCo06pN7<^dnZCN@NzY{_Z1>rrB0U|nC&+!2 z2y!oBcTd2;@lzyk(B=TkyZ)zy0deK05*Q0zk+o$@nun`VI1Er7pjq>8V zNmlW{p7S^Btgb(TA}jL(uR>`0w8gHP^T~Sh5Tkip^spk4SBAhC{TZU}_Z)UJw-}zm zPq{KBm!k)?P{`-(9?LFt&YN4s%SIZ-9lJ!Ws~B%exHOeVFk3~}HewnnH(d)qkLQ_d z6h>O)pEE{vbOVw}E+jdYC^wM+AAhaI(YAibUc@B#_mDss0Ji&BK{WG`4 zOk>vSNq(Bq2IB@s>>Rxm6Wv?h;ZXkpb1l8u|+_qXWdC*jjcPCixq;!%BVPSp#hP zqo`%cNf&YoQXHC$D=D45RiT|5ngPlh?0T~?lUf*O)){K@*Kbh?3RW1j9-T?%lDk@y z4+~?wKI%Y!-=O|_IuKz|=)F;V7ps=5@g)RrE;;tvM$gUhG>jHcw2Hr@fS+k^Zr~>G z^JvPrZc}_&d_kEsqAEMTMJw!!CBw)u&ZVzmq+ZworuaE&TT>$pYsd9|g9O^0orAe8 z221?Va!l1|Y5X1Y?{G7rt1sX#qFA^?RLG^VjoxPf63;AS=_mVDfGJKg73L zsGdnTUD40y(>S##2l|W2Cy!H(@@5KBa(#gs`vlz}Y~$ot5VsqPQ{{YtjYFvIumZzt zA{CcxZLJR|4#{j7k~Tu*jkwz8QA|5G1$Cl895R`Zyp;irp1{KN){kB30O8P1W5;@bG znvX74roeMmQlUi=v9Y%(wl$ZC#9tKNFpvi3!C}f1m6Ct|l2g%psc{TJp)@yu)*e2> z((p0Fg*8gJ!|3WZke9;Z{8}&NRkv7iP=#_y-F}x^y?2m%-D_aj^)f04%mneyjo_;) z6qc_Zu$q37d~X``*eP~Q>I2gg%rrV8v=kDfpp$=%Vj}hF)^dsSWygoN(A$g*E=Do6FX?&(@F#7pbiJ`;c0c@Ul zDqW_90Wm#5f2L<(Lf3)3TeXtI7nhYwRm(F;*r_G6K@OPW4H(Y3O5SjUzBC}u3d|eQ8*8d@?;zUPE+i#QNMn=r(ap?2SH@vo*m z3HJ%XuG_S6;QbWy-l%qU;8x;>z>4pMW7>R}J%QLf%@1BY(4f_1iixd-6GlO7Vp*yU zp{VU^3?s?90i=!#>H`lxT!q8rk>W_$2~kbpz7eV{3wR|8E=8**5?qn8#n`*(bt1xRQrdGxyx2y%B$qmw#>ZV$c7%cO#%JM1lY$Y0q?Yuo> ze9KdJoiM)RH*SB%^;TAdX-zEjA7@%y=!0=Zg%iWK7jVI9b&Dk}0$Af&08KHo+ zOwDhFvA(E|ER%a^cdh@^wLUlmIv6?_3=BvX8jKk92L=Y}7Jf5OGMfh` zBdR1wFCi-i5@`9km{isRb0O%TX+f~)KNaEz{rXQa89`YIF;EN&gN)cigu6mNh>?Cm zAO&Im2flv6D{jwm+y<%WsPe4!89n~KN|7}Cb{Z;XweER73r}Qp2 zz}WP4j}U0&(uD&9yGy6`!+_v-S(yG*iytsTR#x_Rc>=6u^vnRDnf1gP{#2>`ffrAC% zTZ5WQ@hAK;P;>kX{D)mIXe4%a5p=LO1xXH@8T?mz7Q@d)$3pL{{B!2{-v70L*o1AO+|n5beiw~ zk@(>m?T3{2k2c;NWc^`4@P&Z?BjxXJ@;x1qhn)9Mn*IFdt_J-dIqx5#d`NfyfX~m( zIS~5)MfZ2Uy?_4W`47i}u0ZgPh<{D|w_d#;D}Q&U$Q-G}xM1A@1f{#%A$jh6Qp&0hQ<0bPOM z-{1Wm&p%%#eb_?x7i;bol EfAhh=DF6Tf literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..642d572ce9 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b86d33d312..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: java -install: true -script: mvn clean install -B -V - -matrix: - include: - - jdk: oraclejdk8 - after_success: - - mvn jacoco:report && bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" - dist: trusty - - jdk: openjdk11 - # There is an issue with the documentation so skip it - script: mvn -B -V clean install -DskipDistribution=true - - jdk: openjdk13 - # There is an issue with the documentation so skip it - script: mvn -B -V clean install -DskipDistribution=true - - jdk: openjdk14 - # There is an issue with the documentation so skip it - script: mvn -B -V clean install -DskipDistribution=true - # There is an issue with the documentation so skip it - - jdk: openjdk-ea - script: mvn -B -V clean install -DskipDistribution=true - allow_failures: - - jdk: openjdk-ea -deploy: - provider: script - script: "test ${TRAVIS_TEST_RESULT} -eq 0 && mvn -s etc/travis-settings.xml -DskipTests=true -DskipDistribution=true deploy" - skip_cleanup: true - on: - repo: mapstruct/mapstruct - branch: master - jdk: oraclejdk8 - -sudo: required -cache: - directories: - - $HOME/.m2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aaf8fd8646..6064004d73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ You love MapStruct but miss a certain feature? You found a bug and want to repor * Source code: [http://github.com/mapstruct/mapstruct](http://github.com/mapstruct/mapstruct) * Issue tracker: [https://github.com/mapstruct/mapstruct/issues](https://github.com/mapstruct/mapstruct/issues) * Discussions: Join the [mapstruct-users](https://groups.google.com/forum/?fromgroups#!forum/mapstruct-users) Google group -* CI build: [https://travis-ci.org/mapstruct/mapstruct/](https://travis-ci.org/mapstruct/mapstruct/) +* CI build: [https://github.com/mapstruct/mapstruct/actions/](https://github.com/mapstruct/mapstruct/actions) MapStruct follows the _Fork & Pull_ development approach. To get started just fork the [MapStruct repository](http://github.com/mapstruct/mapstruct) to your GitHub account and create a new topic branch for each change. Once you are done with your change, submit a [pull request](https://help.github.com/articles/using-pull-requests) against the MapStruct repo. diff --git a/etc/travis-settings.xml b/etc/ci-settings.xml similarity index 100% rename from etc/travis-settings.xml rename to etc/ci-settings.xml diff --git a/mvnw b/mvnw new file mode 100755 index 0000000000..41c0f0c23d --- /dev/null +++ b/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000000..86115719e5 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/parent/pom.xml b/parent/pom.xml index 83a59bf2cd..9bc07c5cda 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -81,8 +81,8 @@ - Travis CI - https://travis-ci.org/mapstruct/mapstruct + Github Actions + https://github.com/mapstruct/mapstruct/actions @@ -430,7 +430,6 @@ ${org.apache.maven.plugins.surefire.version} ${forkCount} - -Xms1024m -Xmx3072m @@ -577,12 +576,13 @@ true **/.idea/** + **/.mvn/** **/build-config/checkstyle.xml **/build-config/import-control.xml copyright.txt **/LICENSE.txt **/mapstruct.xml - **/travis-settings.xml + **/ci-settings.xml **/eclipse-formatter-config.xml **/forbidden-apis.txt **/checkstyle-for-generated-sources.xml @@ -596,6 +596,7 @@ .factorypath .checkstyle *.yml + mvnw* **/*.asciidoc **/binding.xjb diff --git a/readme.md b/readme.md index f8247297e2..5daba3f777 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) -[![Build Status](https://img.shields.io/travis/mapstruct/mapstruct.svg)](https://travis-ci.org/mapstruct/mapstruct) +[![Build Status](https://github.com/mapstruct/mapstruct/workflows/CI/badge.svg?branch=master)](https://github.com/mapstruct/mapstruct/actions?query=branch%3Amaster+workflow%3ACI) [![Coverage Status](https://img.shields.io/codecov/c/github/mapstruct/mapstruct.svg)](https://codecov.io/gh/mapstruct/mapstruct) [![Gitter](https://img.shields.io/gitter/room/mapstruct/mapstruct.svg)](https://gitter.im/mapstruct/mapstruct-users) [![Code Quality: Java](https://img.shields.io/lgtm/grade/java/g/mapstruct/mapstruct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/mapstruct/mapstruct/context:java) @@ -164,7 +164,7 @@ Make sure that you have the [m2e_apt](https://marketplace.eclipse.org/content/m2 * [Downloads](https://sourceforge.net/projects/mapstruct/files/) * [Issue tracker](https://github.com/mapstruct/mapstruct/issues) * [User group](https://groups.google.com/forum/?hl=en#!forum/mapstruct-users) -* [CI build](https://travis-ci.org/mapstruct/mapstruct/) +* [CI build](https://github.com/mapstruct/mapstruct/actions/) ## Licensing From 63c2edd3334fa1fde7106ea9dc38cbf1b2896510 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 23 Feb 2020 08:34:09 +0100 Subject: [PATCH 035/597] Reset locale and time zone in every test --- .../ap/test/builtin/DatatypeFactoryTest.java | 16 ++++++++++++++++ .../test/conversion/date/DateConversionTest.java | 9 +++++++++ .../java8time/Java8TimeConversionTest.java | 14 ++++++++++++++ .../conversion/jodatime/JodaConversionTest.java | 9 +++++++++ .../numbers/NumberFormatConversionTest.java | 9 +++++++++ .../NestedMappingMethodInvocationTest.java | 9 +++++++++ 6 files changed, 66 insertions(+) diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java index fe52452c73..17d7980ff8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java @@ -10,7 +10,10 @@ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import java.util.TimeZone; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mapstruct.ap.test.builtin.bean.CalendarProperty; @@ -34,6 +37,19 @@ @RunWith(AnnotationProcessorTestRunner.class) public class DatatypeFactoryTest { + private TimeZone originalTimeZone; + + @Before + public void setUp() { + originalTimeZone = TimeZone.getDefault(); + TimeZone.setDefault( TimeZone.getTimeZone( "Europe/Berlin" ) ); + } + + @After + public void tearDown() { + TimeZone.setDefault( originalTimeZone ); + } + @Test public void testNoConflictsWithOwnDatatypeFactory() throws ParseException { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java index c7a3cf3311..43ed5b3301 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java @@ -16,6 +16,7 @@ import java.util.List; import java.util.Locale; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,11 +41,19 @@ @RunWith(AnnotationProcessorTestRunner.class) public class DateConversionTest { + private Locale originalLocale; + @Before public void setDefaultLocale() { + originalLocale = Locale.getDefault(); Locale.setDefault( Locale.GERMAN ); } + @After + public void tearDown() { + Locale.setDefault( originalLocale ); + } + @Test @DisabledOnCompiler(Compiler.JDK11) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java index 573e300bfc..25ed958867 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java @@ -17,6 +17,8 @@ import java.util.Date; import java.util.TimeZone; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; @@ -33,6 +35,18 @@ @IssueKey("121") public class Java8TimeConversionTest { + private TimeZone originalTimeZone; + + @Before + public void setUp() { + originalTimeZone = TimeZone.getDefault(); + } + + @After + public void tearDown() { + TimeZone.setDefault( originalTimeZone ); + } + @Test public void testDateTimeToString() { Source src = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java index 30c96395b7..db61b85bc8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java @@ -16,6 +16,7 @@ import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,11 +37,19 @@ @IssueKey("75") public class JodaConversionTest { + private Locale originalLocale; + @Before public void setDefaultLocale() { + originalLocale = Locale.getDefault(); Locale.setDefault( Locale.GERMAN ); } + @After + public void tearDown() { + Locale.setDefault( originalLocale ); + } + @Test public void testDateTimeToString() { Source src = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java index ad2caecfd3..9b92182f6f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.conversion.numbers; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,11 +31,19 @@ @RunWith(AnnotationProcessorTestRunner.class) public class NumberFormatConversionTest { + private Locale originalLocale; + @Before public void setDefaultLocale() { + originalLocale = Locale.getDefault(); Locale.setDefault( Locale.ENGLISH ); } + @After + public void tearDown() { + Locale.setDefault( originalLocale ); + } + @Test public void shouldApplyStringConversions() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java index 11ac637f91..3738f56282 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java @@ -20,6 +20,7 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,11 +39,19 @@ public class NestedMappingMethodInvocationTest { public static final QName QNAME = new QName( "dont-care" ); + private Locale originalLocale; + @Before public void setDefaultLocale() { + originalLocale = Locale.getDefault(); Locale.setDefault( Locale.GERMAN ); } + @After + public void tearDown() { + Locale.setDefault( originalLocale ); + } + @Test @WithClasses( { OrderTypeToOrderDtoMapper.class, From f8a3924005d41a4a05872db8822b48a39cade864 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 7 Mar 2020 00:29:59 +0100 Subject: [PATCH 036/597] Use fix version for maven-processor-plugin Using the latest (4.0-beta1) does not work on Java 8 since it is compiled with Java 9 --- integrationtest/src/test/resources/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/integrationtest/src/test/resources/pom.xml b/integrationtest/src/test/resources/pom.xml index b386ce6e27..e209daf234 100644 --- a/integrationtest/src/test/resources/pom.xml +++ b/integrationtest/src/test/resources/pom.xml @@ -75,6 +75,7 @@ org.bsc.maven maven-processor-plugin + 3.3.3 process From bbd68baf648ba870df11e2c931dfc6de9de310fd Mon Sep 17 00:00:00 2001 From: Pawel Radzinski Date: Mon, 30 Mar 2020 21:11:07 +0200 Subject: [PATCH 037/597] Update chapter-10-advanced-mapping-options.asciidoc (#2050) Correct some typos and punctuation here and there. --- .../chapter-10-advanced-mapping-options.asciidoc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index a3836d048f..13205caa9b 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -4,7 +4,7 @@ This chapter describes several advanced options which allow to fine-tune the beh [[default-values-and-constants]] === Default values and constants -Default values can be specified to set a predefined value to a target property if the corresponding source property is `null`. Constants can be specified to set such a predefined value in any case. Default values and constants are specified as String values. When the target type is a primitive or a boxed type, the String value is taken literal. Bit / octal / decimal / hex patterns are allowed in such case as long as they are a valid literal. +Default values can be specified to set a predefined value to a target property if the corresponding source property is `null`. Constants can be specified to set such a predefined value in any case. Default values and constants are specified as String values. When the target type is a primitive or a boxed type, the String value is taken literal. Bit / octal / decimal / hex patterns are allowed in such a case as long as they are a valid literal. In all other cases, constant or default values are subject to type conversion either via built-in conversions or the invocation of other mapping methods in order to match the type required by the target property. A mapping with a constant must not include a reference to a source property. The following example shows some mappings using default values and constants: @@ -108,7 +108,7 @@ public interface SourceTargetMapper { ---- ==== -The example demonstrates how to use defaultExpression to set an `ID` field if the source field is null, this could be used to take the existing `sourceId` from the source object if it is set, or create a new `Id` if it isn't. Please note that the fully qualified package name is specified because MapStruct does not take care of the import of the `UUID` class (unless it’s used otherwise explicitly in the `SourceTargetMapper`). This can be resolved by defining imports on the @Mapper annotation ((see <>). +The example demonstrates how to use defaultExpression to set an `ID` field if the source field is null, this could be used to take the existing `sourceId` from the source object if it is set, or create a new `Id` if it isn't. Please note that the fully qualified package name is specified because MapStruct does not take care of the import of the `UUID` class (unless it’s used otherwise explicitly in the `SourceTargetMapper`). This can be resolved by defining imports on the @Mapper annotation (see <>). [[determining-result-type]] === Determining the result type @@ -189,8 +189,8 @@ The strategy works in a hierarchical fashion. Setting `Mapping#nullValueProperty [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. +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 of 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] @@ -209,7 +209,7 @@ MapStruct offers control over when to generate a `null` check. By default (`null .. calling another type conversion and subsequently calling the setter on the target. .. calling a mapping method and subsequently calling the setter on the target. -First calling a mapping method on the source property is not protected by a null check. Therefor generated mapping methods will do a null check prior to carrying out mapping on a source property. Handwritten mapping methods must take care of null value checking. They have the possibility to add 'meaning' to `null`. For instance: mapping `null` to a default value. +First calling a mapping method on the source property is not protected by a null check. Therefore generated mapping methods will do a null check prior to carrying out mapping on a source property. Handwritten mapping methods must take care of null value checking. They have the possibility to add 'meaning' to `null`. For instance: mapping `null` to a default value. The option `nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS` will always include a null check when source is non primitive, unless a source presence checker is defined on the source bean. @@ -227,7 +227,7 @@ The source presence checker name can be changed in the MapStruct service provide [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. +null check, regardless the value of the `NullValueCheckStrategy` to avoid addition of `null` to the target collection or map. ==== [[exceptions]] === Exceptions @@ -299,4 +299,4 @@ public CarDto carToCarDto(Car car) throws GearException { ---- ==== -Some **notes** on null checks. MapStruct does provide null checking only when required: when applying type-conversions or constructing a new type by invoking its constructor. This means that the user is responsible in hand-written code for returning valid non-null objects. Also null objects can be handed to hand-written code, since MapStruct does not want to make assumptions on the meaning assigned by the user to a null object. Hand-written code has to deal with this. \ No newline at end of file +Some **notes** on null checks. MapStruct does provide null checking only when required: when applying type-conversions or constructing a new type by invoking its constructor. This means that the user is responsible in hand-written code for returning valid non-null objects. Also null objects can be handed to hand-written code, since MapStruct does not want to make assumptions on the meaning assigned by the user to a null object. Hand-written code has to deal with this. From b7d5e557c1d5d327d7df0bb50dec37427bd7fda0 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Apr 2020 09:29:00 +0200 Subject: [PATCH 038/597] Checkstyle max line length should be 120 --- build-config/src/main/resources/build-config/checkstyle.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-config/src/main/resources/build-config/checkstyle.xml b/build-config/src/main/resources/build-config/checkstyle.xml index 213a0d21f5..5ae53dc46f 100644 --- a/build-config/src/main/resources/build-config/checkstyle.xml +++ b/build-config/src/main/resources/build-config/checkstyle.xml @@ -32,7 +32,7 @@ - + From 4f76208c62971e7eca45dab831a379dd342214a4 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 7 Mar 2020 18:16:38 +0100 Subject: [PATCH 039/597] #2021 Generate compilable code when Decorator is nested within the Mapper We need to treat the import of the decoratorType specially when it is nested. Calling addIfImportRequired is not the most correct approach since it would lead to checking if the type is to be imported and that would be false since the Decorator is a nested class within the Mapper. However, when generating the Decorator this is not needed, because the Decorator is a top level class itself In a nutshell creating the Decorator should have its own ProcessorContext, but it doesn't --- .../ap/internal/model/Decorator.java | 17 ++++++++++- .../ap/test/bugs/_2021/Issue2021Mapper.java | 22 +++++++++++++++ .../ap/test/bugs/_2021/Issue2021Test.java | 28 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java index ab37cb77d5..e61a25a475 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java @@ -125,7 +125,22 @@ private Decorator(TypeFactory typeFactory, String packageName, String name, Type @Override public SortedSet getImportTypes() { SortedSet importTypes = super.getImportTypes(); - addIfImportRequired( importTypes, decoratorType ); + // DecoratorType needs special handling in case it is nested + // calling addIfImportRequired is not the most correct approach since it would + // lead to checking if the type is to be imported and that would be false + // since the Decorator is a nested class within the Mapper. + // However, when generating the Decorator this is not needed, + // because the Decorator is a top level class itself + // In a nutshell creating the Decorator should have its own ProcessorContext, but it doesn't + if ( decoratorType.getPackageName().equalsIgnoreCase( getPackageName() ) ) { + if ( decoratorType.getTypeElement() != null && + decoratorType.getTypeElement().getNestingKind().isNested() ) { + importTypes.add( decoratorType ); + } + } + else { + importTypes.add( decoratorType ); + } return importTypes; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Mapper.java new file mode 100644 index 0000000000..4627f22323 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Mapper.java @@ -0,0 +1,22 @@ +/* + * 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._2021; + +import org.mapstruct.DecoratedWith; +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +@DecoratedWith(Issue2021Mapper.Decorator.class) +public interface Issue2021Mapper { + + abstract class Decorator implements Issue2021Mapper { + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Test.java new file mode 100644 index 0000000000..f111799d53 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Test.java @@ -0,0 +1,28 @@ +/* + * 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._2021; + +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; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2021") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2021Mapper.class +}) +public class Issue2021Test { + + @Test + public void shouldCompile() { + + } +} From 853ff7f74f0aebcd3b9e49f55d1778e088d7bb70 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Apr 2020 15:14:35 +0200 Subject: [PATCH 040/597] #2060: MapStruct should work properly on the module path --- .../itest/tests/MavenIntegrationTest.java | 6 ++++ .../src/test/resources/moduleInfoTest/pom.xml | 24 ++++++++++++++ .../src/main/java/module-info.java | 9 ++++++ .../mapstruct/itest/modules/CustomerDto.java | 31 +++++++++++++++++++ .../itest/modules/CustomerEntity.java | 31 +++++++++++++++++++ .../itest/modules/CustomerMapper.java | 23 ++++++++++++++ .../mapstruct/itest/modules/ModulesTest.java | 28 +++++++++++++++++ .../ap/internal/conversion/Conversions.java | 20 +++++++++--- 8 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 integrationtest/src/test/resources/moduleInfoTest/pom.xml create mode 100644 integrationtest/src/test/resources/moduleInfoTest/src/main/java/module-info.java create mode 100644 integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerDto.java create mode 100644 integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerEntity.java create mode 100644 integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerMapper.java create mode 100644 integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index e579f031f0..e7e26d9e1c 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -130,4 +130,10 @@ void superTypeGenerationTest() { void targetTypeGenerationTest() { } + @ProcessorTest(baseDir = "moduleInfoTest") + @EnabledForJreRange(min = JRE.JAVA_11) + void moduleInfoTest() { + + } + } diff --git a/integrationtest/src/test/resources/moduleInfoTest/pom.xml b/integrationtest/src/test/resources/moduleInfoTest/pom.xml new file mode 100644 index 0000000000..4561a1b69e --- /dev/null +++ b/integrationtest/src/test/resources/moduleInfoTest/pom.xml @@ -0,0 +1,24 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + modulesTest + jar + + diff --git a/integrationtest/src/test/resources/moduleInfoTest/src/main/java/module-info.java b/integrationtest/src/test/resources/moduleInfoTest/src/main/java/module-info.java new file mode 100644 index 0000000000..07b4a62b06 --- /dev/null +++ b/integrationtest/src/test/resources/moduleInfoTest/src/main/java/module-info.java @@ -0,0 +1,9 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +module test.mapstruct { + requires org.mapstruct; + exports org.mapstruct.itest.modules; +} diff --git a/integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerDto.java b/integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerDto.java new file mode 100644 index 0000000000..30ebb32968 --- /dev/null +++ b/integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerDto.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.modules; + +/** + * @author Filip Hrisafov + */ +public class CustomerDto { + + private String name; + private String email; + + 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; + } +} diff --git a/integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerEntity.java b/integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerEntity.java new file mode 100644 index 0000000000..a52a887f08 --- /dev/null +++ b/integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerEntity.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.modules; + +/** + * @author Filip Hrisafov + */ +public class CustomerEntity { + + private String name; + private String mail; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } +} diff --git a/integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerMapper.java b/integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerMapper.java new file mode 100644 index 0000000000..119bffaae8 --- /dev/null +++ b/integrationtest/src/test/resources/moduleInfoTest/src/main/java/org/mapstruct/itest/modules/CustomerMapper.java @@ -0,0 +1,23 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.modules; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CustomerMapper { + + CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class ); + + @Mapping(target = "mail", source = "email") + CustomerEntity fromDto(CustomerDto record); + +} diff --git a/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java b/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java new file mode 100644 index 0000000000..805662c806 --- /dev/null +++ b/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java @@ -0,0 +1,28 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.modules; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.mapstruct.itest.modules.CustomerDto; +import org.mapstruct.itest.modules.CustomerEntity; +import org.mapstruct.itest.modules.CustomerMapper; + +public class ModulesTest { + + @Test + public void shouldMapRecord() { + CustomerDto dto = new CustomerDto(); + dto.setName( "Kermit" ); + dto.setEmail( "kermit@test.com" ); + CustomerEntity customer = CustomerMapper.INSTANCE.fromDto( dto ); + + assertThat( customer ).isNotNull(); + assertThat( customer.getName() ).isEqualTo( "Kermit" ); + assertThat( customer.getMail() ).isEqualTo( "kermit@test.com" ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 6b2d5e2649..ad5fe85db3 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -184,9 +184,8 @@ public Conversions(TypeFactory typeFactory) { register( Enum.class, String.class, new EnumStringConversion() ); register( Date.class, String.class, new DateToStringConversion() ); register( BigDecimal.class, BigInteger.class, new BigDecimalToBigIntegerConversion() ); - register( Date.class, Time.class, new DateToSqlTimeConversion() ); - register( Date.class, java.sql.Date.class, new DateToSqlDateConversion() ); - register( Date.class, Timestamp.class, new DateToSqlTimestampConversion() ); + + registerJavaTimeSqlConversions(); // java.util.Currency <~> String register( Currency.class, String.class, new CurrencyToStringConversion() ); @@ -227,15 +226,28 @@ private void registerJava8TimeConversions() { register( ZonedDateTime.class, Date.class, new JavaZonedDateTimeToDateConversion() ); register( LocalDateTime.class, Date.class, new JavaLocalDateTimeToDateConversion() ); register( LocalDate.class, Date.class, new JavaLocalDateToDateConversion() ); - register( LocalDate.class, java.sql.Date.class, new JavaLocalDateToSqlDateConversion() ); register( Instant.class, Date.class, new JavaInstantToDateConversion() ); } + private void registerJavaTimeSqlConversions() { + if ( isJavaSqlAvailable() ) { + register( LocalDate.class, java.sql.Date.class, new JavaLocalDateToSqlDateConversion() ); + + register( Date.class, Time.class, new DateToSqlTimeConversion() ); + register( Date.class, java.sql.Date.class, new DateToSqlDateConversion() ); + register( Date.class, Timestamp.class, new DateToSqlTimestampConversion() ); + } + } + private boolean isJodaTimeAvailable() { return typeFactory.isTypeAvailable( JodaTimeConstants.DATE_TIME_FQN ); } + private boolean isJavaSqlAvailable() { + return typeFactory.isTypeAvailable( "java.sql.Date" ); + } + private void registerNativeTypeConversion(Class sourceType, Class targetType) { if ( sourceType.isPrimitive() && targetType.isPrimitive() ) { register( sourceType, targetType, new PrimitiveToPrimitiveConversion( sourceType ) ); From c410379f837d2cd45ebfaec58754e6f040b4ff2f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Apr 2020 16:46:20 +0200 Subject: [PATCH 041/597] #1553: Update tycho-compiler-jdt to latest 1.6.0 version Disable one test in GenericsHierarchyTest for Eclipse on Java 8 due to a bug in the Tycho compiler. Disable freeBuilder integration test for Eclipse since there are some problems in the second round of annotation processing (no ModelElementProcessor(s) are found) --- .../itest/tests/MavenIntegrationTest.java | 3 +- parent/pom.xml | 19 ++++++- .../generics/GenericsHierarchyTest.java | 9 +++ .../conversion/date/DateConversionTest.java | 20 +++++-- .../jodatime/JodaConversionTest.java | 10 +++- .../ap/test/verbose/VerboseTest.java | 55 +++++++++++++++---- .../runner/AnnotationProcessorTestRunner.java | 5 +- .../ap/testutil/runner/Compiler.java | 2 +- .../testutil/runner/DisabledOnCompiler.java | 2 +- .../runner/EclipseCompilingStatement.java | 18 +++++- .../ap/testutil/runner/EnabledOnCompiler.java | 2 +- .../InnerAnnotationProcessorRunner.java | 12 +++- 12 files changed, 127 insertions(+), 30 deletions(-) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index e7e26d9e1c..97da699cbe 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -36,8 +36,7 @@ void externalBeanJarTest() { } @ProcessorTest(baseDir = "freeBuilderBuilderTest", processorTypes = { - ProcessorTest.ProcessorType.JAVAC, - ProcessorTest.ProcessorType.ECLIPSE_JDT + ProcessorTest.ProcessorType.JAVAC }) void freeBuilderBuilderTest() { } diff --git a/parent/pom.xml b/parent/pom.xml index 9bc07c5cda..37551e2ed2 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -27,7 +27,7 @@ 3.0.0-M3 3.1.0 4.0.3.RELEASE - 0.26.0 + 1.6.0 8.29 5.6.0 @@ -237,13 +237,28 @@ org.codehaus.plexus plexus-container-default - 1.6 + 1.7.1 + + + org.codehaus.plexus + plexus-component-annotations + 1.7.1 + + + org.codehaus.plexus + plexus-classworlds + 2.5.1 org.codehaus.plexus plexus-utils 3.0.20 + + commons-io + commons-io + 2.5 + diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/generics/GenericsHierarchyTest.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/generics/GenericsHierarchyTest.java index d79f544541..282a8e8efb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/generics/GenericsHierarchyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/generics/GenericsHierarchyTest.java @@ -12,6 +12,8 @@ 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.Compiler; +import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; /** * @author Andreas Gudian @@ -34,6 +36,13 @@ public class GenericsHierarchyTest { @Test + // Disabled due to a bug in the Eclipse compiler (https://bugs.eclipse.org/bugs/show_bug.cgi?id=540101) + // See https://github.com/mapstruct/mapstruct/issues/1553 and https://github.com/mapstruct/mapstruct/pull/1587 + // for more information + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) public void determinesAnimalKeyGetter() { AbstractAnimal source = new Elephant(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java index 43ed5b3301..2d98b73bc9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java @@ -55,7 +55,10 @@ public void tearDown() { } @Test - @DisabledOnCompiler(Compiler.JDK11) + @DisabledOnCompiler({ + Compiler.JDK11, + Compiler.ECLIPSE11 + }) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversions() { Source source = new Source(); @@ -70,7 +73,10 @@ public void shouldApplyDateFormatForConversions() { } @Test - @EnabledOnCompiler(Compiler.JDK11) + @EnabledOnCompiler({ + Compiler.JDK11, + Compiler.ECLIPSE11 + }) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionsJdk11() { Source source = new Source(); @@ -85,7 +91,10 @@ public void shouldApplyDateFormatForConversionsJdk11() { } @Test - @DisabledOnCompiler(Compiler.JDK11) + @DisabledOnCompiler({ + Compiler.JDK11, + Compiler.ECLIPSE11 + }) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionInReverseMapping() { Target target = new Target(); @@ -102,7 +111,10 @@ public void shouldApplyDateFormatForConversionInReverseMapping() { } @Test - @EnabledOnCompiler(Compiler.JDK11) + @EnabledOnCompiler({ + Compiler.JDK11, + Compiler.ECLIPSE11 + }) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionInReverseMappingJdk11() { Target target = new Target(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java index db61b85bc8..9f61c222db 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java @@ -87,7 +87,10 @@ public void testLocalTimeToString() { } @Test - @DisabledOnCompiler(Compiler.JDK11) + @DisabledOnCompiler({ + Compiler.JDK11, + Compiler.ECLIPSE11 + }) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void testSourceToTargetMappingForStrings() { Source src = new Source(); @@ -115,7 +118,10 @@ public void testSourceToTargetMappingForStrings() { } @Test - @EnabledOnCompiler(Compiler.JDK11) + @EnabledOnCompiler({ + Compiler.JDK11, + Compiler.ECLIPSE11 + }) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void testSourceToTargetMappingForStringsJdk11() { Source src = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java b/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java index 0d8003f91f..c042e3d073 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java @@ -30,7 +30,10 @@ public class VerboseTest { @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses({ CreateBeanMapping.class, CreateBeanMappingConfig.class }) @ExpectedNote("^MapStruct: Using accessor naming strategy:.*DefaultAccessorNamingStrategy.*$") @@ -41,7 +44,10 @@ public void testGeneralMessages() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @WithServiceImplementation(provides = BuilderProvider.class, value = ImmutablesBuilderProvider.class) @WithServiceImplementation(provides = AccessorNamingStrategy.class, value = ImmutablesAccessorNamingStrategy.class) @ProcessorOption(name = "mapstruct.verbose", value = "true") @@ -54,7 +60,10 @@ public void testGeneralWithOtherSPI() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @WithServiceImplementation(provides = AstModifyingAnnotationProcessor.class, value = AstModifyingAnnotationProcessorSaysNo.class) @ProcessorOption(name = "mapstruct.verbose", value = "true") @@ -73,7 +82,10 @@ public void testDeferred() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses({ CreateBeanMapping.class, CreateBeanMappingConfig.class }) @ExpectedNote("^- MapStruct: creating bean mapping method implementation for.*$") @@ -82,7 +94,10 @@ public void testCreateBeanMapping() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(SelectBeanMapping.class) @ExpectedNote("^- MapStruct: creating bean mapping method implementation for.*$") @@ -91,7 +106,10 @@ public void testSelectBeanMapping() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(ValueMapping.class) @ExpectedNote("^- MapStruct: creating value mapping method implementation for.*$") @@ -99,7 +117,10 @@ public void testValueMapping() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(CreateIterableMapping.class) @ExpectedNote("^- MapStruct: creating iterable mapping method implementation for.*$") @@ -108,7 +129,10 @@ public void testVerboseCreateIterableMapping() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(SelectIterableMapping.class) @ExpectedNote("^- MapStruct: creating iterable mapping method implementation for.*$") @@ -117,7 +141,10 @@ public void testVerboseSelectingIterableMapping() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(SelectStreamMapping.class) @ExpectedNote("^- MapStruct: creating stream mapping method implementation for.*$") @@ -125,7 +152,10 @@ public void testVerboseSelectingStreamMapping() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(CreateMapMapping.class) @ExpectedNote("^- MapStruct: creating map mapping method implementation for.*$") @@ -135,7 +165,10 @@ public void testVerboseCreateMapMapping() { } @Test - @DisabledOnCompiler( Compiler.ECLIPSE ) + @DisabledOnCompiler( { + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + } ) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(SelectMapMapping.class) @ExpectedNote("^- MapStruct: creating map mapping method implementation for.*$") diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java index 6ddccf0eb7..2891121afb 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java @@ -74,7 +74,10 @@ else if ( IS_AT_LEAST_JAVA_9 ) { // Current tycho-compiler-jdt (0.26.0) is not compatible with Java 11 // Updating to latest version 1.3.0 fails some tests // Once https://github.com/mapstruct/mapstruct/pull/1587 is resolved we can remove this line - return Arrays.asList( new InnerAnnotationProcessorRunner( klass, Compiler.JDK11 ) ); + return Arrays.asList( + new InnerAnnotationProcessorRunner( klass, Compiler.JDK11 ), + new InnerAnnotationProcessorRunner( klass, Compiler.ECLIPSE11 ) + ); } return Arrays.asList( diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/Compiler.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/Compiler.java index d59a7ed0dc..4b24bb091b 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/Compiler.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/Compiler.java @@ -10,5 +10,5 @@ * */ public enum Compiler { - JDK, JDK11, ECLIPSE; + JDK, JDK11, ECLIPSE, ECLIPSE11; } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/DisabledOnCompiler.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/DisabledOnCompiler.java index 41bcdb1000..6fdfa37268 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/DisabledOnCompiler.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/DisabledOnCompiler.java @@ -22,5 +22,5 @@ /** * @return The compiler to use. */ - Compiler value(); + Compiler[] value(); } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingStatement.java index a0617322ec..4dac535389 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingStatement.java @@ -9,6 +9,8 @@ import java.util.List; import java.util.Set; +import javax.lang.model.SourceVersion; + import org.codehaus.plexus.compiler.CompilerConfiguration; import org.codehaus.plexus.compiler.CompilerException; import org.codehaus.plexus.compiler.CompilerResult; @@ -108,9 +110,10 @@ public CompilationOutcomeDescriptor compileInOtherClassloader(CompilationRequest config.setGeneratedSourcesDirectory( new File( sourceOutputDir ) ); config.setAnnotationProcessors( new String[] { MappingProcessor.class.getName() } ); config.setSourceFiles( sourceFiles ); + String version = getSourceVersion(); config.setShowWarnings( false ); - config.setSourceVersion( "1.8" ); - config.setTargetVersion( "1.8" ); + config.setSourceVersion( version ); + config.setTargetVersion( version ); for ( String option : compilationRequest.getProcessorOptions() ) { config.addCompilerCustomArgument( option, null ); @@ -128,13 +131,22 @@ public CompilationOutcomeDescriptor compileInOtherClassloader(CompilationRequest sourceDir, compilerResult ); } + + private static String getSourceVersion() { + SourceVersion latest = SourceVersion.latest(); + if ( latest == SourceVersion.RELEASE_8 ) { + return "1.8"; + } + return "11"; + } + } private static List buildEclipseCompilerClasspath() { String[] whitelist = new String[] { "tycho-compiler", - "org.eclipse.jdt.", + "ecj", "plexus-compiler-api", "plexus-component-annotations" }; diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EnabledOnCompiler.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EnabledOnCompiler.java index e297bd2c65..7c6d79e0fc 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EnabledOnCompiler.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EnabledOnCompiler.java @@ -22,5 +22,5 @@ /** * @return The compiler to use. */ - Compiler value(); + Compiler[] value(); } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/InnerAnnotationProcessorRunner.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/InnerAnnotationProcessorRunner.java index 4850a48704..50ab9c6eb5 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/InnerAnnotationProcessorRunner.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/InnerAnnotationProcessorRunner.java @@ -68,12 +68,20 @@ protected boolean isIgnored(FrameworkMethod child) { protected boolean isIgnoredForCompiler(FrameworkMethod child) { EnabledOnCompiler enabledOnCompiler = child.getAnnotation( EnabledOnCompiler.class ); if ( enabledOnCompiler != null ) { - return enabledOnCompiler.value() != compiler; + for ( Compiler value : enabledOnCompiler.value() ) { + if ( value != compiler ) { + return true; + } + } } DisabledOnCompiler disabledOnCompiler = child.getAnnotation( DisabledOnCompiler.class ); if ( disabledOnCompiler != null ) { - return disabledOnCompiler.value() == compiler; + for ( Compiler value : disabledOnCompiler.value() ) { + if ( value == compiler ) { + return true; + } + } } return false; From a845197b0bab66608bdfbeee49bea77ad8c06582 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 10 Apr 2020 10:04:51 +0200 Subject: [PATCH 042/597] #2056: Handle null TypeMirror in uses and import gracefully Due to a bug in javac (JDK-8229535) for an annotation with Class values, instead of returning a TypeMirror with TypeKind#ERROR the compiler returns the string "". Eclipse doesn't have this problem currently. --- .../itest/tests/MavenIntegrationTest.java | 21 +++++++ .../usesTypeGenerationTest/generator/pom.xml | 43 +++++++++++++ .../UsesTypeGenerationProcessor.java | 60 +++++++++++++++++++ .../javax.annotation.processing.Processor | 4 ++ .../resources/usesTypeGenerationTest/pom.xml | 27 +++++++++ .../usesTypeGenerationTest/usage/pom.xml | 52 ++++++++++++++++ .../itest/usestypegeneration/usage/Order.java | 22 +++++++ .../usestypegeneration/usage/OrderDto.java | 22 +++++++ .../usestypegeneration/usage/OrderMapper.java | 20 +++++++ .../usage/GeneratedUsesTypeTest.java | 27 +++++++++ parent/pom.xml | 2 +- .../org/mapstruct/ap/MappingProcessor.java | 9 ++- .../model/source/DelegatingOptions.java | 17 ++++-- 13 files changed, 318 insertions(+), 8 deletions(-) create mode 100644 integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml create mode 100644 integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/usestypegeneration/UsesTypeGenerationProcessor.java create mode 100644 integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor create mode 100644 integrationtest/src/test/resources/usesTypeGenerationTest/pom.xml create mode 100644 integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml create mode 100644 integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/Order.java create mode 100644 integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/OrderDto.java create mode 100644 integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/OrderMapper.java create mode 100644 integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 97da699cbe..13cd774754 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -135,4 +135,25 @@ void moduleInfoTest() { } + /** + * Tests usage of MapStruct with another processor that generates the uses type of a mapper. + */ + @ProcessorTest(baseDir = "usesTypeGenerationTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC + }) + void usesTypeGenerationTest() { + } + + /** + * Tests usage of MapStruct with another processor that generates the uses type of a mapper. + */ + @ProcessorTest(baseDir = "usesTypeGenerationTest", processorTypes = { + ProcessorTest.ProcessorType.ECLIPSE_JDT + }) + @EnabledForJreRange(min = JRE.JAVA_11) + // For some reason the second run with eclipse does not load the ModelElementProcessor(s) on java 8, + // therefore we run this only on Java 11 + void usesTypeGenerationTestEclipse() { + } + } diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml new file mode 100644 index 0000000000..6ac3a01297 --- /dev/null +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + + org.mapstruct.itest + itest-usestypegeneration-aggregator + 1.0.0 + ../pom.xml + + + itest-usestypegeneration-generator + jar + + + + junit + junit + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -proc:none + + + + + + diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/usestypegeneration/UsesTypeGenerationProcessor.java b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/usestypegeneration/UsesTypeGenerationProcessor.java new file mode 100644 index 0000000000..2c17279ff8 --- /dev/null +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/usestypegeneration/UsesTypeGenerationProcessor.java @@ -0,0 +1,60 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.usestypegeneration; + +import java.io.IOException; +import java.io.Writer; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.TypeElement; +import javax.tools.JavaFileObject; + +/** + * Generate conversion uses. + * + * @author Filip Hrisafov + * + */ +@SupportedAnnotationTypes("*") +public class UsesTypeGenerationProcessor extends AbstractProcessor { + + private boolean hasRun = false; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if ( !hasRun ) { + try { + JavaFileObject dto = processingEnv.getFiler().createSourceFile( "org.mapstruct.itest.usestypegeneration.usage.StringUtils" ); + Writer writer = dto.openWriter(); + + writer.append( "package org.mapstruct.itest.usestypegeneration.usage;" ); + writer.append( "\n" ); + writer.append( "public class StringUtils {" ); + writer.append( "\n" ); + writer.append( " public static String upperCase(String string) {" ); + writer.append( "\n" ); + writer.append( " return string == null ? null : string.toUpperCase();" ); + writer.append( "\n" ); + writer.append( " }" ); + writer.append( "\n" ); + writer.append( "}" ); + + writer.flush(); + writer.close(); + } + catch (IOException e) { + throw new RuntimeException( e ); + } + + hasRun = true; + } + + return false; + } +} diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000000..57be8919b5 --- /dev/null +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1,4 @@ +# Copyright MapStruct Authors. +# +# Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 +org.mapstruct.itest.usestypegeneration.UsesTypeGenerationProcessor diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/pom.xml b/integrationtest/src/test/resources/usesTypeGenerationTest/pom.xml new file mode 100644 index 0000000000..7c0d555eaa --- /dev/null +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/pom.xml @@ -0,0 +1,27 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + org.mapstruct.itest + itest-usestypegeneration-aggregator + pom + + + generator + usage + + diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml new file mode 100644 index 0000000000..e8b8d7bf07 --- /dev/null +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + + org.mapstruct.itest + itest-usestypegeneration-aggregator + 1.0.0 + ../pom.xml + + + itest-usestypegeneration-usage + jar + + + + junit + junit + 4.12 + test + + + org.mapstruct.itest + itest-usestypegeneration-generator + 1.0.0 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -XprintProcessorInfo + -XprintRounds + + -proc:none + + + + + diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/Order.java b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/Order.java new file mode 100644 index 0000000000..4d5a102aed --- /dev/null +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/Order.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.usestypegeneration.usage; + +/** + * @author Filip Hrisafov + */ +public class Order { + + private String item; + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } +} diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/OrderDto.java b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/OrderDto.java new file mode 100644 index 0000000000..69951da5b2 --- /dev/null +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/OrderDto.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.usestypegeneration.usage; + +/** + * @author Filip Hrisafov + */ +public class OrderDto { + + private String item; + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } +} diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/OrderMapper.java b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/OrderMapper.java new file mode 100644 index 0000000000..8b33ab17d9 --- /dev/null +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/usestypegeneration/usage/OrderMapper.java @@ -0,0 +1,20 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.usestypegeneration.usage; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = StringUtils.class) +public interface OrderMapper { + + OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); + + OrderDto orderToDto(Order order); +} diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java new file mode 100644 index 0000000000..e715e66620 --- /dev/null +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java @@ -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.itest.usestypegeneration.usage; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration test for using MapStruct with another annotation processor that generates the other mappers for uses + * + * @author Filip Hrisafov + */ +public class GeneratedUsesTypeTest { + + @Test + public void considersPropertiesOnGeneratedSourceAndTargetTypes() { + Order order = new Order(); + order.setItem( "my item" ); + + OrderDto dto = OrderMapper.INSTANCE.orderToDto( order ); + assertThat( dto.getItem() ).isEqualTo( "MY ITEM" ); + } +} diff --git a/parent/pom.xml b/parent/pom.xml index 37551e2ed2..3efe0ad8fb 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -21,7 +21,7 @@ UTF-8 - 1.0.0.Alpha1 + 1.0.0.Alpha2 3.0.0-M1 3.0.0-M3 diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 546f06256f..e7e52be695 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -29,6 +29,7 @@ import javax.lang.model.element.Name; import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementKindVisitor6; import javax.tools.Diagnostic.Kind; @@ -177,7 +178,8 @@ else if ( !deferredMappers.isEmpty() ) { erroneousElementName = ( (QualifiedNameable) erroneousElement ).getQualifiedName().toString(); } else { - erroneousElementName = erroneousElement.getSimpleName().toString(); + erroneousElementName = + erroneousElement != null ? erroneousElement.getSimpleName().toString() : null; } // When running on Java 8 we need to fetch the deferredMapperElement again. @@ -265,9 +267,10 @@ processingEnv, options, roundContext, getDeclaredTypesNotToBeImported( mapperEle processMapperTypeElement( context, mapperElement ); } catch ( TypeHierarchyErroneousException thie ) { - Element erroneousElement = roundContext.getAnnotationProcessorContext() + TypeMirror erroneousType = thie.getType(); + Element erroneousElement = erroneousType != null ? roundContext.getAnnotationProcessorContext() .getTypeUtils() - .asElement( thie.getType() ); + .asElement( erroneousType ) : null; if ( options.isVerbose() ) { processingEnv.getMessager().printMessage( Kind.NOTE, "MapStruct: referred types not available (yet), deferring mapper: " diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java index b9dc068d07..8bc6ab982b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -8,7 +8,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; @@ -21,6 +20,7 @@ import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; +import org.mapstruct.ap.spi.TypeHierarchyErroneousException; /** * Chain Of Responsibility Pattern. @@ -110,9 +110,18 @@ DelegatingOptions next() { } protected Set toDeclaredTypes(List in, Set next) { - Set result = in.stream() - .map( DeclaredType.class::cast ) - .collect( Collectors.toCollection( LinkedHashSet::new ) ); + Set result = new LinkedHashSet<>(); + for ( TypeMirror typeMirror : in ) { + if ( typeMirror == null ) { + // When a class used in uses or imports is created by another annotation processor + // then javac will not return correct TypeMirror with TypeKind#ERROR, but rather a string "" + // the gem tools would return a null TypeMirror in that case. + // Therefore throw TypeHierarchyErroneousException so we can postpone the generation of the mapper + throw new TypeHierarchyErroneousException( typeMirror ); + } + + result.add( (DeclaredType) typeMirror ); + } result.addAll( next ); return result; } From 6797afb647cbadb265cb518ca3569f90fdc1aae9 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 12 Apr 2020 01:06:03 +0200 Subject: [PATCH 043/597] Use exact message for Diagnostic test messages assertions --- .../model/BuilderFinisherMethodResolver.java | 19 ++- .../ap/internal/model/common/TypeFactory.java | 14 ++- .../ReferencedAccessibilityTest.java | 18 +-- .../ap/test/bugs/_1005/Issue1005Test.java | 16 ++- .../ap/test/bugs/_1029/Issue1029Test.java | 8 +- .../ap/test/bugs/_1153/Issue1153Test.java | 14 +-- .../ap/test/bugs/_1242/Issue1242Test.java | 17 +-- .../ap/test/bugs/_1283/Issue1283Test.java | 6 +- .../ap/test/bugs/_1353/Issue1353Test.java | 4 +- .../ap/test/bugs/_1457/Issue1457Test.java | 23 ++-- .../ap/test/bugs/_1698/Issue1698Test.java | 4 +- .../ap/test/bugs/_1904/Issue1904Test.java | 9 +- .../ap/test/bugs/_590/Issue590Test.java | 10 +- .../ap/test/bugs/_631/Issue631Test.java | 4 +- .../ap/test/bugs/_880/Issue880Test.java | 2 +- .../multiple/MultipleBuilderMapperTest.java | 36 ++---- .../simple/SimpleImmutableBuilderTest.java | 2 +- .../ErroneousCollectionMappingTest.java | 51 ++++---- .../forged/CollectionMappingTest.java | 23 ++-- .../immutabletarget/ImmutableProductTest.java | 2 +- .../collection/wildcard/WildCardTest.java | 8 +- .../ContextParameterErroneousTest.java | 2 +- .../erroneous/InvalidDateFormatTest.java | 24 ++-- .../conversion/lossy/LossyConversionTest.java | 22 ++-- .../ap/test/decorator/DecoratorTest.java | 2 +- .../test/defaultvalue/DefaultValueTest.java | 118 +++++++++--------- .../ap/test/dependency/OrderingTest.java | 6 +- .../AmbiguousAnnotatedFactoryTest.java | 18 +-- .../ambiguousfactorymethod/FactoryTest.java | 12 +- .../ErroneousMappingsTest.java | 14 +-- .../ErroneousPropertyMappingTest.java | 44 ++++--- .../typemismatch/ErroneousMappingsTest.java | 16 +-- .../ap/test/ignore/IgnorePropertyTest.java | 4 +- .../attribute/AttributeInheritanceTest.java | 3 +- .../complex/ComplexInheritanceTest.java | 12 +- .../InheritFromConfigTest.java | 54 ++++---- .../erroneous/ErroneousStreamMappingTest.java | 57 ++++----- .../forged/ForgedStreamMappingTest.java | 7 +- .../java8stream/wildcard/WildCardTest.java | 8 +- .../ap/test/mapperconfig/ConfigTest.java | 4 +- .../mappingcontrol/MappingControlTest.java | 20 ++- .../SuggestMostSimilarNameTest.java | 17 ++- ...DisablingNestedSimpleBeansMappingTest.java | 10 +- .../nestedbeans/DottedErrorMessageTest.java | 110 ++++++++-------- .../exclusions/ErroneousJavaInternalTest.java | 27 ++-- .../custom/ErroneousCustomExclusionTest.java | 8 +- .../NestedSourcePropertiesTest.java | 2 +- .../NullValuePropertyMappingTest.java | 10 +- .../InheritInverseConfigurationTest.java | 27 ++-- .../selection/generics/ConversionTest.java | 75 ++++++----- .../selection/qualifier/QualifierTest.java | 30 ++--- .../resulttype/InheritanceSelectionTest.java | 26 ++-- .../test/source/constants/ConstantsTest.java | 33 +++-- .../source/constants/SourceConstantsTest.java | 30 ++--- .../java/JavaDefaultExpressionTest.java | 14 +-- .../expressions/java/JavaExpressionTest.java | 4 +- .../ManySourceArgumentsTest.java | 10 +- .../targetthis/TargetThisMappingTest.java | 4 +- .../template/InheritConfigurationTest.java | 34 ++--- .../unmappedsource/UnmappedSourceTest.java | 8 +- .../unmappedtarget/UnmappedProductTest.java | 12 +- .../test/updatemethods/UpdateMethodsTest.java | 24 ++-- .../enum2enum/EnumToEnumMappingTest.java | 14 +-- .../enum2string/EnumToStringMappingTest.java | 4 +- .../string2enum/StringToEnumMappingTest.java | 4 +- .../ap/test/verbose/VerboseTest.java | 10 +- .../compilation/annotation/Diagnostic.java | 8 ++ .../model/DiagnosticDescriptor.java | 21 +++- .../testutil/runner/CompilingStatement.java | 28 +++-- 69 files changed, 753 insertions(+), 588 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java index d72e7cef9c..9b7912f752 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java @@ -7,10 +7,12 @@ import java.util.Collection; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; import org.mapstruct.ap.internal.model.common.BuilderType; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.util.Extractor; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; @@ -23,6 +25,19 @@ public class BuilderFinisherMethodResolver { private static final String DEFAULT_BUILD_METHOD_NAME = "build"; + private static final Extractor EXECUTABLE_ELEMENT_NAME_EXTRACTOR = + executableElement -> { + StringBuilder sb = new StringBuilder( executableElement.getSimpleName() ); + + sb.append( '(' ); + for ( VariableElement parameter : executableElement.getParameters() ) { + sb.append( parameter ); + } + + sb.append( ')' ); + return sb.toString(); + }; + private BuilderFinisherMethodResolver() { } @@ -57,7 +72,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp buildMethodPattern, builderType.getBuilder(), builderType.getBuildingType(), - Strings.join( buildMethods, ", " ) + Strings.join( buildMethods, ", ", EXECUTABLE_ELEMENT_NAME_EXTRACTOR ) ); } else { @@ -68,7 +83,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp buildMethodPattern, builderType.getBuilder(), builderType.getBuildingType(), - Strings.join( buildMethods, ", " ) + Strings.join( buildMethods, ", ", EXECUTABLE_ELEMENT_NAME_EXTRACTOR ) ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 822ba0dd6d..594c2c7f70 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -68,7 +68,19 @@ public class TypeFactory { private static final Extractor BUILDER_INFO_CREATION_METHOD_EXTRACTOR = - builderInfo -> builderInfo.getBuilderCreationMethod().toString(); + builderInfo -> { + ExecutableElement builderCreationMethod = builderInfo.getBuilderCreationMethod(); + + StringBuilder sb = new StringBuilder( builderCreationMethod.getSimpleName() ); + + sb.append( '(' ); + for ( VariableElement parameter : builderCreationMethod.getParameters() ) { + sb.append( parameter ); + } + + sb.append( ')' ); + return sb.toString(); + }; private final Elements elementUtils; private final Types typeUtils; diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java index 424b815718..3819545090 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java @@ -38,9 +38,9 @@ public class ReferencedAccessibilityTest { @Diagnostic(type = SourceTargetMapperPrivate.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 22, - messageRegExp = "Unmapped target property: \"bar\"\\. Mapping from property \"org\\.mapstruct\\.ap\\" + - ".test\\.accessibility\\.referenced\\.ReferencedSource referencedSource\" to \"org\\.mapstruct\\" + - ".ap\\.test\\.accessibility\\.referenced\\.ReferencedTarget referencedTarget\"") + message = "Unmapped target property: \"bar\". Mapping from property \"org.mapstruct.ap" + + ".test.accessibility.referenced.ReferencedSource referencedSource\" to \"org.mapstruct" + + ".ap.test.accessibility.referenced.ReferencedTarget referencedTarget\".") } ) public void shouldNotBeAbleToAccessPrivateMethodInReferenced() { @@ -66,9 +66,9 @@ public void shouldBeAbleToAccessProtectedMethodInReferencedInSamePackage() { } @Diagnostic(type = SourceTargetMapperDefaultOther.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 24, - messageRegExp = "Unmapped target property: \"bar\"\\. Mapping from property \"org\\.mapstruct\\.ap\\" + - ".test\\.accessibility\\.referenced\\.ReferencedSource referencedSource\" to \"org\\.mapstruct\\" + - ".ap\\.test\\.accessibility\\.referenced\\.ReferencedTarget referencedTarget\"") + message = "Unmapped target property: \"bar\". Mapping from property \"org.mapstruct.ap" + + ".test.accessibility.referenced.ReferencedSource referencedSource\" to \"org.mapstruct" + + ".ap.test.accessibility.referenced.ReferencedTarget referencedTarget\".") } ) public void shouldNotBeAbleToAccessDefaultMethodInReferencedInOtherPackage() { @@ -89,9 +89,9 @@ public void shouldBeAbleToAccessProtectedMethodInBase() { } @Diagnostic(type = AbstractSourceTargetMapperPrivate.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 23, - messageRegExp = "Unmapped target property: \"bar\"\\. Mapping from property \"org\\.mapstruct\\.ap\\" + - ".test\\.accessibility\\.referenced\\.ReferencedSource referencedSource\" to \"org\\.mapstruct\\" + - ".ap\\.test\\.accessibility\\.referenced\\.ReferencedTarget referencedTarget\"") + message = "Unmapped target property: \"bar\". Mapping from property \"org.mapstruct.ap" + + ".test.accessibility.referenced.ReferencedSource referencedSource\" to \"org.mapstruct" + + ".ap.test.accessibility.referenced.ReferencedTarget referencedTarget\".") } ) public void shouldNotBeAbleToAccessPrivateMethodInBase() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java index 7a58d56fb2..7f97d2db51 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java @@ -35,7 +35,8 @@ public class Issue1005Test { @Diagnostic(type = Issue1005ErroneousAbstractResultTypeMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - messageRegExp = "The result type .*\\.AbstractEntity may not be an abstract class nor interface.") + message = "The result type org.mapstruct.ap.test.bugs._1005.AbstractEntity may not be an " + + "abstract class nor interface.") }) public void shouldFailDueToAbstractResultType() { } @@ -47,8 +48,9 @@ public void shouldFailDueToAbstractResultType() { @Diagnostic(type = Issue1005ErroneousAbstractReturnTypeMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "The return type .*\\.AbstractEntity is an abstract class or interface. Provide a non" + - " abstract / non interface result type or a factory method.") + message = + "The return type org.mapstruct.ap.test.bugs._1005.AbstractEntity is an abstract class or " + + "interface. Provide a non abstract / non interface result type or a factory method.") }) public void shouldFailDueToAbstractReturnType() { } @@ -60,7 +62,8 @@ public void shouldFailDueToAbstractReturnType() { @Diagnostic(type = Issue1005ErroneousInterfaceResultTypeMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - messageRegExp = "The result type .*\\.HasPrimaryKey may not be an abstract class nor interface.") + message = "The result type org.mapstruct.ap.test.bugs._1005.HasPrimaryKey may not be an " + + "abstract class nor interface.") }) public void shouldFailDueToInterfaceResultType() { } @@ -72,8 +75,9 @@ public void shouldFailDueToInterfaceResultType() { @Diagnostic(type = Issue1005ErroneousInterfaceReturnTypeMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "The return type .*\\.HasKey is an abstract class or interface. Provide a non " + - "abstract / non interface result type or a factory method.") + message = + "The return type org.mapstruct.ap.test.bugs._1005.HasKey is an abstract class or interface. " + + "Provide a non abstract / non interface result type or a factory method.") }) public void shouldFailDueToInterfaceReturnType() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java index 86c55597d5..afebcbef43 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java @@ -29,12 +29,12 @@ public class Issue1029Test { @Test @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(kind = Kind.WARNING, line = 26, type = ErroneousIssue1029Mapper.class, - messageRegExp = "Unmapped target properties: \"knownProp, lastUpdated, computedMapping\"\\."), + message = "Unmapped target properties: \"knownProp, lastUpdated, computedMapping\"."), @Diagnostic(kind = Kind.WARNING, line = 37, type = ErroneousIssue1029Mapper.class, - messageRegExp = "Unmapped target property: \"lastUpdated\"\\."), + message = "Unmapped target property: \"lastUpdated\"."), @Diagnostic(kind = Kind.ERROR, line = 42, type = ErroneousIssue1029Mapper.class, - messageRegExp = "Unknown property \"unknownProp\" in result type " + - "org.mapstruct.ap.test.bugs._1029.ErroneousIssue1029Mapper.Deck\\. Did you mean \"knownProp\"?") + message = "Unknown property \"unknownProp\" in result type " + + "org.mapstruct.ap.test.bugs._1029.ErroneousIssue1029Mapper.Deck. Did you mean \"knownProp\"?") }) public void reportsProperWarningsAndError() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java index 3ed3e20db1..f4142fe641 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java @@ -27,19 +27,19 @@ public class Issue1153Test { @Diagnostic(type = ErroneousIssue1153Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - messageRegExp = "Property \"readOnly\" has no write accessor in " + - "org.mapstruct.ap.test.bugs._1153.ErroneousIssue1153Mapper.Target\\."), + message = "Property \"readOnly\" has no write accessor in " + + "org.mapstruct.ap.test.bugs._1153.ErroneousIssue1153Mapper.Target."), @Diagnostic(type = ErroneousIssue1153Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - messageRegExp = "Property \"nestedTarget.readOnly\" has no write accessor in " + - "org.mapstruct.ap.test.bugs._1153.ErroneousIssue1153Mapper.Target\\."), + message = "Property \"nestedTarget.readOnly\" has no write accessor in " + + "org.mapstruct.ap.test.bugs._1153.ErroneousIssue1153Mapper.Target."), @Diagnostic(type = ErroneousIssue1153Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 23, - messageRegExp = "Unknown property \"nestedTarget2.writable2\" in result type " + - "org.mapstruct.ap.test.bugs._1153.ErroneousIssue1153Mapper.Target\\. " + - "Did you mean \"nestedTarget2\\.writable\"") + message = "Unknown property \"nestedTarget2.writable2\" in result type " + + "org.mapstruct.ap.test.bugs._1153.ErroneousIssue1153Mapper.Target. " + + "Did you mean \"nestedTarget2.writable\"?") }) @Test public void shouldReportErrorsCorrectly() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java index 94a7e3bbae..6cf0ba401f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java @@ -58,16 +58,19 @@ public void factoryMethodWithSourceParamIsChosen() { @Diagnostic(type = ErroneousIssue1242MapperMultipleSources.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - messageRegExp = "Ambiguous factory methods found for creating .*TargetB:" - + " .*TargetB anotherTargetBCreator\\(.*SourceB source\\)," - + " .*TargetB .*TargetFactories\\.createTargetB\\(.*SourceB source," - + " @TargetType .*Class<.*TargetB> clazz\\)," - + " .*TargetB .*TargetFactories\\.createTargetB\\(@TargetType java.lang.Class<.*TargetB> clazz\\)," - + " .*TargetB .*TargetFactories\\.createTargetB\\(\\)."), + message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.bugs._1242.TargetB: org" + + ".mapstruct.ap.test.bugs._1242.TargetB anotherTargetBCreator(org.mapstruct.ap.test.bugs._1242" + + ".SourceB source), org.mapstruct.ap.test.bugs._1242.TargetB org.mapstruct.ap.test.bugs._1242" + + ".TargetFactories.createTargetB(org.mapstruct.ap.test.bugs._1242.SourceB source, @TargetType java" + + ".lang.Class clazz), org.mapstruct.ap.test.bugs._1242" + + ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB(@TargetType java.lang" + + ".Class clazz), org.mapstruct.ap.test.bugs._1242" + + ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB()."), @Diagnostic(type = ErroneousIssue1242MapperMultipleSources.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - messageRegExp = ".*TargetB does not have an accessible parameterless constructor\\.") + message = "org.mapstruct.ap.test.bugs._1242.TargetB does not have an accessible parameterless " + + "constructor.") }) public void ambiguousMethodErrorForTwoFactoryMethodsWithSourceParam() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java index 9e33e2f079..4d875017f8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java @@ -33,7 +33,8 @@ public class Issue1283Test { @Diagnostic(type = ErroneousInverseTargetHasNoSuitableConstructorMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22L, - messageRegExp = ".*\\._1283\\.Source does not have an accessible parameterless constructor" + message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible parameterless " + + "constructor." ) } ) @@ -48,7 +49,8 @@ public void inheritInverseConfigurationReturnTypeHasNoSuitableConstructor() { @Diagnostic(type = ErroneousTargetHasNoSuitableConstructorMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 18L, - messageRegExp = ".*\\._1283\\.Source does not have an accessible parameterless constructor" + message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible parameterless " + + "constructor." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1353/Issue1353Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1353/Issue1353Test.java index da89288013..953ecf8d17 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1353/Issue1353Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1353/Issue1353Test.java @@ -35,13 +35,13 @@ public class Issue1353Test { @Diagnostic (type = Issue1353Mapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 22, - messageRegExp = "The property named \" source.string1\" has whitespaces," + message = "The property named \" source.string1\" has whitespaces," + " using trimmed property \"source.string1\" instead." ), @Diagnostic (type = Issue1353Mapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 22, - messageRegExp = "The property named \"string2 \" has whitespaces," + message = "The property named \"string2 \" has whitespaces," + " using trimmed property \"string2\" instead." ) } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1457/Issue1457Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1457/Issue1457Test.java index 6d30ce345d..910a1e7280 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1457/Issue1457Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1457/Issue1457Test.java @@ -46,11 +46,12 @@ public void setup() { @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, diagnostics = @Diagnostic( - messageRegExp = - "Lifecycle method has multiple matching parameters \\(e\\. g\\. same type\\), in this case " + - "please ensure to name the parameters in the lifecycle and mapping method identical\\. This " + - "lifecycle method will not be used for the mapping method '.*\\.TargetBook mapBook\\(" + - ".*\\.SourceBook sourceBook, .*\\.String authorFirstName, .*\\.String authorLastName\\)'\\.", + message = + "Lifecycle method has multiple matching parameters (e. g. same type), in this case please ensure to " + + "name the parameters in the lifecycle and mapping method identical. This lifecycle method will " + + "not be used for the mapping method 'org.mapstruct.ap.test.bugs._1457.TargetBook mapBook(org" + + ".mapstruct.ap.test.bugs._1457.SourceBook sourceBook, java.lang.String authorFirstName, java.lang" + + ".String authorLastName)'.", kind = javax.tools.Diagnostic.Kind.WARNING, line = 43 ) @@ -108,16 +109,16 @@ private void assertTargetBookMatchesSourceBook(TargetBook targetBook) { @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, diagnostics = @Diagnostic( - messageRegExp = - "Lifecycle method has multiple matching parameters \\(e\\. g\\. same type\\), in this case " + - "please ensure to name the parameters in the lifecycle and mapping method identical\\. This " + - "lifecycle method will not be used for the mapping method '.*\\.TargetBook mapBook\\(" + - ".*\\.SourceBook sourceBook, .*\\.String authorFirstName, .*\\.String authorLastName\\)'\\.", + message = + "Lifecycle method has multiple matching parameters (e. g. same type), in this case please ensure to " + + "name the parameters in the lifecycle and mapping method identical. This lifecycle method will " + + "not be used for the mapping method 'org.mapstruct.ap.test.bugs._1457.TargetBook mapBook(org" + + ".mapstruct.ap.test.bugs._1457.SourceBook sourceBook, java.lang.String authorFirstName, java.lang" + + ".String authorLastName)'.", kind = javax.tools.Diagnostic.Kind.WARNING, line = 22 ) ) public void testMapperWithoutMatchingParameterNames() { - } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java index 77f480346a..47ab19f1dd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java @@ -24,7 +24,9 @@ public class Issue1698Test { diagnostics = { @Diagnostic(type = Erroneous1698Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - messageRegExp = "Can't map property.*") + message = "Can't map property \"java.lang.String rabbit\" to \"org.mapstruct.ap.test.bugs._1698" + + ".Erroneous1698Mapper.Rabbit rabbit\". Consider to declare/implement a mapping method: \"org" + + ".mapstruct.ap.test.bugs._1698.Erroneous1698Mapper.Rabbit map(java.lang.String value)\".") }) public void testErrorMessage() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1904/Issue1904Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1904/Issue1904Test.java index 7c2b95bbfb..b6e0b03cf7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1904/Issue1904Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1904/Issue1904Test.java @@ -36,13 +36,12 @@ public class Issue1904Test { type = Issue1904Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 14, - messageRegExp = ".*No implementation was created for Issue1904Mapper due to having a problem in the " + - "erroneous element .*\\.CarManualDto. Hint: this often means that some other annotation processor " + - "was supposed to process the erroneous element. You can also enable MapStruct verbose mode by " + - "setting -Amapstruct.verbose=true as a compilation argument." + message = "No implementation was created for Issue1904Mapper due to having a problem in the erroneous " + + "element org.mapstruct.ap.test.bugs._1904.Issue1904Mapper.CarManualDto. Hint: this often means that " + + "some other annotation processor was supposed to process the erroneous element. You can also enable " + + "MapStruct verbose mode by setting -Amapstruct.verbose=true as a compilation argument." ) }) public void shouldHaveCompilationErrorIfMapperCouldNotBeCreated() { - } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java index 28f06b2acb..e716d70a34 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java @@ -24,9 +24,13 @@ public class Issue590Test { @Test @ExpectedCompilationOutcome(value = CompilationResult.FAILED, - diagnostics = { @Diagnostic(type = ErroneousSourceTargetMapper.class, - kind = Kind.ERROR, - messageRegExp = "Can't map property \"java\\.lang\\.String prop\" to \"[^ ]+ prop\"") }) + diagnostics = { + @Diagnostic(type = ErroneousSourceTargetMapper.class, + kind = Kind.ERROR, + message = "Can't map property \"java.lang.String prop\" to \"java.util.logging.XMLFormatter " + + "prop\". Consider to declare/implement a mapping method: \"java.util.logging.XMLFormatter map" + + "(java.lang.String value)\".") + }) public void showsCantMapPropertyError() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_631/Issue631Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_631/Issue631Test.java index 5050c76ea0..5d582d1c1f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_631/Issue631Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_631/Issue631Test.java @@ -30,11 +30,11 @@ public class Issue631Test { @Diagnostic(type = ErroneousSourceTargetMapper.class, kind = Kind.ERROR, line = 22, - messageRegExp = "Can't generate mapping method for a generic type variable target."), + message = "Can't generate mapping method for a generic type variable target."), @Diagnostic(type = ErroneousSourceTargetMapper.class, kind = Kind.ERROR, line = 24, - messageRegExp = "Can't generate mapping method for a generic type variable source.") + message = "Can't generate mapping method for a generic type variable source.") } ) @WithClasses({ErroneousSourceTargetMapper.class, Base1.class, Base2.class}) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java index 6ff17597c3..2a80961bc5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java @@ -41,7 +41,7 @@ public class Issue880Test { value = CompilationResult.SUCCEEDED, diagnostics = @Diagnostic(kind = Kind.WARNING, type = UsesConfigFromAnnotationMapper.class, line = 16, - messageRegExp = "Unmapped target property: \"core\"\\.")) + message = "Unmapped target property: \"core\".")) public void compilationSucceedsAndAppliesCorrectComponentModel() { generatedSource.forMapper( UsesConfigFromAnnotationMapper.class ).containsNoImportFor( Component.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/multiple/MultipleBuilderMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/multiple/MultipleBuilderMapperTest.java index ac3e0fc6f6..21b9566c4f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/multiple/MultipleBuilderMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/multiple/MultipleBuilderMapperTest.java @@ -40,23 +40,17 @@ public class MultipleBuilderMapperTest { type = ErroneousMoreThanOneBuildMethodMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - messageRegExp = "No build method \"build\" found in \".*\\.multiple\\.build\\.Process\\.Builder\" " + - "for \".*\\.multiple\\.build\\.Process\"\\. " + - "Found methods: " + - "\".*wrongCreate\\(\\) ?, " + - ".*create\\(\\) ?\"\\. " + - "Consider to add @Builder in order to select the correct build method." + message = "No build method \"build\" found in \"org.mapstruct.ap.test.builder.multiple.build.Process" + + ".Builder\" for \"org.mapstruct.ap.test.builder.multiple.build.Process\". Found methods: " + + "\"wrongCreate(), create()\". Consider to add @Builder in order to select the correct build method." ), @Diagnostic( type = ErroneousMoreThanOneBuildMethodMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 21, - messageRegExp = "No build method \"missingBuild\" found " + - "in \".*\\.multiple\\.build\\.Process\\.Builder\" " + - "for \".*\\.multiple\\.build\\.Process\"\\. " + - "Found methods: " + - "\".*wrongCreate\\(\\) ?, " + - ".*create\\(\\) ?\"\\." + message = "No build method \"missingBuild\" found in \"org.mapstruct.ap.test.builder.multiple.build" + + ".Process.Builder\" for \"org.mapstruct.ap.test.builder.multiple.build.Process\". Found methods: " + + "\"wrongCreate(), create()\"." ) }) @Test @@ -72,12 +66,10 @@ public void moreThanOneBuildMethod() { type = ErroneousMoreThanOneBuildMethodWithMapperDefinedMappingMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 18, - messageRegExp = - "No build method \"mapperBuild\" found in \".*\\.multiple\\.build\\.Process\\.Builder\" " + - "for \".*\\.multiple\\.build\\.Process\"\\. " + - "Found methods: " + - "\".*wrongCreate\\(\\) ?, " + - ".*create\\(\\) ?\"\\." + message = + "No build method \"mapperBuild\" found in \"org.mapstruct.ap.test.builder.multiple.build.Process" + + ".Builder\" for \"org.mapstruct.ap.test.builder.multiple.build.Process\". Found methods: " + + "\"wrongCreate(), create()\"." ) }) @Test @@ -118,11 +110,9 @@ public void builderMappingMapperConfigDefined() { type = Case.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 11, - messageRegExp = "More than one builder creation method for \".*\\.multiple\\.builder.Case\"\\. " + - "Found methods: " + - "\".*wrongBuilder\\(\\) ?, " + - ".*builder\\(\\) ?\"\\. " + - "Builder will not be used\\. Consider implementing a custom BuilderProvider SPI\\." + message = "More than one builder creation method for \"org.mapstruct.ap.test.builder.multiple.builder" + + ".Case\". Found methods: \"wrongBuilder(), builder()\". Builder will not be used. Consider " + + "implementing a custom BuilderProvider SPI." ) }) @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java index a7e579b290..3bc4e91ed8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java @@ -57,7 +57,7 @@ public void testSimpleImmutableBuilderHappyPath() { kind = javax.tools.Diagnostic.Kind.ERROR, type = ErroneousSimpleBuilderMapper.class, line = 21, - messageRegExp = "Unmapped target property: \"name\"\\.")) + message = "Unmapped target property: \"name\".")) public void testSimpleImmutableBuilderMissingPropertyFailsToCompile() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java index feefb92b40..0aaf28d318 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java @@ -35,11 +35,11 @@ public class ErroneousCollectionMappingTest { @Diagnostic(type = ErroneousCollectionToNonCollectionMapper.class, kind = Kind.ERROR, line = 15, - messageRegExp = "Can't generate mapping method from iterable type to non-iterable type"), + message = "Can't generate mapping method from iterable type to non-iterable type."), @Diagnostic(type = ErroneousCollectionToNonCollectionMapper.class, kind = Kind.ERROR, line = 17, - messageRegExp = "Can't generate mapping method from non-iterable type to iterable type") + message = "Can't generate mapping method from non-iterable type to iterable type.") } ) public void shouldFailToGenerateImplementationBetweenCollectionAndNonCollection() { @@ -54,9 +54,9 @@ public void shouldFailToGenerateImplementationBetweenCollectionAndNonCollection( @Diagnostic(type = ErroneousCollectionToPrimitivePropertyMapper.class, kind = Kind.ERROR, line = 13, - messageRegExp = "Can't map property \"java.util.List strings\" to \"int strings\". " - + "Consider to declare/implement a mapping method: \"int map\\(java.util.List" - + " value\\)\"") + message = "Can't map property \"java.util.List strings\" to \"int strings\". " + + "Consider to declare/implement a mapping method: \"int map(java.util.List" + + " value)\".") } ) public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { @@ -71,7 +71,7 @@ public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { @Diagnostic(type = EmptyItererableMappingMapper.class, kind = Kind.ERROR, line = 22, - messageRegExp = "'nullValueMappingStrategy','dateformat', 'qualifiedBy' and 'elementTargetType' are " + message = "'nullValueMappingStrategy','dateformat', 'qualifiedBy' and 'elementTargetType' are " + "undefined in @IterableMapping, define at least one of them.") } ) @@ -87,7 +87,7 @@ public void shouldFailOnEmptyIterableAnnotation() { @Diagnostic(type = EmptyMapMappingMapper.class, kind = Kind.ERROR, line = 22, - messageRegExp = "'nullValueMappingStrategy', 'keyDateFormat', 'keyQualifiedBy', 'keyTargetType', " + message = "'nullValueMappingStrategy', 'keyDateFormat', 'keyQualifiedBy', 'keyTargetType', " + "'valueDateFormat', 'valueQualfiedBy' and 'valueTargetType' are all undefined in @MapMapping, " + "define at least one of them.") } @@ -104,9 +104,10 @@ public void shouldFailOnEmptyMapAnnotation() { @Diagnostic(type = ErroneousCollectionNoElementMappingFound.class, kind = Kind.ERROR, line = 25, - messageRegExp = "No target bean properties found: can't map Collection element \".*WithProperties " - + "withProperties\" to \".*NoProperties noProperties\". Consider to declare/implement " - + "a mapping method: \".*NoProperties map\\(.*WithProperties value\\)") + message = "No target bean properties found: can't map Collection element \"org.mapstruct.ap.test" + + ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + + ".mapstruct.ap.test.WithProperties value)\".") } ) public void shouldFailOnNoElementMappingFound() { @@ -121,9 +122,9 @@ public void shouldFailOnNoElementMappingFound() { @Diagnostic(type = ErroneousCollectionNoElementMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 19, - messageRegExp = - "Can't map collection element \".*AttributedString\" to \".*String \". " + - "Consider to declare/implement a mapping method: \".*String map\\(.*AttributedString value\\)") + message = "Can't map collection element \"java.text.AttributedString\" to \"java.lang.String \". " + + "Consider to declare/implement a mapping method: \"java.lang.String map(java.text" + + ".AttributedString value)\".") } ) public void shouldFailOnNoElementMappingFoundWithDisabledAuto() { @@ -138,10 +139,10 @@ public void shouldFailOnNoElementMappingFoundWithDisabledAuto() { @Diagnostic(type = ErroneousCollectionNoKeyMappingFound.class, kind = Kind.ERROR, line = 25, - messageRegExp = "No target bean properties found: can't map Map key \".*WithProperties " - + "withProperties\" to " - + "\".*NoProperties noProperties\". Consider to declare/implement a mapping method: " - + "\".*NoProperties map\\(.*WithProperties value\\)" ) + message = "No target bean properties found: can't map Map key \"org.mapstruct.ap.test.WithProperties " + + "withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". Consider to " + + "declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org.mapstruct.ap" + + ".test.WithProperties value)\".") } ) public void shouldFailOnNoKeyMappingFound() { @@ -156,8 +157,8 @@ public void shouldFailOnNoKeyMappingFound() { @Diagnostic(type = ErroneousCollectionNoKeyMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 19, - messageRegExp = "Can't map map key \".*AttributedString\" to \".*String \". " + - "Consider to declare/implement a mapping method: \".*String map\\(.*AttributedString value\\)") + message = "Can't map map key \"java.text.AttributedString\" to \"java.lang.String \". Consider to " + + "declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)\".") } ) public void shouldFailOnNoKeyMappingFoundWithDisabledAuto() { @@ -172,10 +173,10 @@ public void shouldFailOnNoKeyMappingFoundWithDisabledAuto() { @Diagnostic(type = ErroneousCollectionNoValueMappingFound.class, kind = Kind.ERROR, line = 25, - messageRegExp = "No target bean properties found: can't map Map value \".*WithProperties " - + "withProperties\" to " - + "\".*NoProperties noProperties\". Consider to declare/implement a mapping method: " - + "\".*NoProperties map\\(.*WithProperties value\\)" ) + message = "No target bean properties found: can't map Map value \"org.mapstruct.ap.test" + + ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + + ".mapstruct.ap.test.WithProperties value)\".") } ) public void shouldFailOnNoValueMappingFound() { @@ -190,8 +191,8 @@ public void shouldFailOnNoValueMappingFound() { @Diagnostic(type = ErroneousCollectionNoValueMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 19, - messageRegExp = "Can't map map value \".*AttributedString\" to \".*String \". " + - "Consider to declare/implement a mapping method: \".*String map(.*AttributedString value)") + message = "Can't map map value \"java.text.AttributedString\" to \"java.lang.String \". Consider to " + + "declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)\".") } ) public void shouldFailOnNoValueMappingFoundWithDisabledAuto() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java index c538318fd7..50c2ffcb96 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java @@ -75,7 +75,8 @@ public void shouldForgeNewMapMappingMethod() { } @Test - @WithClasses({ ErroneousCollectionNonMappableSetMapper.class, + @WithClasses({ + ErroneousCollectionNonMappableSetMapper.class, ErroneousNonMappableSetSource.class, ErroneousNonMappableSetTarget.class, Foo.class, @@ -87,8 +88,10 @@ public void shouldForgeNewMapMappingMethod() { @Diagnostic(type = ErroneousCollectionNonMappableSetMapper.class, kind = Kind.ERROR, line = 17, - messageRegExp = "No target bean properties found: can't map Collection element \".* nonMappableSet\" " - + "to \".* nonMappableSet\". Consider to declare/implement a mapping method: .*." ), + message = "No target bean properties found: can't map Collection element \"org.mapstruct.ap" + + ".test.collection.forged.Foo nonMappableSet\" to \"org.mapstruct.ap.test.collection.forged.Bar " + + "nonMappableSet\". Consider to declare/implement a mapping method: \"org.mapstruct.ap.test" + + ".collection.forged.Bar map(org.mapstruct.ap.test.collection.forged.Foo value)\"."), } ) public void shouldGenerateNonMappleMethodForSetMapping() { @@ -107,15 +110,17 @@ public void shouldGenerateNonMappleMethodForSetMapping() { @Diagnostic(type = ErroneousCollectionNonMappableMapMapper.class, kind = Kind.ERROR, line = 17, - messageRegExp = "No target bean properties found: can't map Map key \".* nonMappableMap\\{:key\\}\" " - + "to \".* " - + "nonMappableMap\\{:key\\}\". Consider to declare/implement a mapping method: .*." ), + message = "No target bean properties found: can't map Map key \"org.mapstruct.ap.test" + + ".collection.forged.Foo nonMappableMap{:key}\" to \"org.mapstruct.ap.test.collection.forged.Bar " + + "nonMappableMap{:key}\". Consider to declare/implement a mapping method: \"org.mapstruct.ap" + + ".test.collection.forged.Bar map(org.mapstruct.ap.test.collection.forged.Foo value)\"."), @Diagnostic(type = ErroneousCollectionNonMappableMapMapper.class, kind = Kind.ERROR, line = 17, - messageRegExp = "No target bean properties found: can't map Map value \".* " - + "nonMappableMap\\{:value\\}\" to \".* nonMappableMap\\{:value\\}\". " - + "Consider to declare/implement a mapping method: .*." ), + message = "No target bean properties found: can't map Map value \"org.mapstruct.ap.test" + + ".collection.forged.Foo nonMappableMap{:value}\" to \"org.mapstruct.ap.test.collection.forged.Bar" + + " nonMappableMap{:value}\". Consider to declare/implement a mapping method: \"org.mapstruct.ap" + + ".test.collection.forged.Bar map(org.mapstruct.ap.test.collection.forged.Foo value)\"."), } ) public void shouldGenerateNonMappleMethodForMapMapping() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java index 4c4932e2c4..85dff16393 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java @@ -53,7 +53,7 @@ public void shouldHandleImmutableTarget() { @Diagnostic(type = ErroneousCupboardMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "No write accessor found for property \"content\" in target type.") + message = "No write accessor found for property \"content\" in target type.") } ) public void testShouldFailOnPropertyMappingNoPropertySetterOnlyGetter() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java index 4f5c161a20..aa09cdbc4f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java @@ -87,7 +87,7 @@ public void shouldGenerateSuperBoundTargetForgedIterableMethod() { @Diagnostic( type = ErroneousIterableSuperBoundSourceMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - messageRegExp = "Can't generate mapping method for a wildcard super bound source." ) + message = "Can't generate mapping method for a wildcard super bound source." ) } ) public void shouldFailOnSuperBoundSource() { @@ -101,7 +101,7 @@ public void shouldFailOnSuperBoundSource() { @Diagnostic( type = ErroneousIterableExtendsBoundTargetMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - messageRegExp = "Can't generate mapping method for a wildcard extends bound result." ) + message = "Can't generate mapping method for a wildcard extends bound result." ) } ) public void shouldFailOnExtendsBoundTarget() { @@ -115,7 +115,7 @@ public void shouldFailOnExtendsBoundTarget() { @Diagnostic(type = ErroneousIterableTypeVarBoundMapperOnMethod.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - messageRegExp = "Can't generate mapping method for a generic type variable target." ) + message = "Can't generate mapping method for a generic type variable target." ) } ) public void shouldFailOnTypeVarSource() { @@ -129,7 +129,7 @@ public void shouldFailOnTypeVarSource() { @Diagnostic( type = ErroneousIterableTypeVarBoundMapperOnMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - messageRegExp = "Can't generate mapping method for a generic type variable source." ) + message = "Can't generate mapping method for a generic type variable source." ) } ) public void shouldFailOnTypeVarTarget() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java index db72521e9a..2569973372 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java @@ -41,7 +41,7 @@ public class ContextParameterErroneousTest { kind = Kind.ERROR, line = 20, type = ErroneousNodeMapperWithNonUniqueContextTypes.class, - messageRegExp = "The types of @Context parameters must be unique")) + message = "The types of @Context parameters must be unique.")) public void reportsNonUniqueContextParamType() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java index 669b4cfdad..8f630df22c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java @@ -33,51 +33,51 @@ public class InvalidDateFormatTest { @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 25, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern character 'q'\""), + message = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern character 'q'\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 26, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Unknown pattern letter: r\"\\."), + message = "Given date format \"qwertz\" is invalid. Message: \"Unknown pattern letter: r\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 27, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Unknown pattern letter: r\"\\."), + message = "Given date format \"qwertz\" is invalid. Message: \"Unknown pattern letter: r\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 28, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Unknown pattern letter: r\"\\."), + message = "Given date format \"qwertz\" is invalid. Message: \"Unknown pattern letter: r\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 29, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Unknown pattern letter: r\"\\."), + message = "Given date format \"qwertz\" is invalid. Message: \"Unknown pattern letter: r\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 30, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern component: q\""), + message = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern component: q\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 31, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern component: q\""), + message = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern component: q\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 32, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern component: q\""), + message = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern component: q\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 33, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern component: q\""), + message = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern component: q\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 37, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern character 'q'\""), + message = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern character 'q'\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 40, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern character 'q'\""), + message = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern character 'q'\"."), @Diagnostic(type = ErroneousFormatMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 43, - messageRegExp = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern character 'q'\"") + message = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern character 'q'\".") }) @Test public void shouldFailWithInvalidDateFormats() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java index a9bd35c2b5..ae61da8e19 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java @@ -58,7 +58,7 @@ public void testNoErrorCase() { @Diagnostic(type = ErroneousKitchenDrawerMapper1.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - messageRegExp = "Can't map property \"long numberOfForks\". It has a possibly lossy conversion from " + message = "Can't map property \"long numberOfForks\". It has a possibly lossy conversion from " + "long to int.") }) public void testConversionFromLongToInt() { @@ -71,7 +71,7 @@ public void testConversionFromLongToInt() { @Diagnostic(type = KitchenDrawerMapper2.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 24, - messageRegExp = "property \"java.math.BigInteger numberOfKnifes\" has a possibly lossy conversion " + message = "property \"java.math.BigInteger numberOfKnifes\" has a possibly lossy conversion " + "from java.math.BigInteger to java.lang.Integer.") }) public void testConversionFromBigIntegerToInteger() { @@ -84,8 +84,8 @@ public void testConversionFromBigIntegerToInteger() { @Diagnostic(type = ErroneousKitchenDrawerMapper3.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - messageRegExp = "org.mapstruct.ap.test.conversion.lossy.VerySpecialNumber numberOfSpoons\". It has " - + "a possibly lossy conversion from java.math.BigInteger to java.lang.Long") + message = "Can't map property \"org.mapstruct.ap.test.conversion.lossy.VerySpecialNumber " + + "numberOfSpoons\". It has a possibly lossy conversion from java.math.BigInteger to java.lang.Long.") }) public void test2StepConversionFromBigIntegerToLong() { } @@ -97,7 +97,7 @@ public void test2StepConversionFromBigIntegerToLong() { @Diagnostic(type = ErroneousKitchenDrawerMapper4.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - messageRegExp = "Can't map property \"java.lang.Double depth\". It has a possibly lossy conversion " + message = "Can't map property \"java.lang.Double depth\". It has a possibly lossy conversion " + "from java.lang.Double to float.") }) public void testConversionFromDoubleToFloat() { @@ -110,7 +110,7 @@ public void testConversionFromDoubleToFloat() { @Diagnostic(type = ErroneousKitchenDrawerMapper5.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - messageRegExp = "\"java.math.BigDecimal length\". It has a possibly lossy conversion from " + message = "Can't map property \"java.math.BigDecimal length\". It has a possibly lossy conversion from " + "java.math.BigDecimal to java.lang.Float.") }) public void testConversionFromBigDecimalToFloat() { @@ -123,7 +123,7 @@ public void testConversionFromBigDecimalToFloat() { @Diagnostic(type = KitchenDrawerMapper6.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 24, - messageRegExp = "property \"double height\" has a possibly lossy conversion from double to float.") + message = "property \"double height\" has a possibly lossy conversion from double to float.") }) public void test2StepConversionFromDoubleToFloat() { } @@ -135,8 +135,8 @@ public void test2StepConversionFromDoubleToFloat() { @Diagnostic(type = ListMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 21, - messageRegExp = "collection element has a possibly lossy conversion from java.math.BigDecimal to " - + "java.math.BigInteger") + message = "collection element has a possibly lossy conversion from java.math.BigDecimal to " + + "java.math.BigInteger.") }) public void testListElementConversion() { } @@ -148,11 +148,11 @@ public void testListElementConversion() { @Diagnostic(type = MapMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 19, - messageRegExp = "map key has a possibly lossy conversion from java.lang.Long to java.lang.Integer."), + message = "map key has a possibly lossy conversion from java.lang.Long to java.lang.Integer."), @Diagnostic(type = MapMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 19, - messageRegExp = "map value has a possibly lossy conversion from java.lang.Double to java.lang.Float.") + message = "map value has a possibly lossy conversion from java.lang.Double to java.lang.Float.") }) public void testMapElementConversion() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java index 84a5657930..59d59eb987 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java @@ -182,7 +182,7 @@ public void shouldApplyCustomMappers() { @Diagnostic(type = ErroneousPersonMapper.class, kind = Kind.ERROR, line = 14, - messageRegExp = "Specified decorator type is no subtype of the annotated mapper type") + message = "Specified decorator type is no subtype of the annotated mapper type.") } ) public void shouldRaiseErrorInCaseWrongDecoratorTypeIsGiven() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java index e554e4a4b2..f2054b90b9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.defaultvalue; -import static org.assertj.core.api.Assertions.assertThat; - import org.junit.Test; import org.junit.runner.RunWith; import org.mapstruct.ap.test.defaultvalue.other.Continent; @@ -17,19 +15,21 @@ import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -@IssueKey( "600" ) -@RunWith( AnnotationProcessorTestRunner.class ) -@WithClasses( { - CountryEntity.class, - CountryDts.class, - Continent.class -} ) +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("600") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + CountryEntity.class, + CountryDts.class, + Continent.class +}) public class DefaultValueTest { @Test - @WithClasses( { - Region.class, - CountryMapper.class - } ) + @WithClasses({ + Region.class, + CountryMapper.class + }) /** * Checks: *
      @@ -56,10 +56,10 @@ public void shouldDefaultValueAndUseConstantExpression() { } @Test - @WithClasses( { - Region.class, - CountryMapper.class - } ) + @WithClasses({ + Region.class, + CountryMapper.class + }) public void shouldIgnoreDefaultValue() { CountryEntity countryEntity = new CountryEntity(); countryEntity.setCode( "US" ); @@ -77,10 +77,10 @@ public void shouldIgnoreDefaultValue() { } @Test - @WithClasses( { - Region.class, - CountryMapper.class - } ) + @WithClasses({ + Region.class, + CountryMapper.class + }) public void shouldHandleUpdateMethodsFromDtsToEntity() { CountryEntity countryEntity = new CountryEntity(); CountryDts countryDts = new CountryDts(); @@ -95,10 +95,10 @@ public void shouldHandleUpdateMethodsFromDtsToEntity() { } @Test - @WithClasses( { - Region.class, - CountryMapper.class - } ) + @WithClasses({ + Region.class, + CountryMapper.class + }) public void shouldHandleUpdateMethodsFromEntityToEntity() { CountryEntity source = new CountryEntity(); CountryEntity target = new CountryEntity(); @@ -114,45 +114,49 @@ public void shouldHandleUpdateMethodsFromEntityToEntity() { } @Test - @WithClasses( { - ErroneousMapper.class, - Region.class, - } ) + @WithClasses({ + ErroneousMapper.class, + Region.class, + }) @ExpectedCompilationOutcome( - value = CompilationResult.FAILED, - diagnostics = { - @Diagnostic( type = ErroneousMapper.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 18, - messageRegExp = "Constant and default value are both defined in @Mapping," - + " either define a defaultValue or a constant." ), - @Diagnostic(type = ErroneousMapper.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 20, - messageRegExp = "Can't map property \".*Region region\" to \".*String region\"\\. Consider") - } + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 18, + message = "Constant and default value are both defined in @Mapping, either define a defaultValue or a" + + " constant."), + @Diagnostic(type = ErroneousMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = "Can't map property \"org.mapstruct.ap.test.defaultvalue.Region region\" to \"java.lang" + + ".String region\". Consider to declare/implement a mapping method: \"java.lang.String map(org" + + ".mapstruct.ap.test.defaultvalue.Region value)\".") + } ) public void errorOnDefaultValueAndConstant() { } @Test - @WithClasses( { - ErroneousMapper2.class, - Region.class, - } ) + @WithClasses({ + ErroneousMapper2.class, + Region.class, + }) @ExpectedCompilationOutcome( - value = CompilationResult.FAILED, - diagnostics = { - @Diagnostic( type = ErroneousMapper2.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 18, - messageRegExp = "Expression and default value are both defined in @Mapping," - + " either define a defaultValue or an expression." ), - @Diagnostic(type = ErroneousMapper2.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 20, - messageRegExp = "Can't map property \".*Region region\" to \".*String region\"\\. Consider") - } + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousMapper2.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 18, + message = "Expression and default value are both defined in @Mapping, either define a defaultValue or" + + " an expression."), + @Diagnostic(type = ErroneousMapper2.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = "Can't map property \"org.mapstruct.ap.test.defaultvalue.Region region\" to \"java.lang" + + ".String region\". Consider to declare/implement a mapping method: \"java.lang.String map(org" + + ".mapstruct.ap.test.defaultvalue.Region value)\".") + } ) public void errorOnDefaultValueAndExpression() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java b/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java index ede43320dc..0a2ed604cf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java @@ -63,8 +63,8 @@ public void shouldApplySeveralDependenciesConfiguredForOneProperty() { @Diagnostic(type = ErroneousAddressMapperWithCyclicDependency.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - messageRegExp = "Cycle\\(s\\) between properties given via dependsOn\\(\\): lastName -> middleName -> " - + "firstName -> lastName" + message = "Cycle(s) between properties given via dependsOn(): lastName -> middleName -> " + + "firstName -> lastName." ) } ) @@ -80,7 +80,7 @@ public void shouldReportErrorIfDependenciesContainCycle() { @Diagnostic(type = ErroneousAddressMapperWithUnknownPropertyInDependsOn.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - messageRegExp = "\"doesnotexist\" is no property of the method return type" + message = "\"doesnotexist\" is no property of the method return type." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java index 64191955cb..561b53effd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java @@ -30,18 +30,18 @@ public class AmbiguousAnnotatedFactoryTest { @Diagnostic(type = SourceTargetMapperAndBarFactory.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "Ambiguous factory methods found for creating " - + "org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod.Bar: " - + "org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod.Bar " - + "createBar\\(org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod.Foo foo\\), " - + "org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod.Bar " - + ".*AmbiguousBarFactory.createBar\\(org.mapstruct.ap.test.erroneous." - + "ambiguousannotatedfactorymethod.Foo foo\\)."), + message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.erroneous" + + ".ambiguousannotatedfactorymethod.Bar: org.mapstruct.ap.test.erroneous" + + ".ambiguousannotatedfactorymethod.Bar createBar(org.mapstruct.ap.test.erroneous" + + ".ambiguousannotatedfactorymethod.Foo foo), org.mapstruct.ap.test.erroneous" + + ".ambiguousannotatedfactorymethod.Bar org.mapstruct.ap.test.erroneous" + + ".ambiguousannotatedfactorymethod.AmbiguousBarFactory.createBar(org.mapstruct.ap.test.erroneous" + + ".ambiguousannotatedfactorymethod.Foo foo)."), @Diagnostic(type = SourceTargetMapperAndBarFactory.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = ".*\\.ambiguousannotatedfactorymethod.Bar does not have an accessible parameterless " + - "constructor\\.") + message = "org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod.Bar does not have an " + + "accessible parameterless constructor.") } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java index e54602b01a..82886eb038 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java @@ -34,15 +34,15 @@ public class FactoryTest { @Diagnostic(type = SourceTargetMapperAndBarFactory.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "Ambiguous factory methods found for creating " - + "org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar: " - + "org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar createBar\\(\\), " - + "org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar .*BarFactory.createBar\\(\\)."), + message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.erroneous" + + ".ambiguousfactorymethod.Bar: org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar " + + "createBar(), org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar org.mapstruct.ap.test" + + ".erroneous.ambiguousfactorymethod.a.BarFactory.createBar()."), @Diagnostic(type = SourceTargetMapperAndBarFactory.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = ".*\\.ambiguousfactorymethod\\.Bar does not have an accessible parameterless " - + "constructor\\.") + message = "org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar does not have an accessible " + + "parameterless constructor.") } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java index 5e30722152..f95614132c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java @@ -34,26 +34,26 @@ public class ErroneousMappingsTest { @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 20, - messageRegExp = "Target property \"foo\" must not be mapped more than once"), + message = "Target property \"foo\" must not be mapped more than once."), @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 16, - messageRegExp = "No property named \"bar\" exists in source parameter\\(s\\)\\. " + + message = "No property named \"bar\" exists in source parameter(s). " + "Did you mean \"foo\"?"), @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 18, - messageRegExp = "Unknown property \"bar\" in result type " + + message = "Unknown property \"bar\" in result type " + "org.mapstruct.ap.test.erroneous.attributereference.Target. Did you mean \"foo\"?"), @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 23, - messageRegExp = "No property named \"source1.foo\" exists in source parameter\\(s\\)\\. " + + message = "No property named \"source1.foo\" exists in source parameter(s). " + "Did you mean \"foo\"?"), @Diagnostic(type = ErroneousMapper.class, kind = Kind.WARNING, line = 26, - messageRegExp = "Unmapped target property: \"bar\"") + message = "Unmapped target property: \"bar\".") } ) public void shouldFailToGenerateMappings() { @@ -67,7 +67,7 @@ public void shouldFailToGenerateMappings() { @Diagnostic(type = ErroneousMapper1.class, kind = Kind.ERROR, line = 16, - messageRegExp = "The type of parameter \"source\" has no property named \"foobar\"") + message = "The type of parameter \"source\" has no property named \"foobar\".") } ) public void shouldFailToGenerateMappingsErrorOnMandatoryParameterName() { @@ -81,7 +81,7 @@ public void shouldFailToGenerateMappingsErrorOnMandatoryParameterName() { @Diagnostic(type = ErroneousMapper2.class, kind = Kind.ERROR, line = 19, - messageRegExp = "Target property \"foo\" must not be mapped more than once" ) + message = "Target property \"foo\" must not be mapped more than once." ) } ) public void shouldFailToGenerateMappingsErrorOnDuplicateTarget() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java index ed2e9e2108..db97dc133a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java @@ -15,52 +15,68 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @IssueKey("1504") -@WithClasses( { Source.class, Target.class, UnmappableClass.class } ) +@WithClasses({ Source.class, Target.class, UnmappableClass.class }) @RunWith(AnnotationProcessorTestRunner.class) public class ErroneousPropertyMappingTest { @Test - @WithClasses( ErroneousMapper1.class ) + @WithClasses(ErroneousMapper1.class) @IssueKey("1504") @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @Diagnostic(kind = javax.tools.Diagnostic.Kind.ERROR, - line = 16, - messageRegExp = ".*Consider to declare/implement a mapping method.*") } + line = 16, + message = "Can't map property \"org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass " + + "source\" to \"java.lang.String property\". Consider to declare/implement a mapping method: " + + "\"java.lang.String map(org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass value)\".") + } ) - public void testUnmappableSourceProperty() { } + public void testUnmappableSourceProperty() { + } @Test - @WithClasses( ErroneousMapper2.class ) + @WithClasses(ErroneousMapper2.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @Diagnostic(kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = ".*Consider to declare/implement a mapping method.*") } + message = "Can't map property \"org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass " + + "nameBasedSource\" to \"java.lang.String nameBasedSource\". Consider to declare/implement a " + + "mapping method: \"java.lang.String map(org.mapstruct.ap.test.erroneous.propertymapping" + + ".UnmappableClass value)\".") + } ) - public void testUnmappableSourcePropertyWithNoSourceDefinedInMapping() { } + public void testUnmappableSourcePropertyWithNoSourceDefinedInMapping() { + } @Test - @WithClasses( ErroneousMapper3.class ) + @WithClasses(ErroneousMapper3.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @Diagnostic(kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "Can't map.*constant.*" ) } + message = "Can't map \"java.lang.String \"constant\"\" to \"org.mapstruct.ap.test.erroneous" + + ".propertymapping.UnmappableClass constant\".") + } ) - public void testUnmappableConstantAssignment() { } + public void testUnmappableConstantAssignment() { + } @Test - @WithClasses( ErroneousMapper4.class ) + @WithClasses(ErroneousMapper4.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @Diagnostic(kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = ".*Consider to declare/implement a mapping method.*") } + message = "Can't map property \"org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass " + + "source\" to \"java.lang.String property\". Consider to declare/implement a mapping method: " + + "\"java.lang.String map(org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass value)\".") + } ) - public void testUnmappableParameterAssignment() { } + public void testUnmappableParameterAssignment() { + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java index 4462bdffa9..2a1b2bae28 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java @@ -33,26 +33,26 @@ public class ErroneousMappingsTest { @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 14, - messageRegExp = "Can't map property \"boolean foo\" to \"int foo\". Consider to declare/implement a " - + "mapping method: \"int map\\(boolean value\\)\"."), + message = "Can't map property \"boolean foo\" to \"int foo\". Consider to declare/implement a " + + "mapping method: \"int map(boolean value)\"."), @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 16, - messageRegExp = "Can't map property \"int foo\" to \"boolean foo\". Consider to declare/implement a " - + "mapping method: \"boolean map\\(int value\\)\"."), + message = "Can't map property \"int foo\" to \"boolean foo\". Consider to declare/implement a " + + "mapping method: \"boolean map(int value)\"."), @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 18, - messageRegExp = "Can't generate mapping method with primitive return type\\."), + message = "Can't generate mapping method with primitive return type."), @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 20, - messageRegExp = "Can't generate mapping method with primitive parameter type\\."), + message = "Can't generate mapping method with primitive parameter type."), @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 22, - messageRegExp = - "Can't generate mapping method that has a parameter annotated with @TargetType\\.") + message = + "Can't generate mapping method that has a parameter annotated with @TargetType.") } ) public void shouldFailToGenerateMappings() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java index 3b2496327d..e1549ef4a8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java @@ -82,8 +82,8 @@ public void propertyIsIgnoredInReverseMappingWhenSourceIsAlsoSpecifiedICWIgnore( @Diagnostic(type = ErroneousTargetHasNoWriteAccessorMapper.class, kind = Kind.ERROR, line = 22, - messageRegExp = "Property \"hasClaws\" has no write accessor in " + - "org.mapstruct.ap.test.ignore.PreditorDto\\.") + message = "Property \"hasClaws\" has no write accessor in " + + "org.mapstruct.ap.test.ignore.PreditorDto.") } ) public void shouldGiveErrorOnMappingForReadOnlyProp() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java index 839d0987f7..e290aed450 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java @@ -44,7 +44,8 @@ public void shouldMapAttributeFromSuperType() { type = ErroneousTargetSourceMapper.class, kind = Kind.ERROR, line = 16, - messageRegExp = "Can't map property \"java.lang.CharSequence foo\" to \"java.lang.String foo\"" + message = "Can't map property \"java.lang.CharSequence foo\" to \"java.lang.String foo\". Consider to " + + "declare/implement a mapping method: \"java.lang.String map(java.lang.CharSequence value)\"." )) public void shouldReportErrorDueToUnmappableAttribute() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java index a8e1033a2b..dc37b5f79c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java @@ -69,11 +69,13 @@ public void shouldMapAttributesWithSuperTypeUsingOtherMapper() { kind = Kind.ERROR, type = ErroneousSourceCompositeTargetCompositeMapper.class, line = 19, - messageRegExp = - "Ambiguous mapping methods found for mapping property " - + "\"org.mapstruct.ap.test.inheritance.complex.SourceExt prop1\" to .*Reference: " - + ".*Reference .*AdditionalMappingHelper\\.asReference\\(.*SourceBase source\\), " - + ".*Reference .*AdditionalMappingHelper\\.asReference\\(.*AdditionalFooSource source\\)")) + message = "Ambiguous mapping methods found for mapping property \"org.mapstruct.ap.test.inheritance" + + ".complex.SourceExt prop1\" to org.mapstruct.ap.test.inheritance.complex.Reference: org.mapstruct.ap" + + ".test.inheritance.complex.Reference org.mapstruct.ap.test.inheritance.complex" + + ".AdditionalMappingHelper.asReference(org.mapstruct.ap.test.inheritance.complex.SourceBase source), " + + "org.mapstruct.ap.test.inheritance.complex.Reference org.mapstruct.ap.test.inheritance.complex" + + ".AdditionalMappingHelper.asReference(org.mapstruct.ap.test.inheritance.complex.AdditionalFooSource " + + "source).")) public void ambiguousMappingMethodsReportError() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java index 29c01c0a6d..8ab3583aa9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java @@ -203,29 +203,28 @@ public void autoInheritedFromMultipleSources() { @Diagnostic(type = Erroneous1Mapper.class, kind = Kind.ERROR, line = 23, - messageRegExp = "More than one configuration prototype method is applicable. Use @InheritConfiguration" - + " to select one of them explicitly:" - + " .*BaseVehicleEntity baseDtoToEntity\\(.*BaseVehicleDto dto\\)," - + " .*BaseVehicleEntity anythingToEntity\\(java.lang.Object anyting\\)\\."), + message = "More than one configuration prototype method is applicable. Use @InheritConfiguration to " + + "select one of them explicitly: org.mapstruct.ap.test.inheritfromconfig.BaseVehicleEntity " + + "baseDtoToEntity(org.mapstruct.ap.test.inheritfromconfig.BaseVehicleDto dto), org.mapstruct.ap" + + ".test.inheritfromconfig.BaseVehicleEntity anythingToEntity(java.lang.Object anyting)."), @Diagnostic(type = Erroneous1Mapper.class, kind = Kind.WARNING, line = 23, - messageRegExp = "Unmapped target properties: \"primaryKey, auditTrail\"\\."), + message = "Unmapped target properties: \"primaryKey, auditTrail\"."), @Diagnostic(type = Erroneous1Mapper.class, kind = Kind.ERROR, line = 29, - messageRegExp = "More than one configuration prototype method is applicable. Use @InheritConfiguration" - + " to select one of them explicitly:" - + " .*BaseVehicleEntity baseDtoToEntity\\(.*BaseVehicleDto dto\\)," - + " .*BaseVehicleEntity anythingToEntity\\(java.lang.Object anyting\\)\\."), + message = "More than one configuration prototype method is applicable. Use @InheritConfiguration to " + + "select one of them explicitly: org.mapstruct.ap.test.inheritfromconfig.BaseVehicleEntity " + + "baseDtoToEntity(org.mapstruct.ap.test.inheritfromconfig.BaseVehicleDto dto), org.mapstruct.ap" + + ".test.inheritfromconfig.BaseVehicleEntity anythingToEntity(java.lang.Object anyting)."), @Diagnostic(type = Erroneous1Mapper.class, kind = Kind.WARNING, line = 29, - messageRegExp = "Unmapped target property: \"primaryKey\"\\.") + message = "Unmapped target property: \"primaryKey\".") } ) public void erroneous1MultiplePrototypeMethodsMatch() { - } @Test @@ -236,15 +235,16 @@ public void erroneous1MultiplePrototypeMethodsMatch() { @Diagnostic(type = Erroneous2Mapper.class, kind = Kind.ERROR, line = 25, - messageRegExp = "Cycle detected while evaluating inherited configurations. Inheritance path:" - + " .*CarEntity toCarEntity1\\(.*CarDto carDto\\)" - + " -> .*CarEntity toCarEntity2\\(.*CarDto carDto\\)" - + " -> void toCarEntity3\\(.*CarDto carDto, @MappingTarget .*CarEntity entity\\)" - + " -> .*CarEntity toCarEntity1\\(.*CarDto carDto\\)") + message = "Cycle detected while evaluating inherited configurations. Inheritance path: org.mapstruct" + + ".ap.test.inheritfromconfig.CarEntity toCarEntity1(org.mapstruct.ap.test.inheritfromconfig.CarDto" + + " carDto) -> org.mapstruct.ap.test.inheritfromconfig.CarEntity toCarEntity2(org.mapstruct.ap.test" + + ".inheritfromconfig.CarDto carDto) -> void toCarEntity3(org.mapstruct.ap.test.inheritfromconfig" + + ".CarDto carDto, @MappingTarget org.mapstruct.ap.test.inheritfromconfig.CarEntity entity) -> org" + + ".mapstruct.ap.test.inheritfromconfig.CarEntity toCarEntity1(org.mapstruct.ap.test" + + ".inheritfromconfig.CarDto carDto)") } ) public void erroneous2InheritanceCycle() { - } @Test @@ -256,7 +256,7 @@ public void erroneous2InheritanceCycle() { @Diagnostic(type = ErroneousMapperAutoInheritance.class, kind = Kind.ERROR, line = 22, - messageRegExp = "Unmapped target properties: \"primaryKey, auditTrail\"\\.") + message = "Unmapped target properties: \"primaryKey, auditTrail\".") } ) public void erroneousWrongReverseConfigInherited() { } @@ -270,28 +270,32 @@ public void erroneousWrongReverseConfigInherited() { } @Diagnostic(type = ErroneousMapperReverseWithAutoInheritance.class, kind = Kind.ERROR, line = 23, - messageRegExp = "Unmapped target property: \"id\"\\.") + message = "Unmapped target property: \"id\".") } ) public void erroneousWrongConfigInherited() { } @Test - @IssueKey( "1255" ) - @WithClasses({ Erroneous3Mapper.class, Erroneous3Config.class } ) + @IssueKey("1255") + @WithClasses({ Erroneous3Mapper.class, Erroneous3Config.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = Erroneous3Mapper.class, kind = Kind.ERROR, line = 22, - messageRegExp = "More than one configuration prototype method is applicable. " - + "Use @InheritInverseConfiguration.*"), + message = "More than one configuration prototype method is applicable. Use " + + "@InheritInverseConfiguration to select one of them explicitly: org.mapstruct.ap.test" + + ".inheritfromconfig.BaseVehicleEntity baseDtoToEntity(org.mapstruct.ap.test.inheritfromconfig" + + ".BaseVehicleDto dto), org.mapstruct.ap.test.inheritfromconfig.BaseVehicleEntity baseDtoToEntity2" + + "(org.mapstruct.ap.test.inheritfromconfig.BaseVehicleDto dto)."), @Diagnostic(type = Erroneous3Mapper.class, kind = Kind.ERROR, line = 22, - messageRegExp = "Unmapped target property: \"id\"\\.") + message = "Unmapped target property: \"id\".") } ) - public void erroneousDuplicateReverse() { } + public void erroneousDuplicateReverse() { + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java index dfa9849f0f..4cc6ce3e66 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java @@ -42,11 +42,11 @@ public class ErroneousStreamMappingTest { @Diagnostic(type = ErroneousStreamToNonStreamMapper.class, kind = Kind.ERROR, line = 15, - messageRegExp = "Can't generate mapping method from iterable type to non-iterable type"), + message = "Can't generate mapping method from iterable type to non-iterable type."), @Diagnostic(type = ErroneousStreamToNonStreamMapper.class, kind = Kind.ERROR, line = 17, - messageRegExp = "Can't generate mapping method from non-iterable type to iterable type") + message = "Can't generate mapping method from non-iterable type to iterable type.") } ) public void shouldFailToGenerateImplementationBetweenStreamAndNonStreamOrIterable() { @@ -60,12 +60,12 @@ public void shouldFailToGenerateImplementationBetweenStreamAndNonStreamOrIterabl @Diagnostic(type = ErroneousStreamToPrimitivePropertyMapper.class, kind = Kind.ERROR, line = 13, - messageRegExp = + message = "Can't map property \"java.util.stream.Stream strings\" to \"int strings\". " + - "Consider to declare/implement a mapping method: \"int map\\(java.util.stream.Stream" - + " value\\)\"") + + " value)\".") } ) public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { @@ -79,17 +79,17 @@ public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { @Diagnostic(type = EmptyStreamMappingMapper.class, kind = Kind.ERROR, line = 23, - messageRegExp = "'nullValueMappingStrategy','dateformat', 'qualifiedBy' and 'elementTargetType' are " + message = "'nullValueMappingStrategy','dateformat', 'qualifiedBy' and 'elementTargetType' are " + "undefined in @IterableMapping, define at least one of them."), @Diagnostic(type = EmptyStreamMappingMapper.class, kind = Kind.ERROR, line = 26, - messageRegExp = "'nullValueMappingStrategy','dateformat', 'qualifiedBy' and 'elementTargetType' are " + message = "'nullValueMappingStrategy','dateformat', 'qualifiedBy' and 'elementTargetType' are " + "undefined in @IterableMapping, define at least one of them."), @Diagnostic(type = EmptyStreamMappingMapper.class, kind = Kind.ERROR, line = 29, - messageRegExp = "'nullValueMappingStrategy','dateformat', 'qualifiedBy' and 'elementTargetType' are " + message = "'nullValueMappingStrategy','dateformat', 'qualifiedBy' and 'elementTargetType' are " + "undefined in @IterableMapping, define at least one of them.") } ) @@ -104,10 +104,10 @@ public void shouldFailOnEmptyIterableAnnotationStreamMappings() { @Diagnostic(type = ErroneousStreamToStreamNoElementMappingFound.class, kind = Kind.ERROR, line = 24, - messageRegExp = "No target bean properties found: can't map Stream element \".*WithProperties " - + "withProperties\" to \".*NoProperties noProperties\". " - + "Consider to declare/implement a mapping method: \".*NoProperties " - + "map\\(.*WithProperties value\\)" ) + message = "No target bean properties found: can't map Stream element \"org.mapstruct.ap.test" + + ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + + ".mapstruct.ap.test.WithProperties value)\".") } ) public void shouldFailOnNoElementMappingFoundForStreamToStream() { @@ -122,8 +122,9 @@ public void shouldFailOnNoElementMappingFoundForStreamToStream() { @Diagnostic(type = ErroneousStreamToStreamNoElementMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 19, - messageRegExp = "Can't map stream element \".*AttributedString\" to \".*String \". Consider to " + - "declare/implement a mapping method: \".*String map(.*AttributedString value)") + message = "Can't map stream element \"java.text.AttributedString\" to \"java.lang.String \". Consider" + + " to declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)" + + "\".") } ) public void shouldFailOnNoElementMappingFoundForStreamToStreamWithDisabledAuto() { @@ -137,11 +138,10 @@ public void shouldFailOnNoElementMappingFoundForStreamToStreamWithDisabledAuto() @Diagnostic(type = ErroneousListToStreamNoElementMappingFound.class, kind = Kind.ERROR, line = 25, - messageRegExp = "No target bean properties found: can't map Stream element \".*WithProperties " - + "withProperties\" to \".*NoProperties noProperties\"." - + " Consider to declare/implement a mapping method: \".*NoProperties map\\(" - + ".*WithProperties " - + "value\\)" ) + message = "No target bean properties found: can't map Stream element \"org.mapstruct.ap.test" + + ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + + ".mapstruct.ap.test.WithProperties value)\".") } ) public void shouldFailOnNoElementMappingFoundForListToStream() { @@ -156,9 +156,9 @@ public void shouldFailOnNoElementMappingFoundForListToStream() { @Diagnostic(type = ErroneousListToStreamNoElementMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 20, - messageRegExp = "Can't map stream element \".*AttributedString\" to " - + "\".*String \". Consider to declare/implement a mapping method: \".*String " - + "map\\(.*AttributedString value\\)" ) + message = "Can't map stream element \"java.text.AttributedString\" to \"java.lang.String \". Consider" + + " to declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)" + + "\".") } ) public void shouldFailOnNoElementMappingFoundForListToStreamWithDisabledAuto() { @@ -172,10 +172,10 @@ public void shouldFailOnNoElementMappingFoundForListToStreamWithDisabledAuto() { @Diagnostic(type = ErroneousStreamToListNoElementMappingFound.class, kind = Kind.ERROR, line = 25, - messageRegExp = "No target bean properties found: can't map Stream element \".*WithProperties " - + "withProperties\" to .*NoProperties noProperties\"." - + " Consider to declare/implement a mapping method: \".*NoProperties map(" - + ".*WithProperties value)" ) + message = "No target bean properties found: can't map Stream element \"org.mapstruct.ap.test" + + ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + + ".mapstruct.ap.test.WithProperties value)\".") } ) public void shouldFailOnNoElementMappingFoundForStreamToList() { @@ -190,8 +190,9 @@ public void shouldFailOnNoElementMappingFoundForStreamToList() { @Diagnostic(type = ErroneousStreamToListNoElementMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 20, - messageRegExp = "Can't map stream element \".*AttributedString\" to .*String \". Consider to " + - "declare/implement a mapping method: \".*String map(.*AttributedString value)") + message = "Can't map stream element \"java.text.AttributedString\" to \"java.lang.String \". Consider" + + " to declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)" + + "\".") } ) public void shouldFailOnNoElementMappingFoundForStreamToListWithDisabledAuto() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java index 67091f5798..a996974cb7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java @@ -70,8 +70,11 @@ public void shouldForgeNewIterableMappingMethod() { @Diagnostic(type = ErroneousStreamNonMappableStreamMapper.class, kind = Kind.ERROR, line = 17, - messageRegExp = "No target bean properties found: can't map Stream element \".* nonMappableStream\" " - + "to \".* nonMappableStream\". Consider to declare/implement a mapping method: .*." ) } + message = "No target bean properties found: can't map Stream element \"org.mapstruct.ap.test" + + ".java8stream.forged.Foo nonMappableStream\" to \"org.mapstruct.ap.test.java8stream.forged.Bar " + + "nonMappableStream\". Consider to declare/implement a mapping method: \"org.mapstruct.ap.test" + + ".java8stream.forged.Bar map(org.mapstruct.ap.test.java8stream.forged.Foo value)\".") + } ) public void shouldGenerateNonMappableMethodForSetMapping() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/WildCardTest.java index a24a9d8b5d..acf89c5b93 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/WildCardTest.java @@ -71,7 +71,7 @@ public void shouldGenerateSuperBoundTargetForgedIterableMethod() { @Diagnostic(type = ErroneousIterableSuperBoundSourceMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 21, - messageRegExp = "Can't generate mapping method for a wildcard super bound source.") + message = "Can't generate mapping method for a wildcard super bound source.") } ) public void shouldFailOnSuperBoundSource() { @@ -85,7 +85,7 @@ public void shouldFailOnSuperBoundSource() { @Diagnostic(type = ErroneousIterableExtendsBoundTargetMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 21, - messageRegExp = "Can't generate mapping method for a wildcard extends bound result.") + message = "Can't generate mapping method for a wildcard extends bound result.") } ) public void shouldFailOnExtendsBoundTarget() { @@ -99,7 +99,7 @@ public void shouldFailOnExtendsBoundTarget() { @Diagnostic(type = ErroneousIterableTypeVarBoundMapperOnMethod.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 21, - messageRegExp = "Can't generate mapping method for a generic type variable target.") + message = "Can't generate mapping method for a generic type variable target.") } ) public void shouldFailOnTypeVarSource() { @@ -113,7 +113,7 @@ public void shouldFailOnTypeVarSource() { @Diagnostic(type = ErroneousIterableTypeVarBoundMapperOnMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 21, - messageRegExp = "Can't generate mapping method for a generic type variable source.") + message = "Can't generate mapping method for a generic type variable source.") } ) public void shouldFailOnTypeVarTarget() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/mapperconfig/ConfigTest.java b/processor/src/test/java/org/mapstruct/ap/test/mapperconfig/ConfigTest.java index cd7e56a93e..d128e6f77e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mapperconfig/ConfigTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mapperconfig/ConfigTest.java @@ -58,7 +58,7 @@ public void shouldUseCustomMapperViaMapperConfigForFooToDto() { diagnostics = { @Diagnostic(type = SourceTargetMapperWarn.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 24, - messageRegExp = "Unmapped target property: \"noFoo\"") + message = "Unmapped target property: \"noFoo\".") }) public void shouldUseWARNViaMapper() { } @@ -69,7 +69,7 @@ public void shouldUseWARNViaMapper() { diagnostics = { @Diagnostic(type = SourceTargetMapperErroneous.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - messageRegExp = "Unmapped target property: \"noFoo\"") + message = "Unmapped target property: \"noFoo\".") }) public void shouldUseERRORViaMapperConfig() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java index 90f0957e68..a1a1fd6079 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java @@ -76,7 +76,10 @@ public void testDeepCloning() { @Diagnostic(type = ErroneousDirectMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \".*\\.ShelveDTO shelve\".*" + message = "Can't map property \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\" to \"org" + + ".mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\". Consider to declare/implement a mapping " + + "method: \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO map(org.mapstruct.ap.test" + + ".mappingcontrol.ShelveDTO value)\"." ) }) public void directSelectionNotAllowed() { @@ -101,7 +104,9 @@ public void methodSelectionAllowed() { @Diagnostic(type = ErroneousMethodMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \"int beerCount\".*" + message = "Can't map property \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\" to \"int " + + "beerCount\". Consider to declare/implement a mapping method: \"int map(org.mapstruct.ap.test" + + ".mappingcontrol.ShelveDTO value)\"." ) }) public void methodSelectionNotAllowed() { @@ -126,7 +131,8 @@ public void conversionSelectionAllowed() { @Diagnostic(type = ErroneousConversionMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "Can't map property \".*\\.String beerCount\" to \"int beerCount\".*" + message = "Can't map property \"java.lang.String beerCount\" to \"int beerCount\". Consider to " + + "declare/implement a mapping method: \"int map(java.lang.String value)\"." ) }) public void conversionSelectionNotAllowed() { @@ -151,7 +157,9 @@ public void complexSelectionAllowed() { @Diagnostic(type = ErroneousComplexMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \"int beerCount\".*" + message = "Can't map property \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\" to \"int " + + "beerCount\". Consider to declare/implement a mapping method: \"int map(org.mapstruct.ap.test" + + ".mappingcontrol.ShelveDTO value)\"." ) }) public void complexSelectionNotAllowed() { @@ -164,7 +172,9 @@ public void complexSelectionNotAllowed() { @Diagnostic(type = ErroneousComplexMapperWithConfig.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - messageRegExp = "Can't map property \".*\\.ShelveDTO shelve\" to \"int beerCount\".*" + message = "Can't map property \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\" to \"int " + + "beerCount\". Consider to declare/implement a mapping method: \"int map(org.mapstruct.ap.test" + + ".mappingcontrol.ShelveDTO value)\"." ) }) public void complexSelectionNotAllowedWithConfig() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java index dcf9451303..9908ea8e4a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java @@ -33,7 +33,7 @@ public class SuggestMostSimilarNameTest { @Diagnostic(type = PersonAgeMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - messageRegExp = ".*Did you mean \"age\"\\?") + message = "No property named \"agee\" exists in source parameter(s). Did you mean \"age\"?") } ) public void testAgeSuggestion() { @@ -49,7 +49,8 @@ public void testAgeSuggestion() { @Diagnostic(type = PersonNameMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - messageRegExp = ".*Did you mean \"fullName\"\\?") + message = "Unknown property \"fulName\" in result type org.mapstruct.ap.test.namesuggestion.Person. " + + "Did you mean \"fullName\"?") } ) public void testNameSuggestion() { @@ -65,11 +66,13 @@ public void testNameSuggestion() { @Diagnostic(type = PersonGarageWrongTargetMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - messageRegExp = "Unknown property \"garage\\.colour\\.rgb\".*Did you mean \"garage\\.color\"\\?"), + message = "Unknown property \"garage.colour.rgb\" in result type org.mapstruct.ap.test.namesuggestion" + + ".Person. Did you mean \"garage.color\"?"), @Diagnostic(type = PersonGarageWrongTargetMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "Unknown property \"garage\\.colour\".*Did you mean \"garage\\.color\"\\?") + message = "Unknown property \"garage.colour\" in result type org.mapstruct.ap.test.namesuggestion" + + ".Person. Did you mean \"garage.color\"?") } ) public void testGarageTargetSuggestion() { @@ -85,11 +88,13 @@ public void testGarageTargetSuggestion() { @Diagnostic(type = PersonGarageWrongSourceMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 21, - messageRegExp = "No property named \"garage\\.colour\\.rgb\".*Did you mean \"garage\\.color\"\\?"), + message = "No property named \"garage.colour.rgb\" exists in source parameter(s). Did you mean " + + "\"garage.color\"?"), @Diagnostic(type = PersonGarageWrongSourceMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 28, - messageRegExp = "No property named \"garage\\.colour\".*Did you mean \"garage\\.color\"\\?") + message = "No property named \"garage.colour\" exists in source parameter(s). Did you mean \"garage" + + ".color\"?") } ) public void testGarageSourceSuggestion() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java index b7dd9b70ac..434ad225a6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java @@ -33,8 +33,9 @@ public class DisablingNestedSimpleBeansMappingTest { @Diagnostic(type = ErroneousDisabledHouseMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "Can't map property \".*\\.Roof roof\" to \".*\\.RoofDto roof\"\\. Consider to " + - "declare/implement a mapping method: \".*\\.RoofDto map\\(.*\\.Roof value\\)\"\\." + message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.Roof roof\" to \"org.mapstruct.ap" + + ".test.nestedbeans.RoofDto roof\". Consider to declare/implement a mapping method: \"org" + + ".mapstruct.ap.test.nestedbeans.RoofDto map(org.mapstruct.ap.test.nestedbeans.Roof value)\"." ) }) @Test @@ -50,8 +51,9 @@ public void shouldUseDisabledMethodGenerationOnMapper() { @Diagnostic(type = ErroneousDisabledViaConfigHouseMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "Can't map property \".*\\.Roof roof\" to \".*\\.RoofDto roof\"\\. Consider to " + - "declare/implement a mapping method: \".*\\.RoofDto map\\(.*\\.Roof value\\)\"\\." + message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.Roof roof\" to \"org.mapstruct.ap" + + ".test.nestedbeans.RoofDto roof\". Consider to declare/implement a mapping method: \"org" + + ".mapstruct.ap.test.nestedbeans.RoofDto map(org.mapstruct.ap.test.nestedbeans.Roof value)\"." ) }) @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java index 1d684ec502..b1f58967ff 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java @@ -6,7 +6,6 @@ package org.mapstruct.ap.test.nestedbeans; import org.junit.Test; - import org.junit.runner.RunWith; import org.mapstruct.ap.test.nestedbeans.unmappable.BaseCollectionElementPropertyMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.BaseDeepListMapper; @@ -14,21 +13,38 @@ import org.mapstruct.ap.test.nestedbeans.unmappable.BaseDeepMapValueMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.BaseDeepNestingMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.BaseValuePropertyMapper; +import org.mapstruct.ap.test.nestedbeans.unmappable.Car; +import org.mapstruct.ap.test.nestedbeans.unmappable.CarDto; +import org.mapstruct.ap.test.nestedbeans.unmappable.Cat; +import org.mapstruct.ap.test.nestedbeans.unmappable.CatDto; +import org.mapstruct.ap.test.nestedbeans.unmappable.Color; +import org.mapstruct.ap.test.nestedbeans.unmappable.ColorDto; import org.mapstruct.ap.test.nestedbeans.unmappable.Computer; import org.mapstruct.ap.test.nestedbeans.unmappable.ComputerDto; import org.mapstruct.ap.test.nestedbeans.unmappable.Dictionary; import org.mapstruct.ap.test.nestedbeans.unmappable.DictionaryDto; +import org.mapstruct.ap.test.nestedbeans.unmappable.ExternalRoofType; import org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWord; import org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWordDto; -import org.mapstruct.ap.test.nestedbeans.unmappable.Cat; -import org.mapstruct.ap.test.nestedbeans.unmappable.CatDto; +import org.mapstruct.ap.test.nestedbeans.unmappable.House; +import org.mapstruct.ap.test.nestedbeans.unmappable.HouseDto; import org.mapstruct.ap.test.nestedbeans.unmappable.Info; import org.mapstruct.ap.test.nestedbeans.unmappable.InfoDto; +import org.mapstruct.ap.test.nestedbeans.unmappable.Roof; +import org.mapstruct.ap.test.nestedbeans.unmappable.RoofDto; +import org.mapstruct.ap.test.nestedbeans.unmappable.RoofType; import org.mapstruct.ap.test.nestedbeans.unmappable.RoofTypeMapper; +import org.mapstruct.ap.test.nestedbeans.unmappable.User; +import org.mapstruct.ap.test.nestedbeans.unmappable.UserDto; +import org.mapstruct.ap.test.nestedbeans.unmappable.Wheel; +import org.mapstruct.ap.test.nestedbeans.unmappable.WheelDto; +import org.mapstruct.ap.test.nestedbeans.unmappable.Word; +import org.mapstruct.ap.test.nestedbeans.unmappable.WordDto; import org.mapstruct.ap.test.nestedbeans.unmappable.erroneous.UnmappableCollectionElementPropertyMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.erroneous.UnmappableDeepListMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.erroneous.UnmappableDeepMapKeyMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.erroneous.UnmappableDeepMapValueMapper; +import org.mapstruct.ap.test.nestedbeans.unmappable.erroneous.UnmappableDeepNestingMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.erroneous.UnmappableEnumMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.erroneous.UnmappableValuePropertyMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.ignore.UnmappableIgnoreCollectionElementPropertyMapper; @@ -43,23 +59,6 @@ import org.mapstruct.ap.test.nestedbeans.unmappable.warn.UnmappableWarnDeepMapValueMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.warn.UnmappableWarnDeepNestingMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.warn.UnmappableWarnValuePropertyMapper; -import org.mapstruct.ap.test.nestedbeans.unmappable.UserDto; -import org.mapstruct.ap.test.nestedbeans.unmappable.User; -import org.mapstruct.ap.test.nestedbeans.unmappable.WheelDto; -import org.mapstruct.ap.test.nestedbeans.unmappable.Wheel; -import org.mapstruct.ap.test.nestedbeans.unmappable.Car; -import org.mapstruct.ap.test.nestedbeans.unmappable.CarDto; -import org.mapstruct.ap.test.nestedbeans.unmappable.ExternalRoofType; -import org.mapstruct.ap.test.nestedbeans.unmappable.House; -import org.mapstruct.ap.test.nestedbeans.unmappable.HouseDto; -import org.mapstruct.ap.test.nestedbeans.unmappable.Color; -import org.mapstruct.ap.test.nestedbeans.unmappable.ColorDto; -import org.mapstruct.ap.test.nestedbeans.unmappable.Roof; -import org.mapstruct.ap.test.nestedbeans.unmappable.RoofType; -import org.mapstruct.ap.test.nestedbeans.unmappable.RoofDto; -import org.mapstruct.ap.test.nestedbeans.unmappable.erroneous.UnmappableDeepNestingMapper; -import org.mapstruct.ap.test.nestedbeans.unmappable.Word; -import org.mapstruct.ap.test.nestedbeans.unmappable.WordDto; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; @@ -100,8 +99,9 @@ public class DottedErrorMessageTest { @Diagnostic(type = BaseDeepNestingMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, - messageRegExp = "Unmapped target property: \"rgb\"\\. Mapping from " + PROPERTY + - " \".*Color house\\.roof\\.color\" to \".*ColorDto house\\.roof\\.color\"\\.") + message = "Unmapped target property: \"rgb\". Mapping from " + PROPERTY + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Color house.roof.color\" to \"org.mapstruct.ap" + + ".test.nestedbeans.unmappable.ColorDto house.roof.color\".") } ) public void testDeepNestedBeans() { @@ -117,8 +117,9 @@ public void testDeepNestedBeans() { @Diagnostic(type = BaseDeepListMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, - messageRegExp = "Unmapped target property: \"left\"\\. Mapping from " + COLLECTION_ELEMENT + - " \".*Wheel car\\.wheels\" to \".*WheelDto car\\.wheels\"\\.") + message = "Unmapped target property: \"left\". Mapping from " + COLLECTION_ELEMENT + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Wheel car.wheels\" to \"org.mapstruct.ap.test" + + ".nestedbeans.unmappable.WheelDto car.wheels\".") } ) public void testIterables() { @@ -134,8 +135,9 @@ public void testIterables() { @Diagnostic(type = BaseDeepMapKeyMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, - messageRegExp = "Unmapped target property: \"pronunciation\"\\. Mapping from " + MAP_KEY + - " \".*Word dictionary\\.wordMap\\{:key\\}\" to \".*WordDto dictionary\\.wordMap\\{:key\\}\"\\.") + message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_KEY + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Word dictionary.wordMap{:key}\" to \"org" + + ".mapstruct.ap.test.nestedbeans.unmappable.WordDto dictionary.wordMap{:key}\".") } ) public void testMapKeys() { @@ -151,9 +153,9 @@ public void testMapKeys() { @Diagnostic(type = BaseDeepMapValueMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, - messageRegExp = "Unmapped target property: \"pronunciation\"\\. Mapping from " + MAP_VALUE + - " \".*ForeignWord dictionary\\.wordMap\\{:value\\}\" " + - "to \".*ForeignWordDto dictionary\\.wordMap\\{:value\\}\"\\.") + message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_VALUE + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWord dictionary.wordMap{:value}\" " + + "to \"org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWordDto dictionary.wordMap{:value}\".") } ) public void testMapValues() { @@ -169,8 +171,9 @@ public void testMapValues() { @Diagnostic(type = BaseCollectionElementPropertyMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, - messageRegExp = "Unmapped target property: \"color\"\\. Mapping from " + PROPERTY + - " \".*Info computers\\[\\].info\" to \".*InfoDto computers\\[\\].info\"\\.") + message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Info computers[].info\" to \"org.mapstruct.ap" + + ".test.nestedbeans.unmappable.InfoDto computers[].info\".") } ) public void testCollectionElementProperty() { @@ -186,8 +189,9 @@ public void testCollectionElementProperty() { @Diagnostic(type = BaseValuePropertyMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, - messageRegExp = "Unmapped target property: \"color\"\\. Mapping from " + PROPERTY + - " \".*Info catNameMap\\{:value\\}.info\" to \".*InfoDto catNameMap\\{:value\\}.info\"\\.") + message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Info catNameMap{:value}.info\" to \"org" + + ".mapstruct.ap.test.nestedbeans.unmappable.InfoDto catNameMap{:value}.info\".") } ) public void testMapValueProperty() { @@ -203,9 +207,12 @@ public void testMapValueProperty() { @Diagnostic(type = UnmappableEnumMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 25, - messageRegExp = "The following constants from the property \".*RoofType house\\.roof\\.type\" enum " + - "have no corresponding constant in the \".*ExternalRoofType house\\.roof\\.type\" enum and must " + - "be be mapped via adding additional mappings: NORMAL\\." + message = + "The following constants from the property \"org.mapstruct.ap.test.nestedbeans.unmappable" + + ".RoofType house.roof.type\" enum " + + "have no corresponding constant in the \"org.mapstruct.ap.test.nestedbeans.unmappable" + + ".ExternalRoofType house.roof.type\" enum and must " + + "be be mapped via adding additional mappings: NORMAL." ) } ) @@ -227,34 +234,39 @@ public void testMapEnumProperty() { @Diagnostic(type = BaseDeepNestingMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, - messageRegExp = "Unmapped target property: \"rgb\"\\. Mapping from " + PROPERTY + - " \".*Color house\\.roof\\.color\" to \".*ColorDto house\\.roof\\.color\"\\."), + message = "Unmapped target property: \"rgb\". Mapping from " + PROPERTY + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Color house.roof.color\" to \"org.mapstruct.ap" + + ".test.nestedbeans.unmappable.ColorDto house.roof.color\"."), @Diagnostic(type = BaseDeepListMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, - messageRegExp = "Unmapped target property: \"left\"\\. Mapping from " + COLLECTION_ELEMENT + - " \".*Wheel car\\.wheels\" to \".*WheelDto car\\.wheels\"\\."), + message = "Unmapped target property: \"left\". Mapping from " + COLLECTION_ELEMENT + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Wheel car.wheels\" to \"org.mapstruct.ap.test" + + ".nestedbeans.unmappable.WheelDto car.wheels\"."), @Diagnostic(type = BaseDeepMapKeyMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, - messageRegExp = "Unmapped target property: \"pronunciation\"\\. Mapping from " + MAP_KEY + - " \".*Word dictionary\\.wordMap\\{:key\\}\" to \".*WordDto dictionary\\.wordMap\\{:key\\}\"\\."), + message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_KEY + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Word dictionary.wordMap{:key}\" to \"org" + + ".mapstruct.ap.test.nestedbeans.unmappable.WordDto dictionary.wordMap{:key}\"."), @Diagnostic(type = BaseDeepMapValueMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, - messageRegExp = "Unmapped target property: \"pronunciation\"\\. Mapping from " + MAP_VALUE + - " \".*ForeignWord dictionary\\.wordMap\\{:value\\}\" " + - "to \".*ForeignWordDto dictionary\\.wordMap\\{:value\\}\"\\."), + message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_VALUE + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWord dictionary.wordMap{:value}\" " + + "to \"org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWordDto dictionary.wordMap{:value}\"."), @Diagnostic(type = BaseCollectionElementPropertyMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, - messageRegExp = "Unmapped target property: \"color\"\\. Mapping from " + PROPERTY + - " \".*Info computers\\[\\].info\" to \".*InfoDto computers\\[\\].info\"\\."), + message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Info computers[].info\" to \"org.mapstruct.ap" + + ".test.nestedbeans.unmappable.InfoDto computers[].info\"."), @Diagnostic(type = BaseValuePropertyMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, - messageRegExp = "Unmapped target property: \"color\"\\. Mapping from " + PROPERTY + - " \".*Info catNameMap\\{:value\\}.info\" to \".*InfoDto catNameMap\\{:value\\}.info\"\\.") + message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + + " \"org.mapstruct.ap.test.nestedbeans.unmappable.Info catNameMap{:value}.info\" to \"org" + + ".mapstruct.ap.test.nestedbeans.unmappable.InfoDto catNameMap{:value}.info\".") } ) public void testWarnUnmappedTargetProperties() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java index 6f50266f2f..267dfb438c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java @@ -31,26 +31,31 @@ public class ErroneousJavaInternalTest { @Diagnostic(type = ErroneousJavaInternalMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "Can't map property \".*MyType date\" to \"java\\.util\\.Date date\"\\. Consider to " + - "declare/implement a mapping method: \"java\\.util\\.Date map\\(.*MyType value\\)\"\\."), + message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.exclusions.Source.MyType date\" to " + + "\"java.util.Date date\". Consider to declare/implement a mapping method: \"java.util.Date map" + + "(org.mapstruct.ap.test.nestedbeans.exclusions.Source.MyType value)\"."), @Diagnostic(type = ErroneousJavaInternalMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "Can't map property \".*MyType calendar\" to \"java\\.util\\.GregorianCalendar " + - "calendar\"\\. Consider to declare/implement a mapping method: \"java\\.util\\.GregorianCalendar " + - "map\\(.*MyType value\\)\"\\."), + message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.exclusions.Source.MyType calendar\"" + + " to \"java.util.GregorianCalendar calendar\". Consider to declare/implement a mapping method: " + + "\"java.util.GregorianCalendar map(org.mapstruct.ap.test.nestedbeans.exclusions.Source.MyType " + + "value)\"."), @Diagnostic(type = ErroneousJavaInternalMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "Can't map property \".*List<.*MyType> types\" to \".*List<.*String> types\"\\" + - ". Consider to declare/implement a mapping method: \".*List<.*String> map\\(.*List<.*MyType> " + - "value\\)\"\\."), + message = "Can't map property \"java.util.List types\" to \"java.util.List types\". Consider to declare/implement a " + + "mapping method: \"java.util.List map(java.util.List value)\"."), @Diagnostic(type = ErroneousJavaInternalMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "Can't map property \".*List<.*MyType> nestedMyType\\.deepNestedType\\.types\" to \"" + - ".*List<.*String> nestedMyType\\.deepNestedType\\.types\"\\. Consider to declare/implement a " + - "mapping method: \".*List<.*String> map\\(.*List<.*MyType> value\\)\"\\.") + message = "Can't map property \"java.util.List nestedMyType.deepNestedType.types\" to \"java.util.List nestedMyType" + + ".deepNestedType.types\". Consider to declare/implement a mapping method: \"java.util.List map(java.util.List " + + "value)\".") }) @Test public void shouldNotNestIntoJavaPackageObjects() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java index b34a8d451c..1eccfdb4d7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java @@ -33,9 +33,11 @@ public class ErroneousCustomExclusionTest { @Diagnostic(type = ErroneousCustomExclusionMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - messageRegExp = "Can't map property \".*NestedSource nested\" to \".*NestedTarget nested\"\\. " + - "Consider to declare/implement a mapping method: \".*NestedTarget map\\(.*NestedSource value\\)" + - "\"\\.") + message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.exclusions.custom.Source" + + ".NestedSource nested\" to \"org.mapstruct.ap.test.nestedbeans.exclusions.custom.Target" + + ".NestedTarget nested\". Consider to declare/implement a mapping method: \"org.mapstruct.ap.test" + + ".nestedbeans.exclusions.custom.Target.NestedTarget map(org.mapstruct.ap.test.nestedbeans" + + ".exclusions.custom.Source.NestedSource value)\".") } ) @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java index 7a1187f05d..1d61b6e69a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java @@ -171,7 +171,7 @@ public void shouldUseGetAsTargetAccessor() { @Diagnostic( type = ArtistToChartEntryErroneous.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 34, - messageRegExp = "java.lang.Integer does not have an accessible parameterless constructor." ) + message = "java.lang.Integer does not have an accessible parameterless constructor." ) } ) @WithClasses({ ArtistToChartEntryErroneous.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java index a7f8e6bb9c..014c2c8220 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java @@ -111,7 +111,7 @@ public void testStrategyDefaultAppliedOnForgedMethod() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, alternativeLine = 22, // Javac wrong error reporting on repeatable annotations JDK-8042710 - messageRegExp = "Default value and nullValuePropertyMappingStrategy are both defined in @Mapping, " + + message = "Default value and nullValuePropertyMappingStrategy are both defined in @Mapping, " + "either define a defaultValue or an nullValuePropertyMappingStrategy.") } ) @@ -127,7 +127,7 @@ public void testBothDefaultValueAndNvpmsDefined() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, alternativeLine = 22, // Javac wrong error reporting on repeatable annotations JDK-8042710 - messageRegExp = "Expression and nullValuePropertyMappingStrategy are both defined in @Mapping, " + + message = "Expression and nullValuePropertyMappingStrategy are both defined in @Mapping, " + "either define an expression or an nullValuePropertyMappingStrategy.") } ) @@ -143,7 +143,7 @@ public void testBothExpressionAndNvpmsDefined() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, alternativeLine = 22, // Javac wrong error reporting on repeatable annotations JDK-8042710 - messageRegExp = "DefaultExpression and nullValuePropertyMappingStrategy are both defined in " + + message = "DefaultExpression and nullValuePropertyMappingStrategy are both defined in " + "@Mapping, either define a defaultExpression or an nullValuePropertyMappingStrategy.") } ) @@ -159,7 +159,7 @@ public void testBothDefaultExpressionAndNvpmsDefined() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, alternativeLine = 22, // Javac wrong error reporting on repeatable annotations JDK-8042710 - messageRegExp = "Constant and nullValuePropertyMappingStrategy are both defined in @Mapping, " + + message = "Constant and nullValuePropertyMappingStrategy are both defined in @Mapping, " + "either define a constant or an nullValuePropertyMappingStrategy.") } ) @@ -175,7 +175,7 @@ public void testBothConstantAndNvpmsDefined() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, alternativeLine = 22, // Javac wrong error reporting on repeatable annotations JDK-8042710 - messageRegExp = "Ignore and nullValuePropertyMappingStrategy are both defined in @Mapping, " + + message = "Ignore and nullValuePropertyMappingStrategy are both defined in @Mapping, " + "either define ignore or an nullValuePropertyMappingStrategy.") } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java b/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java index fa7b4015cb..2e1598f8aa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.reverse; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; import org.junit.Test; @@ -22,6 +20,8 @@ import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Sjaak Derksen */ @@ -61,12 +61,12 @@ public void shouldInheritInverseConfigurationMultipleCandidates() { @Diagnostic(type = SourceTargetMapperAmbiguous1.class, kind = Kind.ERROR, line = 38, - messageRegExp = "Several matching inverse methods exist: forward\\(\\), " - + "forwardNotToReverse\\(\\). Specify a name explicitly."), + message = "Several matching inverse methods exist: forward(), " + + "forwardNotToReverse(). Specify a name explicitly."), @Diagnostic(type = SourceTargetMapperAmbiguous1.class, kind = Kind.WARNING, line = 43, - messageRegExp = "Unmapped target properties: \"stringPropX, integerPropX\"") + message = "Unmapped target properties: \"stringPropX, integerPropX\".") } ) public void shouldRaiseAmbiguousReverseMethodError() { @@ -80,12 +80,12 @@ public void shouldRaiseAmbiguousReverseMethodError() { @Diagnostic(type = SourceTargetMapperAmbiguous2.class, kind = Kind.ERROR, line = 38, - messageRegExp = "None of the candidates forward\\(\\), forwardNotToReverse\\(\\) matches given " + message = "None of the candidates forward(), forwardNotToReverse() matches given " + "name: \"blah\"."), @Diagnostic(type = SourceTargetMapperAmbiguous2.class, kind = Kind.WARNING, line = 43, - messageRegExp = "Unmapped target properties: \"stringPropX, integerPropX\"") + message = "Unmapped target properties: \"stringPropX, integerPropX\".") } ) public void shouldRaiseAmbiguousReverseMethodErrorWrongName() { @@ -99,12 +99,14 @@ public void shouldRaiseAmbiguousReverseMethodErrorWrongName() { @Diagnostic(type = SourceTargetMapperAmbiguous3.class, kind = Kind.ERROR, line = 39, - messageRegExp = "Given name \"forward\" matches several candidate methods: .*forward\\(.+\\), " - + ".*forward\\(.+\\)"), + message = "Given name \"forward\" matches several candidate methods: org.mapstruct.ap.test.reverse" + + ".Target forward(org.mapstruct.ap.test.reverse.Source source), org.mapstruct.ap.test.reverse" + + ".Target forward(org.mapstruct.ap.test.reverse.Source source, @MappingTarget org.mapstruct.ap" + + ".test.reverse.Target target)."), @Diagnostic(type = SourceTargetMapperAmbiguous3.class, kind = Kind.WARNING, line = 44, - messageRegExp = "Unmapped target properties: \"stringPropX, integerPropX\"") + message = "Unmapped target properties: \"stringPropX, integerPropX\".") } ) public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() { @@ -118,12 +120,11 @@ public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() { @Diagnostic(type = SourceTargetMapperNonMatchingName.class, kind = Kind.ERROR, line = 31, - messageRegExp = "Given name \"blah\" does not match the only candidate. Did you mean: " - + "\"forward\"."), + message = "Given name \"blah\" does not match the only candidate. Did you mean: \"forward\"."), @Diagnostic(type = SourceTargetMapperNonMatchingName.class, kind = Kind.WARNING, line = 36, - messageRegExp = "Unmapped target properties: \"stringPropX, integerPropX\"") + message = "Unmapped target properties: \"stringPropX, integerPropX\".") } ) public void shouldAdviseOnSpecifyingCorrectName() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java index 1adc1aba8f..12ba5f111c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java @@ -85,9 +85,12 @@ public void shouldApplyGenericTypeMapper() { diagnostics = { @Diagnostic(type = ErroneousSourceTargetMapper1.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection." - + "generics.UpperBoundWrapper " - + "fooUpperBoundFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeA fooUpperBoundFailure\"") + message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + + ".generics.UpperBoundWrapper " + + "fooUpperBoundFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeA " + + "fooUpperBoundFailure\". Consider to declare/implement a mapping method: \"org.mapstruct.ap.test" + + ".selection.generics.TypeA map(org.mapstruct.ap.test.selection.generics.UpperBoundWrapper value)\".") }) public void shouldFailOnUpperBound() { } @@ -99,10 +102,12 @@ public void shouldFailOnUpperBound() { @Diagnostic(type = ErroneousSourceTargetMapper2.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "No target bean properties found: can't map property " - + "\"org.mapstruct.ap.test.selection.generics.WildCardExtendsWrapper" - + " fooWildCardExtendsTypeAFailure\" to" - + " \"org.mapstruct.ap.test.selection.generics.TypeA fooWildCardExtendsTypeAFailure\"") + message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + + ".generics.WildCardExtendsWrapper " + + "fooWildCardExtendsTypeAFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeA " + + "fooWildCardExtendsTypeAFailure\". Consider to declare/implement a mapping method: \"org" + + ".mapstruct.ap.test.selection.generics.TypeA map(org.mapstruct.ap.test.selection.generics" + + ".WildCardExtendsWrapper value)\".") }) public void shouldFailOnWildCardBound() { } @@ -114,11 +119,12 @@ public void shouldFailOnWildCardBound() { @Diagnostic(type = ErroneousSourceTargetMapper3.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "No target bean properties found: can't map property " - + "\"org.mapstruct.ap.test.selection.generics." - + "WildCardExtendsMBWrapper " - + "fooWildCardExtendsMBTypeBFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeB " - + "fooWildCardExtendsMBTypeBFailure\"") + message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + + ".generics.WildCardExtendsMBWrapper " + + "fooWildCardExtendsMBTypeBFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeB " + + "fooWildCardExtendsMBTypeBFailure\". Consider to declare/implement a mapping method: \"org" + + ".mapstruct.ap.test.selection.generics.TypeB map(org.mapstruct.ap.test.selection.generics" + + ".WildCardExtendsMBWrapper value)\".") }) public void shouldFailOnWildCardMultipleBounds() { } @@ -130,10 +136,12 @@ public void shouldFailOnWildCardMultipleBounds() { @Diagnostic(type = ErroneousSourceTargetMapper4.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "No target bean properties found: can't map property " - + "\"org.mapstruct.ap.test.selection.generics.WildCardSuperWrapper" - + " fooWildCardSuperTypeAFailure\" to" - + " \"org.mapstruct.ap.test.selection.generics.TypeA fooWildCardSuperTypeAFailure\"") + message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + + ".generics.WildCardSuperWrapper " + + "fooWildCardSuperTypeAFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeA " + + "fooWildCardSuperTypeAFailure\". Consider to declare/implement a mapping method: \"org.mapstruct" + + ".ap.test.selection.generics.TypeA map(org.mapstruct.ap.test.selection.generics" + + ".WildCardSuperWrapper value)\".") }) public void shouldFailOnSuperBounds1() { } @@ -145,10 +153,12 @@ public void shouldFailOnSuperBounds1() { @Diagnostic(type = ErroneousSourceTargetMapper5.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - messageRegExp = "No target bean properties found: can't map property " - + "\"org.mapstruct.ap.test.selection.generics.WildCardSuperWrapper" - + " fooWildCardSuperTypeCFailure\" to" - + " \"org.mapstruct.ap.test.selection.generics.TypeC fooWildCardSuperTypeCFailure\"") + message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + + ".generics.WildCardSuperWrapper " + + "fooWildCardSuperTypeCFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeC " + + "fooWildCardSuperTypeCFailure\". Consider to declare/implement a mapping method: \"org.mapstruct" + + ".ap.test.selection.generics.TypeC map(org.mapstruct.ap.test.selection.generics" + + ".WildCardSuperWrapper value)\".") }) public void shouldFailOnSuperBounds2() { } @@ -160,18 +170,19 @@ public void shouldFailOnSuperBounds2() { ErroneousSourceTargetMapper6.class, NoProperties.class }) - @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { - @Diagnostic( type = ErroneousSourceTargetMapper6.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 16, messageRegExp = - ".*\\.generics\\.WildCardSuperWrapper<.*\\.generics\\.TypeA> does not have an " - + "accessible parameterless constructor\\." ), - @Diagnostic( type = ErroneousSourceTargetMapper6.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 16, messageRegExp = - "No target bean properties found: can't map property \".*NoProperties " - + "foo\\.wrapped\" to" - + " \"org.mapstruct.ap.test.selection.generics.TypeA " - + "foo\\.wrapped\"" ) - } ) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { + @Diagnostic(type = ErroneousSourceTargetMapper6.class, kind = javax.tools.Diagnostic.Kind.ERROR, + line = 16, message = "org.mapstruct.ap.test.selection.generics.WildCardSuperWrapper does not have an accessible parameterless constructor."), + @Diagnostic(type = ErroneousSourceTargetMapper6.class, kind = javax.tools.Diagnostic.Kind.ERROR, + line = 16, message = + "No target bean properties found: can't map property \"org.mapstruct.ap.test.NoProperties " + + "foo.wrapped\" to" + + " \"org.mapstruct.ap.test.selection.generics.TypeA " + + "foo.wrapped\". Consider to declare/implement a mapping method: \"org" + + ".mapstruct.ap.test.selection.generics.TypeA map(org.mapstruct.ap.test.NoProperties " + + "value)\".") + }) public void shouldFailOnNonMatchingWildCards() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java index 9ec88d431e..912affa1a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java @@ -91,10 +91,10 @@ public void shouldMatchClassAndMethod() { } @Test - @WithClasses( { + @WithClasses({ YetAnotherMapper.class, ErroneousMapper.class - } ) + }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @@ -102,17 +102,16 @@ public void shouldMatchClassAndMethod() { type = ErroneousMapper.class, kind = Kind.ERROR, line = 28, - messageRegExp = - "No qualifying method found for qualifiers: " - + "org.mapstruct.ap.test.selection.qualifier.annotation.NonQualifierAnnotated and " - + "/ or qualifying names: .*"), + message = + "No qualifying method found for qualifiers: org.mapstruct.ap.test.selection.qualifier.annotation" + + ".NonQualifierAnnotated and / or qualifying names: "), @Diagnostic( type = ErroneousMapper.class, kind = Kind.ERROR, line = 28, - messageRegExp = - "Can't map property \"java.lang.String title\" to \"java.lang.String title\". " - + "Consider to declare/implement a mapping method: \"java.lang.String map(java.lang.String value)*") + message = + "Can't map property \"java.lang.String title\" to \"java.lang.String title\". Consider to " + + "declare/implement a mapping method: \"java.lang.String map(java.lang.String value)\".") } ) public void shouldNotProduceMatchingMethod() { @@ -174,23 +173,24 @@ public void testFactorySelectionWithQualifier() { } @Test - @IssueKey( "342") - @WithClasses( { + @IssueKey("342") + @WithClasses({ ErroneousMovieFactoryMapper.class - } ) + }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = ErroneousMovieFactoryMapper.class, kind = Kind.ERROR, line = 24, - messageRegExp = "'nullValueMappingStrategy', 'nullValuePropertyMappingStrategy', 'resultType' and " + + message = "'nullValueMappingStrategy', 'nullValuePropertyMappingStrategy', 'resultType' and " + "'qualifiedBy' are undefined in @BeanMapping, define at least one of them."), @Diagnostic(type = ErroneousMovieFactoryMapper.class, kind = Kind.ERROR, line = 24, - messageRegExp = "The return type .*\\.AbstractEntry is an abstract class or interface. Provide a non " + - "abstract / non interface result type or a factory method.") + message = "The return type org.mapstruct.ap.test.selection.qualifier.bean.AbstractEntry is an " + + "abstract class or interface. Provide a non abstract / non interface result type or a factory " + + "method.") } ) public void testEmptyBeanMapping() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java index ed9f813489..efdea3c0eb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java @@ -38,20 +38,22 @@ public class InheritanceSelectionTest { @Test - @WithClasses( { ConflictingFruitFactory.class, ErroneousFruitMapper.class, Banana.class } ) + @WithClasses({ ConflictingFruitFactory.class, ErroneousFruitMapper.class, Banana.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = ErroneousFruitMapper.class, kind = Kind.ERROR, line = 23, - messageRegExp = "Ambiguous factory methods found for creating .*Fruit: " - + ".*Apple .*ConflictingFruitFactory\\.createApple\\(\\), " - + ".*Banana .*ConflictingFruitFactory\\.createBanana\\(\\)\\."), + message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.selection.resulttype" + + ".Fruit: org.mapstruct.ap.test.selection.resulttype.Apple org.mapstruct.ap.test.selection" + + ".resulttype.ConflictingFruitFactory.createApple(), org.mapstruct.ap.test.selection.resulttype" + + ".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana()."), @Diagnostic(type = ErroneousFruitMapper.class, kind = Kind.ERROR, line = 23, - messageRegExp = ".*Fruit does not have an accessible parameterless constructor\\.") + message = "org.mapstruct.ap.test.selection.resulttype.Fruit does not have an accessible parameterless" + + " constructor.") } ) public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() { @@ -59,14 +61,15 @@ public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() @IssueKey("1283") @Test - @WithClasses( { ErroneousResultTypeNoEmptyConstructorMapper.class, Banana.class } ) + @WithClasses({ ErroneousResultTypeNoEmptyConstructorMapper.class, Banana.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = ErroneousResultTypeNoEmptyConstructorMapper.class, kind = Kind.ERROR, line = 18, - messageRegExp = ".*\\.resulttype\\.Banana does not have an accessible parameterless constructor\\.") + message = "org.mapstruct.ap.test.selection.resulttype.Banana does not have an accessible " + + "parameterless constructor.") } ) public void testResultTypeHasNoSuitableEmptyConstructor() { @@ -112,8 +115,8 @@ public void testResultTypeBasedConstructionOfResultForInterface() { @Diagnostic(type = ResultTypeConstructingFruitInterfaceErroneousMapper.class, kind = Kind.ERROR, line = 23, - messageRegExp = "The return type .*\\.IsFruit is an abstract class or interface. Provide a non " + - "abstract / non interface result type or a factory method." + message = "The return type org.mapstruct.ap.test.selection.resulttype.IsFruit is an abstract class or" + + " interface. Provide a non abstract / non interface result type or a factory method." ) } ) @@ -129,11 +132,12 @@ public void testResultTypeBasedConstructionOfResultForInterfaceErroneous() { @Diagnostic(type = ErroneousFruitMapper2.class, kind = Kind.ERROR, line = 22, - messageRegExp = ".*\\.Banana not assignable to: .*\\.Apple.") + message = "org.mapstruct.ap.test.selection.resulttype.Banana not assignable to: org.mapstruct.ap.test" + + ".selection.resulttype.Apple.") } ) @IssueKey("434") - @WithClasses( { ErroneousFruitMapper2.class, Banana.class } ) + @WithClasses({ ErroneousFruitMapper2.class, Banana.class }) public void testResultTypeBasedConstructionOfResultNonAssignable() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java index 7182b62740..98a6970598 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.source.constants; -import static org.assertj.core.api.Assertions.assertThat; - import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -18,6 +16,8 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; +import static org.assertj.core.api.Assertions.assertThat; + /** * * @author Sjaak Derksen @@ -70,31 +70,38 @@ public void testNumericConstants() { @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 25, - messageRegExp = "^.*only 'true' or 'false' are supported\\.$"), + message = "Can't map \"java.lang.String \"zz\"\" to \"boolean booleanValue\". Reason: only 'true' or " + + "'false' are supported."), @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 26, - messageRegExp = "^.*invalid character literal\\.$"), + message = "Can't map \"java.lang.String \"'ba'\"\" to \"char charValue\". Reason: invalid character " + + "literal."), @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 27, - messageRegExp = "^.*Value out of range. Value:\"200\" Radix:10\\.$"), - @Diagnostic(type = ErroneousConstantMapper.class, + message = "Can't map \"java.lang.String \"200\"\" to \"byte byteValue\". Reason: Value out of range. " + + "Value:\"200\" Radix:10."), + @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 28, - messageRegExp = "^.*integer number too large.$"), - @Diagnostic(type = ErroneousConstantMapper.class, + message = "Can't map \"java.lang.String \"0xFFFF_FFFF_FFFF\"\" to \"int intValue\". Reason: integer " + + "number too large."), + @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 29, - messageRegExp = "^.*L/l mandatory for long types.$"), - @Diagnostic(type = ErroneousConstantMapper.class, + message = "Can't map \"java.lang.String \"1\"\" to \"long longValue\". Reason: L/l mandatory for long" + + " types."), + @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 30, - messageRegExp = "^.*improperly placed underscores.$"), - @Diagnostic(type = ErroneousConstantMapper.class, + message = "Can't map \"java.lang.String \"1.40e-_45f\"\" to \"float floatValue\". Reason: improperly " + + "placed underscores."), + @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 31, - messageRegExp = "^.*floating point number too small.$") + message = "Can't map \"java.lang.String \"1e-137000\"\" to \"double doubleValue\". Reason: floating " + + "point number too small.") } ) public void miscellaneousDetailMessages() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java index fa10dce5d0..7e7bc2d69e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java @@ -91,12 +91,12 @@ public void shouldMapTargetToSourceWithoutWhining() throws ParseException { @Diagnostic(type = ErroneousMapper1.class, kind = Kind.ERROR, line = 24, - messageRegExp = "Source and constant are both defined in @Mapping, either define a source or a " - + "constant"), + message = "Source and constant are both defined in @Mapping, either define a source or a " + + "constant."), @Diagnostic(type = ErroneousMapper1.class, kind = Kind.WARNING, line = 30, - messageRegExp = "Unmapped target property: \"integerConstant\"") + message = "Unmapped target property: \"integerConstant\".") } ) public void errorOnSourceAndConstant() throws ParseException { @@ -117,13 +117,13 @@ public void errorOnSourceAndConstant() throws ParseException { @Diagnostic(type = ErroneousMapper3.class, kind = Kind.ERROR, line = 24, - messageRegExp = + message = "Expression and constant are both defined in @Mapping, either define an expression or a " - + "constant"), + + "constant."), @Diagnostic(type = ErroneousMapper3.class, kind = Kind.WARNING, line = 30, - messageRegExp = "Unmapped target property: \"integerConstant\"") + message = "Unmapped target property: \"integerConstant\".") } ) public void errorOnConstantAndExpression() throws ParseException { @@ -144,12 +144,12 @@ public void errorOnConstantAndExpression() throws ParseException { @Diagnostic(type = ErroneousMapper4.class, kind = Kind.ERROR, line = 24, - messageRegExp = "Source and expression are both defined in @Mapping, either define a source or an " - + "expression"), + message = "Source and expression are both defined in @Mapping, either define a source or an " + + "expression."), @Diagnostic(type = ErroneousMapper4.class, kind = Kind.WARNING, line = 30, - messageRegExp = "Unmapped target property: \"integerConstant\"") + message = "Unmapped target property: \"integerConstant\".") } ) public void errorOnSourceAndExpression() throws ParseException { @@ -192,13 +192,13 @@ public void shouldMapSameSourcePropertyToSeveralTargetPropertiesFromSeveralSourc @Diagnostic(type = ErroneousMapper5.class, kind = Kind.ERROR, line = 28, - messageRegExp = "^Constant \"DENMARK\" doesn't exist in enum type org.mapstruct.ap.test.source." - + "constants.CountryEnum for property \"country\".$"), + message = "Constant \"DENMARK\" doesn't exist in enum type org.mapstruct.ap.test.source." + + "constants.CountryEnum for property \"country\"."), @Diagnostic(type = ErroneousMapper5.class, kind = Kind.ERROR, line = 28, - messageRegExp = "^Can't map \"java.lang.String \"DENMARK\"\" to \"org.mapstruct.ap.test.source." - + "constants.CountryEnum country\".$") + message = "Can't map \"java.lang.String \"DENMARK\"\" to \"org.mapstruct.ap.test.source." + + "constants.CountryEnum country\".") } ) public void errorOnNonExistingEnumConstant() throws ParseException { @@ -219,8 +219,8 @@ public void errorOnNonExistingEnumConstant() throws ParseException { @Diagnostic(type = ErroneousMapper6.class, kind = Kind.ERROR, line = 25, - messageRegExp = "^.*Can't map \"java.lang.String \"3001\"\" to \"java.lang.Long " - + "longWrapperConstant\".*$") + message = "Can't map \"java.lang.String \"3001\"\" to \"java.lang.Long " + + "longWrapperConstant\". Reason: L/l mandatory for long types.") } ) public void cannotMapIntConstantToLong() throws ParseException { diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java index 09343d597b..24b2ee3538 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java @@ -56,13 +56,13 @@ public void testJavaDefaultExpressionWithNoValues() { @Diagnostic(type = ErroneousDefaultExpressionExpressionMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "Expression and default expression are both defined in @Mapping," + message = "Expression and default expression are both defined in @Mapping," + " either define an expression or a default expression." ), @Diagnostic(type = ErroneousDefaultExpressionExpressionMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 26, - messageRegExp = "Unmapped target property: \"sourceId\"" + message = "Unmapped target property: \"sourceId\"." ) } ) @@ -77,13 +77,13 @@ public void testJavaDefaultExpressionExpression() { @Diagnostic(type = ErroneousDefaultExpressionConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "Constant and default expression are both defined in @Mapping," + message = "Constant and default expression are both defined in @Mapping," + " either define a constant or a default expression." ), @Diagnostic(type = ErroneousDefaultExpressionConstantMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 25, - messageRegExp = "Unmapped target property: \"sourceId\"" + message = "Unmapped target property: \"sourceId\"." ) } ) @@ -98,13 +98,13 @@ public void testJavaDefaultExpressionConstant() { @Diagnostic(type = ErroneousDefaultExpressionDefaultValueMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "Default value and default expression are both defined in @Mapping," + message = "Default value and default expression are both defined in @Mapping," + " either define a default value or a default expression." ), @Diagnostic(type = ErroneousDefaultExpressionDefaultValueMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 25, - messageRegExp = "Unmapped target property: \"sourceId\"" + message = "Unmapped target property: \"sourceId\"." ) } ) @@ -119,7 +119,7 @@ public void testJavaDefaultExpressionDefaultValue() { @Diagnostic(type = ErroneousDefaultExpressionMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "Value for default expression must be given in the form \"java\\(\\)\"" + message = "Value for default expression must be given in the form \"java()\"." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java index 726690ed80..68d19376de 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java @@ -150,13 +150,13 @@ public void testGetterOnly() throws ParseException { @Diagnostic(type = ErroneousSourceTargetMapperExpressionAndQualifiers.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 18, - messageRegExp = "Expression and a qualifier both defined in @Mapping," + + message = "Expression and a qualifier both defined in @Mapping," + " either define an expression or a qualifier." ), @Diagnostic(type = ErroneousSourceTargetMapperExpressionAndQualifiers.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - messageRegExp = "Expression and a qualifier both defined in @Mapping," + + message = "Expression and a qualifier both defined in @Mapping," + " either define an expression or a qualifier." ) } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java index 2f56517e06..549397def7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java @@ -166,15 +166,15 @@ public void shouldUseConfig() { @Diagnostic(type = ErroneousSourceTargetMapper.class, kind = Kind.ERROR, line = 16, - messageRegExp = "Several possible source properties for target property \"street\"."), + message = "Several possible source properties for target property \"street\"."), @Diagnostic(type = ErroneousSourceTargetMapper.class, kind = Kind.ERROR, line = 16, - messageRegExp = "Several possible source properties for target property \"zipCode\"."), + message = "Several possible source properties for target property \"zipCode\"."), @Diagnostic(type = ErroneousSourceTargetMapper.class, kind = Kind.ERROR, line = 16, - messageRegExp = "Several possible source properties for target property \"description\".") + message = "Several possible source properties for target property \"description\".") }) public void shouldFailToGenerateMappingsForAmbigiousSourceProperty() { } @@ -193,8 +193,8 @@ public void shouldFailToGenerateMappingsForAmbigiousSourceProperty() { type = ErroneousSourceTargetMapper2.class, kind = Kind.ERROR, line = 15, - messageRegExp = "Method has no source parameter named \"houseNo\"\\." + - " Method source parameters are: \"address, person\"\\." + message = "Method has no source parameter named \"houseNo\"." + + " Method source parameters are: \"address, person\"." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/targetthis/TargetThisMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/targetthis/TargetThisMappingTest.java index dea128b27f..6ef6c29532 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/targetthis/TargetThisMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/targetthis/TargetThisMappingTest.java @@ -97,11 +97,11 @@ public void testTargetingThisWithIgnore() { @Diagnostic(type = ErroneousNestedMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "^Several possible source properties for target property \"id\"\\.$"), + message = "Several possible source properties for target property \"id\"."), @Diagnostic(type = ErroneousNestedMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - messageRegExp = "^Several possible source properties for target property \"status\"\\.$") + message = "Several possible source properties for target property \"status\".") } ) public void testNestedDuplicates() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java b/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java index 1b5c9c9cc6..f5eda7024d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java @@ -122,13 +122,13 @@ public void shouldInheritConfigurationSeveralArgs() { @Diagnostic(type = SourceTargetMapperAmbiguous1.class, kind = Kind.ERROR, line = 43, - messageRegExp = "Several matching methods exist: forwardCreate\\(\\), " - + "forwardCreate1\\(\\). Specify a name explicitly."), + message = "Several matching methods exist: forwardCreate(), " + + "forwardCreate1(). Specify a name explicitly."), @Diagnostic(type = SourceTargetMapperAmbiguous1.class, kind = Kind.WARNING, line = 44, - messageRegExp = "Unmapped target properties: \"stringPropY, integerPropY, constantProp, " - + "expressionProp, nestedResultProp\"") + message = "Unmapped target properties: \"stringPropY, integerPropY, constantProp, " + + "expressionProp, nestedResultProp\".") } ) public void shouldRaiseAmbiguousReverseMethodError() { @@ -142,13 +142,13 @@ public void shouldRaiseAmbiguousReverseMethodError() { @Diagnostic(type = SourceTargetMapperAmbiguous2.class, kind = Kind.ERROR, line = 43, - messageRegExp = "None of the candidates forwardCreate\\(\\), forwardCreate1\\(\\) matches given " + message = "None of the candidates forwardCreate(), forwardCreate1() matches given " + "name: \"blah\"."), @Diagnostic(type = SourceTargetMapperAmbiguous2.class, kind = Kind.WARNING, line = 44, - messageRegExp = "Unmapped target properties: \"stringPropY, integerPropY, constantProp, " - + "expressionProp, nestedResultProp\"") + message = "Unmapped target properties: \"stringPropY, integerPropY, constantProp, " + + "expressionProp, nestedResultProp\".") } ) public void shouldRaiseAmbiguousReverseMethodErrorWrongName() { @@ -162,13 +162,16 @@ public void shouldRaiseAmbiguousReverseMethodErrorWrongName() { @Diagnostic(type = SourceTargetMapperAmbiguous3.class, kind = Kind.ERROR, line = 43, - messageRegExp = "Given name \"forwardCreate\" matches several candidate methods: " - + ".*forwardCreate.*, .*forwardCreate.*"), + message = "Given name \"forwardCreate\" matches several candidate methods: org.mapstruct.ap.test" + + ".template.Target forwardCreate(org.mapstruct.ap.test.template.Source source), void forwardCreate" + + "(org.mapstruct.ap.test.template.Source source, @MappingTarget org.mapstruct.ap.test.template" + + ".Target target)."), @Diagnostic(type = SourceTargetMapperAmbiguous3.class, kind = Kind.WARNING, line = 44, - messageRegExp = "Unmapped target properties: \"stringPropY, integerPropY, constantProp, " - + "expressionProp, nestedResultProp\"") } + message = "Unmapped target properties: \"stringPropY, integerPropY, constantProp, expressionProp, " + + "nestedResultProp\".") + } ) public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() { } @@ -181,13 +184,12 @@ public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() { @Diagnostic(type = SourceTargetMapperNonMatchingName.class, kind = Kind.ERROR, line = 34, - messageRegExp = "Given name \"blah\" does not match the only candidate. Did you mean: " - + "\"forwardCreate\"."), + message = "Given name \"blah\" does not match the only candidate. Did you mean: \"forwardCreate\"."), @Diagnostic(type = SourceTargetMapperNonMatchingName.class, - kind = Kind.WARNING, + kind = Kind.WARNING, line = 35, - messageRegExp = "Unmapped target properties: \"stringPropY, integerPropY, constantProp, " - + "expressionProp, nestedResultProp\"") + message = "Unmapped target properties: \"stringPropY, integerPropY, constantProp, expressionProp, " + + "nestedResultProp\".") } ) public void shouldAdviseOnSpecifyingCorrectName() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java index 999064237d..0adcb9e0ff 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java @@ -35,11 +35,11 @@ public class UnmappedSourceTest { @Diagnostic(type = SourceTargetMapper.class, kind = Kind.WARNING, line = 20, - messageRegExp = "Unmapped source property: \"qux\""), + message = "Unmapped source property: \"qux\"."), @Diagnostic(type = SourceTargetMapper.class, kind = Kind.WARNING, line = 22, - messageRegExp = "Unmapped source property: \"bar\"") + message = "Unmapped source property: \"bar\".") } ) public void shouldLeaveUnmappedSourcePropertyUnset() { @@ -61,11 +61,11 @@ public void shouldLeaveUnmappedSourcePropertyUnset() { @Diagnostic(type = ErroneousStrictSourceTargetMapper.class, kind = Kind.ERROR, line = 20, - messageRegExp = "Unmapped source property: \"qux\""), + message = "Unmapped source property: \"qux\"."), @Diagnostic(type = ErroneousStrictSourceTargetMapper.class, kind = Kind.ERROR, line = 22, - messageRegExp = "Unmapped source property: \"bar\"") + message = "Unmapped source property: \"bar\".") } ) public void shouldRaiseErrorDueToUnsetSourceProperty() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java index b027202d96..4a31fb93ea 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java @@ -36,11 +36,11 @@ public class UnmappedProductTest { @Diagnostic(type = SourceTargetMapper.class, kind = Kind.WARNING, line = 16, - messageRegExp = "Unmapped target property: \"bar\""), + message = "Unmapped target property: \"bar\"."), @Diagnostic(type = SourceTargetMapper.class, kind = Kind.WARNING, line = 18, - messageRegExp = "Unmapped target property: \"qux\"") + message = "Unmapped target property: \"qux\".") } ) public void shouldLeaveUnmappedTargetPropertyUnset() { @@ -62,11 +62,11 @@ public void shouldLeaveUnmappedTargetPropertyUnset() { @Diagnostic(type = ErroneousStrictSourceTargetMapper.class, kind = Kind.ERROR, line = 17, - messageRegExp = "Unmapped target property: \"bar\""), + message = "Unmapped target property: \"bar\"."), @Diagnostic(type = ErroneousStrictSourceTargetMapper.class, kind = Kind.ERROR, line = 19, - messageRegExp = "Unmapped target property: \"qux\"") + message = "Unmapped target property: \"qux\".") } ) public void shouldRaiseErrorDueToUnsetTargetProperty() { @@ -81,11 +81,11 @@ public void shouldRaiseErrorDueToUnsetTargetProperty() { @Diagnostic(type = SourceTargetMapper.class, kind = Kind.ERROR, line = 16, - messageRegExp = "Unmapped target property: \"bar\""), + message = "Unmapped target property: \"bar\"."), @Diagnostic(type = SourceTargetMapper.class, kind = Kind.ERROR, line = 18, - messageRegExp = "Unmapped target property: \"qux\"") + message = "Unmapped target property: \"qux\".") } ) public void shouldRaiseErrorDueToUnsetTargetPropertyWithPolicySetViaProcessorOption() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java index d2038a9c5c..f04ddf338d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java @@ -155,48 +155,50 @@ public void testPreferUpdateMethodEncapsulatingCreateMethod() { @Diagnostic(type = ErroneousOrganizationMapper1.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - messageRegExp = "No read accessor found for property \"company\" in target type.") + message = "No read accessor found for property \"company\" in target type.") } ) public void testShouldFailOnPropertyMappingNoPropertyGetter() { } @Test - @WithClasses( { + @WithClasses({ ErroneousOrganizationMapper2.class, OrganizationWithoutTypeGetterEntity.class - } ) + }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = ErroneousOrganizationMapper2.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 23, - messageRegExp = "No read accessor found for property \"type\" in target type."), + message = "No read accessor found for property \"type\" in target type."), @Diagnostic(type = ErroneousOrganizationMapper2.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 35, - messageRegExp = ".*\\.updatemethods\\.DepartmentEntity does not have an accessible parameterless " + - "constructor\\.") + message = "org.mapstruct.ap.test.updatemethods.DepartmentEntity does not have an accessible " + + "parameterless constructor.") }) - public void testShouldFailOnConstantMappingNoPropertyGetter() { } + public void testShouldFailOnConstantMappingNoPropertyGetter() { + } @Test - @WithClasses( { + @WithClasses({ CompanyMapper1.class, DepartmentInBetween.class, UnmappableCompanyDto.class, UnmappableDepartmentDto.class - } ) + }) @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, diagnostics = { @Diagnostic(type = CompanyMapper1.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 23, - messageRegExp = "Unmapped target property: \"employees\"\\. Mapping from property \"" + - ".*UnmappableDepartmentDto department\" to \".*DepartmentEntity department.") + message = "Unmapped target property: \"employees\". Mapping from property \"org.mapstruct.ap.test" + + ".updatemethods.UnmappableDepartmentDto department\" to \"org.mapstruct.ap.test.updatemethods" + + ".DepartmentEntity department\".") } ) public void testShouldNotUseTwoNestedUpdateMethods() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java index 51f867947a..43b621f8cc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java @@ -217,7 +217,7 @@ public void shouldMapAnyRemainingToNullCorrectly() { @Diagnostic(type = ErroneousOrderMapperMappingSameConstantTwice.class, kind = Kind.ERROR, line = 27, - messageRegExp = "Source value mapping: \"EXTRA\" cannot be mapped more than once\\.") + message = "Source value mapping: \"EXTRA\" cannot be mapped more than once.") } ) public void shouldRaiseErrorIfSameSourceEnumConstantIsMappedTwice() { @@ -231,12 +231,12 @@ public void shouldRaiseErrorIfSameSourceEnumConstantIsMappedTwice() { @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, kind = Kind.ERROR, line = 26, - messageRegExp = "Constant FOO doesn't exist in enum type org.mapstruct.ap.test.value.OrderType\\."), + message = "Constant FOO doesn't exist in enum type org.mapstruct.ap.test.value.OrderType."), @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, kind = Kind.ERROR, line = 27, - messageRegExp = "Constant BAR doesn't exist in enum type org.mapstruct.ap.test.value." + - "ExternalOrderType\\.") + message = "Constant BAR doesn't exist in enum type org.mapstruct.ap.test.value." + + "ExternalOrderType.") } ) public void shouldRaiseErrorIfUnknownEnumConstantsAreSpecifiedInMapping() { @@ -250,8 +250,8 @@ public void shouldRaiseErrorIfUnknownEnumConstantsAreSpecifiedInMapping() { @Diagnostic(type = ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.class, kind = Kind.ERROR, line = 23, - messageRegExp = "The following constants from the source enum have no corresponding constant in the " + - "target enum and must be be mapped via adding additional mappings: EXTRA, STANDARD, NORMAL") + message = "The following constants from the source enum have no corresponding constant in the " + + "target enum and must be be mapped via adding additional mappings: EXTRA, STANDARD, NORMAL.") } ) public void shouldRaiseErrorIfSourceConstantWithoutMatchingConstantInTargetTypeIsNotMapped() { @@ -265,7 +265,7 @@ public void shouldRaiseErrorIfSourceConstantWithoutMatchingConstantInTargetTypeI @Diagnostic(type = ErroneousOrderMapperDuplicateANY.class, kind = Kind.ERROR, line = 28, - messageRegExp = "Source = \"\" or \"\" can only be used once\\." ) + message = "Source = \"\" or \"\" can only be used once." ) } ) public void shouldRaiseErrorIfMappingsContainDuplicateANY() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java index 2f1be53125..145721b83d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java @@ -50,8 +50,8 @@ public void testRemainingAndNull() { @Diagnostic(type = ErroneousOrderMapperUsingAnyRemaining.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 26, - messageRegExp = "\"\" can only be used on targets of type enum and not for " + - "java\\.lang\\.String\\.") + message = "Source = \"\" can only be used on targets of type enum and not for " + + "java.lang.String.") } ) public void shouldRaiseErrorWhenUsingAnyRemaining() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java index 552766d240..7bf06ab860 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java @@ -49,8 +49,8 @@ public void testRemainingAndNull() { @Diagnostic(type = ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 28, - messageRegExp = "Source = \"\" or \"\" is advisable for mapping of " + - "type String to an enum type" ) + message = "Source = \"\" or \"\" is advisable for mapping of " + + "type String to an enum type." ) } ) public void shouldRaiseErrorWhenUsingAnyRemaining() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java b/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java index c042e3d073..fd17eed070 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java @@ -60,10 +60,10 @@ public void testGeneralWithOtherSPI() { } @Test - @DisabledOnCompiler( { + @DisabledOnCompiler({ Compiler.ECLIPSE, Compiler.ECLIPSE11 - } ) + }) @WithServiceImplementation(provides = AstModifyingAnnotationProcessor.class, value = AstModifyingAnnotationProcessorSaysNo.class) @ProcessorOption(name = "mapstruct.verbose", value = "true") @@ -74,8 +74,10 @@ public void testGeneralWithOtherSPI() { type = CreateBeanMapping.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 12, - messageRegExp = ".*No implementation was created for CreateBeanMapping due to having a problem in the " + - "erroneous element .*" + message = "No implementation was created for CreateBeanMapping due to having a problem in the erroneous " + + "element java.util.ArrayList. Hint: this often means that some other annotation processor was " + + "supposed to process the erroneous element. You can also enable MapStruct verbose mode by setting " + + "-Amapstruct.verbose=true as a compilation argument." ) }) public void testDeferred() { diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/annotation/Diagnostic.java b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/annotation/Diagnostic.java index b2016f1c2d..982c3e42e7 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/annotation/Diagnostic.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/annotation/Diagnostic.java @@ -45,6 +45,14 @@ */ long alternativeLine() default -1; + /** + * A message matching the exact expected message of the diagnostic. + * + * @return A message matching the exact expected message of the + * diagnostic. + */ + String message() default ""; + /** * A regular expression matching the expected message of the diagnostic. * Wild-cards matching any character (".*") will be added to the beginning diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java index 5a87bf0a09..94dda74708 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java @@ -29,17 +29,20 @@ public class DiagnosticDescriptor { private final Long line; private final Long alternativeLine; private final String message; + private final String messageRegex; - private DiagnosticDescriptor(String sourceFileName, Kind kind, Long line, String message) { - this( sourceFileName, kind, line, null, message ); + private DiagnosticDescriptor(String sourceFileName, Kind kind, Long line, String message, String messageRegex) { + this( sourceFileName, kind, line, null, message, messageRegex ); } - private DiagnosticDescriptor(String sourceFileName, Kind kind, Long line, Long alternativeLine, String message) { + private DiagnosticDescriptor(String sourceFileName, Kind kind, Long line, Long alternativeLine, String message, + String messageRegex) { this.sourceFileName = sourceFileName; this.kind = kind; this.line = line; this.alternativeLine = alternativeLine; this.message = message; + this.messageRegex = messageRegex; } public static DiagnosticDescriptor forDiagnostic(Diagnostic diagnostic) { @@ -51,6 +54,7 @@ public static DiagnosticDescriptor forDiagnostic(Diagnostic diagnostic) { diagnostic.kind(), diagnostic.line() != -1 ? diagnostic.line() : null, diagnostic.alternativeLine() != -1 ? diagnostic.alternativeLine() : null, + diagnostic.message(), diagnostic.messageRegExp() ); } @@ -61,7 +65,8 @@ public static DiagnosticDescriptor forDiagnostic(String sourceDir, getSourceName( sourceDir, diagnostic ), diagnostic.getKind(), diagnostic.getLineNumber(), - diagnostic.getMessage( null ) + diagnostic.getMessage( null ), + null ); } @@ -73,7 +78,9 @@ public static DiagnosticDescriptor forCompilerMessage(String sourceDir, Compiler removeSourceDirPrefix( sourceDir, compilerMessage.getFile() ), toJavaxKind( compilerMessage.getKind() ), Long.valueOf( compilerMessage.getStartLine() ), - message ); + message, + null + ); } private static Kind toJavaxKind(CompilerMessage.Kind kind) { @@ -151,6 +158,10 @@ public String getMessage() { return message; } + public String getMessageRegex() { + return messageRegex; + } + @Override public int hashCode() { final int prime = 31; diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java index ca51554f30..e735db715d 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java @@ -311,14 +311,26 @@ else if ( expected.getLine() != null ) { assertThat( actual.getLine() ).isEqualTo( expected.getLine() ); } assertThat( actual.getKind() ).isEqualTo( expected.getKind() ); - assertThat( actual.getMessage() ).describedAs( - String.format( - "Unexpected message for diagnostic %s:%s %s", - actual.getSourceFileName(), - actual.getLine(), - actual.getKind() - ) - ).matches( "(?ms).*" + expected.getMessage() + ".*" ); + if ( expected.getMessage() != null && !expected.getMessage().isEmpty() ) { + assertThat( actual.getMessage() ).describedAs( + String.format( + "Unexpected message for diagnostic %s:%s %s", + actual.getSourceFileName(), + actual.getLine(), + actual.getKind() + ) + ).isEqualTo( expected.getMessage() ); + } + else { + assertThat( actual.getMessage() ).describedAs( + String.format( + "Unexpected message for diagnostic %s:%s %s", + actual.getSourceFileName(), + actual.getLine(), + actual.getKind() + ) + ).matches( "(?ms).*" + expected.getMessage() + ".*" ); + } } } From 1bbc4e1ca852d2fe414cf7813c0de1ac6dab71f3 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 12 Apr 2020 12:26:07 +0200 Subject: [PATCH 044/597] Use expected.getMessageRegex when no message for the Diagnostic descriptor has been defined --- .../org/mapstruct/ap/testutil/runner/CompilingStatement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java index e735db715d..e8c09bec98 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java @@ -329,7 +329,7 @@ else if ( expected.getLine() != null ) { actual.getLine(), actual.getKind() ) - ).matches( "(?ms).*" + expected.getMessage() + ".*" ); + ).matches( "(?ms).*" + expected.getMessageRegex() + ".*" ); } } } From c58f80cc5f403a1d177411c6c633fa4faaaa9d83 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 12 Apr 2020 13:27:31 +0200 Subject: [PATCH 045/597] #2069 Refactor target reference to report errors during bean mapping instead of creation of the target reference With this we can more easily go in the direction of using constructor to map into target beans. --- .../resources/build-config/checkstyle.xml | 4 +- .../ap/internal/model/BeanMappingMethod.java | 227 ++++++++--- .../internal/model/MappingBuilderContext.java | 4 + .../NestedTargetPropertyMappingHolder.java | 107 +++-- .../ap/internal/model/PropertyMapping.java | 34 +- .../model/beanmapping/MappingReference.java | 6 +- .../model/beanmapping/MappingReferences.java | 14 +- .../model/beanmapping/PropertyEntry.java | 48 +-- .../model/beanmapping/TargetReference.java | 381 +++++------------- .../internal/model/source/MappingOptions.java | 12 + .../DefaultModelElementProcessorContext.java | 1 + .../processor/MapperCreationProcessor.java | 6 +- .../ap/internal/util/FormattingMessager.java | 2 + .../mapstruct/ap/internal/util/Message.java | 2 + .../common/DefaultConversionContextTest.java | 4 + .../ap/test/bugs/_1153/Issue1153Test.java | 10 +- .../SuggestMostSimilarNameTest.java | 16 +- .../selection/generics/ConversionTest.java | 5 +- .../ErroneousSourceTargetMapper6.java | 8 + 19 files changed, 436 insertions(+), 455 deletions(-) diff --git a/build-config/src/main/resources/build-config/checkstyle.xml b/build-config/src/main/resources/build-config/checkstyle.xml index 5ae53dc46f..945015d5cd 100644 --- a/build-config/src/main/resources/build-config/checkstyle.xml +++ b/build-config/src/main/resources/build-config/checkstyle.xml @@ -122,7 +122,9 @@ - + + + diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 1fcf8e4ce2..5cb2192aae 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -22,12 +22,13 @@ import javax.lang.model.type.DeclaredType; import javax.tools.Diagnostic; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.model.PropertyMapping.ConstantMappingBuilder; import org.mapstruct.ap.internal.model.PropertyMapping.JavaExpressionMappingBuilder; import org.mapstruct.ap.internal.model.PropertyMapping.PropertyMappingBuilder; import org.mapstruct.ap.internal.model.beanmapping.MappingReference; import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; -import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry; import org.mapstruct.ap.internal.model.beanmapping.SourceReference; import org.mapstruct.ap.internal.model.beanmapping.TargetReference; import org.mapstruct.ap.internal.model.common.BuilderType; @@ -40,8 +41,6 @@ import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; -import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; -import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; @@ -96,7 +95,6 @@ public Builder returnTypeBuilder( BuilderType returnTypeBuilder ) { public Builder sourceMethod(SourceMethod sourceMethod) { this.method = sourceMethod; - this.mappingReferences = forSourceMethod( sourceMethod, ctx.getMessager(), ctx.getTypeFactory() ); return this; } @@ -131,6 +129,7 @@ public BeanMappingMethod build() { MethodReference factoryMethod = null; // determine which return type to construct + boolean cannotConstructReturnType = false; if ( !method.getReturnType().isVoid() ) { Type returnTypeImpl = getReturnTypeToConstructFromSelectionParameters( selectionParameters ); if ( returnTypeImpl != null ) { @@ -138,6 +137,9 @@ public BeanMappingMethod build() { if ( factoryMethod != null || canResultTypeFromBeanMappingBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } + else { + cannotConstructReturnType = true; + } } else if ( isBuilderRequired() ) { returnTypeImpl = returnTypeBuilder.getBuilder(); @@ -145,6 +147,9 @@ else if ( isBuilderRequired() ) { if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } + else { + cannotConstructReturnType = true; + } } else if ( !method.isUpdateMethod() ) { returnTypeImpl = method.getReturnType(); @@ -152,9 +157,17 @@ else if ( !method.isUpdateMethod() ) { if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } + else { + cannotConstructReturnType = true; + } } } + if ( cannotConstructReturnType ) { + // If the return type cannot be constructed then no need to try to create mappings + return null; + } + /* the type that needs to be used in the mapping process as target */ Type resultTypeToMap = returnTypeToConstruct == null ? method.getResultType() : returnTypeToConstruct; @@ -187,8 +200,10 @@ else if ( !method.isUpdateMethod() ) { } } + initializeMappingReferencesIfNeeded( resultTypeToMap ); + // map properties with mapping - boolean mappingErrorOccurred = handleDefinedMappings(); + boolean mappingErrorOccurred = handleDefinedMappings( resultTypeToMap ); if ( mappingErrorOccurred ) { return null; } @@ -261,6 +276,20 @@ else if ( !method.isUpdateMethod() ) { ); } + private void initializeMappingReferencesIfNeeded(Type resultTypeToMap) { + if ( mappingReferences == null && method instanceof SourceMethod ) { + Set readAndWriteTargetProperties = new HashSet<>( unprocessedTargetProperties.keySet() ); + readAndWriteTargetProperties.addAll( resultTypeToMap.getPropertyReadAccessors().keySet() ); + mappingReferences = forSourceMethod( + (SourceMethod) method, + resultTypeToMap, + readAndWriteTargetProperties, + ctx.getMessager(), + ctx.getTypeFactory() + ); + } + } + /** * @return builder is required when there is a returnTypeBuilder and the mapping method is not update method. * However, builder is also required when there is a returnTypeBuilder, the mapping target is the builder and @@ -320,9 +349,11 @@ private void handleUnprocessedDefinedTargets() { PropertyMapping propertyMapping = new PropertyMappingBuilder() .mappingContext( ctx ) .sourceMethod( method ) - .targetWriteAccessor( unprocessedTargetProperties.get( propertyName ) ) - .targetReadAccessor( targetPropertyReadAccessor ) - .targetPropertyName( propertyName ) + .target( + propertyName, + targetPropertyReadAccessor, + unprocessedTargetProperties.get( propertyName ) + ) .sourceReference( reference ) .existingVariableNames( existingVariableNames ) .dependsOn( mappingRefs.collectNestedDependsOn() ) @@ -464,22 +495,24 @@ private MethodReference getFactoryMethod(Type returnTypeImpl, SelectionParameter *

      * It is furthermore checked whether the given mappings are correct. When an error occurs, the method continues * in search of more problems. + * + * @param resultTypeToMap the type in which the defined target properties are defined */ - private boolean handleDefinedMappings() { + private boolean handleDefinedMappings(Type resultTypeToMap) { boolean errorOccurred = false; Set handledTargets = new HashSet<>(); // first we have to handle nested target mappings if ( mappingReferences.hasNestedTargetReferences() ) { - errorOccurred = handleDefinedNestedTargetMapping( handledTargets ); + errorOccurred = handleDefinedNestedTargetMapping( handledTargets, resultTypeToMap ); } for ( MappingReference mapping : mappingReferences.getMappingReferences() ) { if ( mapping.isValid() ) { String target = mapping.getTargetReference().getShallowestPropertyName(); if ( !handledTargets.contains( target ) ) { - if ( handleDefinedMapping( mapping, handledTargets ) ) { + if ( handleDefinedMapping( mapping, resultTypeToMap, handledTargets ) ) { errorOccurred = true; } } @@ -504,11 +537,13 @@ private boolean handleDefinedMappings() { return errorOccurred; } - private boolean handleDefinedNestedTargetMapping(Set handledTargets) { + private boolean handleDefinedNestedTargetMapping(Set handledTargets, Type resultTypeToMap) { NestedTargetPropertyMappingHolder holder = new NestedTargetPropertyMappingHolder.Builder() .mappingContext( ctx ) .method( method ) + .targetPropertiesWriteAccessors( unprocessedTargetProperties ) + .targetPropertyType( resultTypeToMap ) .mappingReferences( mappingReferences ) .existingVariableNames( existingVariableNames ) .build(); @@ -517,26 +552,24 @@ private boolean handleDefinedNestedTargetMapping(Set handledTargets) { propertyMappings.addAll( holder.getPropertyMappings() ); handledTargets.addAll( holder.getHandledTargets() ); // Store all the unprocessed defined targets. - for ( Entry> entry : holder.getUnprocessedDefinedTarget() + for ( Entry> entry : holder.getUnprocessedDefinedTarget() .entrySet() ) { if ( entry.getValue().isEmpty() ) { continue; } - unprocessedDefinedTargets.put( entry.getKey().getName(), entry.getValue() ); + unprocessedDefinedTargets.put( entry.getKey(), entry.getValue() ); } return holder.hasErrorOccurred(); } - private boolean handleDefinedMapping(MappingReference mappingRef, Set handledTargets) { - + private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTypeToMap, + Set handledTargets) { boolean errorOccured = false; PropertyMapping propertyMapping = null; TargetReference targetRef = mappingRef.getTargetReference(); MappingOptions mapping = mappingRef.getMapping(); - PropertyEntry targetProperty = first( targetRef.getPropertyEntries() ); - String targetPropertyName = targetProperty.getName(); // unknown properties given via dependsOn()? for ( String dependency : mapping.getDependsOn() ) { @@ -552,16 +585,95 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Set ha } } + String targetPropertyName = first( targetRef.getPropertyEntries() ); + // check if source / expression / constant are not somehow handled already if ( unprocessedDefinedTargets.containsKey( targetPropertyName ) ) { return false; } + Accessor targetWriteAccessor = unprocessedTargetProperties.get( targetPropertyName ); + Accessor targetReadAccessor = resultTypeToMap.getPropertyReadAccessors().get( targetPropertyName ); + + if ( targetWriteAccessor == null ) { + if ( targetReadAccessor == null ) { + Set readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet(); + String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); + + Message msg; + Object[] args; + + if ( targetRef.getPathProperties().isEmpty() ) { + msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE; + args = new Object[] { + targetPropertyName, + resultTypeToMap, + mostSimilarProperty + }; + } + else { + List pathProperties = new ArrayList<>( targetRef.getPathProperties() ); + pathProperties.add( mostSimilarProperty ); + msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE; + args = new Object[] { + targetPropertyName, + resultTypeToMap, + mapping.getTargetName(), + Strings.join( pathProperties, "." ) + }; + } + + ctx.getMessager() + .printMessage( + mapping.getElement(), + mapping.getMirror(), + mapping.getTargetAnnotationValue(), + msg, + args + ); + return true; + } + else if ( mapping.getInheritContext() != null && mapping.getInheritContext().isReversed() ) { + // read only reversed mappings are implicitly ignored + return false; + } + else if ( !mapping.isIgnored() ) { + // report an error for read only mappings + Message msg; + Object[] args; + + if ( Objects.equals( targetPropertyName, mapping.getTargetName() ) ) { + msg = Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE; + args = new Object[] { + mapping.getTargetName(), + resultTypeToMap + }; + } + else { + msg = Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_TYPE; + args = new Object[] { + targetPropertyName, + resultTypeToMap, + mapping.getTargetName() + }; + } + ctx.getMessager() + .printMessage( + mapping.getElement(), + mapping.getMirror(), + mapping.getTargetAnnotationValue(), + msg, + args + ); + return true; + } + } + // check the mapping options // its an ignored property mapping if ( mapping.isIgnored() ) { propertyMapping = null; - handledTargets.add( targetProperty.getName() ); + handledTargets.add( targetPropertyName ); } // its a constant @@ -573,8 +685,7 @@ else if ( mapping.getConstant() != null ) { .mappingContext( ctx ) .sourceMethod( method ) .constantExpression( mapping.getConstant() ) - .targetProperty( targetProperty ) - .targetPropertyName( targetPropertyName ) + .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) .formattingParameters( mapping.getFormattingParameters() ) .selectionParameters( mapping.getSelectionParameters() ) .options( mapping ) @@ -595,8 +706,7 @@ else if ( mapping.getJavaExpression() != null ) { .sourceMethod( method ) .javaExpression( mapping.getJavaExpression() ) .existingVariableNames( existingVariableNames ) - .targetProperty( targetProperty ) - .targetPropertyName( targetPropertyName ) + .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) .dependsOn( mapping.getDependsOn() ) .mirror( mapping.getMirror() ) .build(); @@ -618,8 +728,7 @@ else if ( mapping.getJavaExpression() != null ) { propertyMapping = new PropertyMappingBuilder() .mappingContext( ctx ) .sourceMethod( method ) - .targetProperty( targetProperty ) - .targetPropertyName( targetPropertyName ) + .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) .sourcePropertyName( mapping.getSourceName() ) .sourceReference( sourceRef ) .selectionParameters( mapping.getSelectionParameters() ) @@ -723,15 +832,13 @@ private void applyPropertyNameBasedMapping(List sourceReference method.getResultType().getPropertyReadAccessors().get( targetPropertyName ); MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false ); PropertyMapping propertyMapping = new PropertyMappingBuilder().mappingContext( ctx ) - .sourceMethod( method ) - .targetWriteAccessor( targetPropertyWriteAccessor ) - .targetReadAccessor( targetPropertyReadAccessor ) - .targetPropertyName( targetPropertyName ) - .sourceReference( sourceRef ) - .existingVariableNames( existingVariableNames ) - .forgeMethodWithMappingReferences( mappingRefs ) - .options( method.getOptions().getBeanMapping() ) - .build(); + .sourceMethod( method ) + .target( targetPropertyName, targetPropertyReadAccessor, targetPropertyWriteAccessor ) + .sourceReference( sourceRef ) + .existingVariableNames( existingVariableNames ) + .forgeMethodWithMappingReferences( mappingRefs ) + .options( method.getOptions().getBeanMapping() ) + .build(); unprocessedSourceParameters.remove( sourceRef.getParameter() ); @@ -770,9 +877,7 @@ private void applyParameterNameBasedMapping() { PropertyMapping propertyMapping = new PropertyMappingBuilder() .mappingContext( ctx ) .sourceMethod( method ) - .targetWriteAccessor( targetProperty.getValue() ) - .targetReadAccessor( targetPropertyReadAccessor ) - .targetPropertyName( targetProperty.getKey() ) + .target( targetProperty.getKey(), targetPropertyReadAccessor, targetProperty.getValue() ) .sourceReference( sourceRef ) .existingVariableNames( existingVariableNames ) .forgeMethodWithMappingReferences( mappingRefs ) @@ -866,17 +971,25 @@ private void reportErrorForUnmappedTargetPropertiesIfRequired() { } else if ( !unprocessedTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) { - Message msg = unmappedTargetPolicy.getDiagnosticKind() == Diagnostic.Kind.ERROR ? - Message.BEANMAPPING_UNMAPPED_TARGETS_ERROR : Message.BEANMAPPING_UNMAPPED_TARGETS_WARNING; - Object[] args = new Object[] { - MessageFormat.format( - "{0,choice,1#property|1 processedSourceParameters; private final Set handledTargets; private final List propertyMappings; - private final Map> unprocessedDefinedTarget; + private final Map> unprocessedDefinedTarget; private final boolean errorOccurred; public NestedTargetPropertyMappingHolder( List processedSourceParameters, Set handledTargets, List propertyMappings, - Map> unprocessedDefinedTarget, boolean errorOccurred) { + Map> unprocessedDefinedTarget, boolean errorOccurred) { this.processedSourceParameters = processedSourceParameters; this.handledTargets = handledTargets; this.propertyMappings = propertyMappings; @@ -75,7 +79,7 @@ public List getPropertyMappings() { /** * @return a map of all the unprocessed defined targets that can be applied to name forged base methods */ - public Map> getUnprocessedDefinedTarget() { + public Map> getUnprocessedDefinedTarget() { return unprocessedDefinedTarget; } @@ -94,6 +98,9 @@ public static class Builder { private Set existingVariableNames; private List propertyMappings; private Set handledTargets; + private Map targetPropertiesWriteAccessors; + private Type targetType; + private boolean errorOccurred; public Builder mappingReferences(MappingReferences mappingReferences) { this.mappingReferences = mappingReferences; @@ -115,6 +122,16 @@ public Builder existingVariableNames(Set existingVariableNames) { return this; } + public Builder targetPropertiesWriteAccessors(Map targetPropertiesWriteAccessors) { + this.targetPropertiesWriteAccessors = targetPropertiesWriteAccessors; + return this; + } + + public Builder targetPropertyType(Type targetType) { + this.targetType = targetType; + return this; + } + public NestedTargetPropertyMappingHolder build() { List processedSourceParameters = new ArrayList<>(); handledTargets = new HashSet<>(); @@ -123,11 +140,11 @@ public NestedTargetPropertyMappingHolder build() { // first we group by the first property in the target properties and for each of those // properties we get the new mappings as if the first property did not exist. GroupedTargetReferences groupedByTP = groupByTargetReferences( ); - Map> unprocessedDefinedTarget = new LinkedHashMap<>(); + Map> unprocessedDefinedTarget = new LinkedHashMap<>(); - for ( Map.Entry> entryByTP : + for ( Map.Entry> entryByTP : groupedByTP.poppedTargetReferences.entrySet() ) { - PropertyEntry targetProperty = entryByTP.getKey(); + String targetProperty = entryByTP.getKey(); //Now we are grouping the already popped mappings by the source parameter(s) of the method GroupedBySourceParameters groupedBySourceParam = groupBySourceParameter( entryByTP.getValue(), @@ -188,7 +205,7 @@ public NestedTargetPropertyMappingHolder build() { .type( sourceEntry.getType() ) .readAccessor( sourceEntry.getReadAccessor() ) .presenceChecker( sourceEntry.getPresenceChecker() ) - .name( targetProperty.getName() ) + .name( targetProperty ) .build(); // If we have multiple source parameters that are mapped to the target reference, or @@ -205,7 +222,7 @@ public NestedTargetPropertyMappingHolder build() { propertyMappings.add( propertyMapping ); } - handledTargets.add( entryByTP.getKey().getName() ); + handledTargets.add( entryByTP.getKey() ); } // For the nonNested mappings (assymetric) Mappings we also forge mappings @@ -216,7 +233,7 @@ public NestedTargetPropertyMappingHolder build() { new MappingReferences( groupedSourceReferences.nonNested, true ); SourceReference reference = new SourceReference.BuilderFromProperty() .sourceParameter( sourceParameter ) - .name( targetProperty.getName() ) + .name( targetProperty ) .build(); boolean forceUpdateMethodForNonNested = @@ -236,7 +253,7 @@ public NestedTargetPropertyMappingHolder build() { propertyMappings.add( propertyMapping ); } - handledTargets.add( entryByTP.getKey().getName() ); + handledTargets.add( entryByTP.getKey() ); } handleSourceParameterMappings( @@ -255,7 +272,7 @@ public NestedTargetPropertyMappingHolder build() { handledTargets, propertyMappings, unprocessedDefinedTarget, - groupedByTP.errorOccurred + errorOccurred ); } @@ -268,7 +285,7 @@ public NestedTargetPropertyMappingHolder build() { * @param forceUpdateMethod whether we need to force an update method */ private void handleSourceParameterMappings(Set sourceParameterMappings, - PropertyEntry targetProperty, Parameter sourceParameter, + String targetProperty, Parameter sourceParameter, boolean forceUpdateMethod) { if ( !sourceParameterMappings.isEmpty() ) { // The source parameter mappings have no mappings, the source name is actually the parameter itself @@ -276,7 +293,7 @@ private void handleSourceParameterMappings(Set sourceParameter new MappingReferences( Collections.emptySet(), false, true ); SourceReference reference = new SourceReference.BuilderFromProperty() .sourceParameter( sourceParameter ) - .name( targetProperty.getName() ) + .name( targetProperty ) .build(); PropertyMapping propertyMapping = createPropertyMappingForNestedTarget( @@ -290,7 +307,7 @@ private void handleSourceParameterMappings(Set sourceParameter propertyMappings.add( propertyMapping ); } - handledTargets.add( targetProperty.getName() ); + handledTargets.add( targetProperty ); } } @@ -340,16 +357,11 @@ private void handleSourceParameterMappings(Set sourceParameter */ private GroupedTargetReferences groupByTargetReferences( ) { // group all mappings based on the top level name before popping - Map> mappingsKeyedByProperty = new LinkedHashMap<>(); - Map> singleTargetReferences = new LinkedHashMap<>(); - boolean errorOccurred = false; + Map> mappingsKeyedByProperty = new LinkedHashMap<>(); + Map> singleTargetReferences = new LinkedHashMap<>(); for ( MappingReference mapping : mappingReferences.getMappingReferences() ) { TargetReference targetReference = mapping.getTargetReference(); - if ( !targetReference.isValid() ) { - errorOccurred = true; - continue; - } - PropertyEntry property = first( targetReference.getPropertyEntries() ); + String property = first( targetReference.getPropertyEntries() ); MappingReference newMapping = mapping.popTargetReference(); if ( newMapping != null ) { // group properties on current name. @@ -362,7 +374,7 @@ private GroupedTargetReferences groupByTargetReferences( ) { } } - return new GroupedTargetReferences( mappingsKeyedByProperty, singleTargetReferences, errorOccurred ); + return new GroupedTargetReferences( mappingsKeyedByProperty, singleTargetReferences ); } /** @@ -625,14 +637,45 @@ private Set extractSingleTargetReferencesToUseAndPopulateSourc } private PropertyMapping createPropertyMappingForNestedTarget(MappingReferences mappingReferences, - PropertyEntry targetProperty, + String targetPropertyName, SourceReference sourceReference, boolean forceUpdateMethod) { + + Accessor targetWriteAccessor = targetPropertiesWriteAccessors.get( targetPropertyName ); + Accessor targetReadAccessor = targetType.getPropertyReadAccessors().get( targetPropertyName ); + if ( targetWriteAccessor == null ) { + Set readAccessors = targetType.getPropertyReadAccessors().keySet(); + String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); + + for ( MappingReference mappingReference : mappingReferences.getMappingReferences() ) { + MappingOptions mapping = mappingReference.getMapping(); + List pathProperties = new ArrayList<>( mappingReference.getTargetReference() + .getPathProperties() ); + if ( !pathProperties.isEmpty() ) { + pathProperties.set( pathProperties.size() - 1, mostSimilarProperty ); + } + + mappingContext.getMessager() + .printMessage( + mapping.getElement(), + mapping.getMirror(), + mapping.getTargetAnnotationValue(), + Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE, + targetPropertyName, + targetType, + mapping.getTargetName(), + Strings.join( pathProperties, "." ) + ); + } + + errorOccurred = true; + return null; + } + return new PropertyMapping.PropertyMappingBuilder() .mappingContext( mappingContext ) .sourceMethod( method ) - .targetProperty( targetProperty ) - .targetPropertyName( targetProperty.getName() ) + .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) .sourceReference( sourceReference ) .existingVariableNames( existingVariableNames ) .dependsOn( mappingReferences.collectNestedDependsOn() ) @@ -685,21 +728,19 @@ private GroupedBySourceParameters(Map> groupedB * references (target references that were not nested). */ private static class GroupedTargetReferences { - private final Map> poppedTargetReferences; - private final Map> singleTargetReferences; - private final boolean errorOccurred; + private final Map> poppedTargetReferences; + private final Map> singleTargetReferences; - private GroupedTargetReferences(Map> poppedTargetReferences, - Map> singleTargetReferences, boolean errorOccurred) { + private GroupedTargetReferences(Map> poppedTargetReferences, + Map> singleTargetReferences) { this.poppedTargetReferences = poppedTargetReferences; this.singleTargetReferences = singleTargetReferences; - this.errorOccurred = errorOccurred; } @Override public String toString() { return "GroupedTargetReferences{" + "poppedTargetReferences=" + poppedTargetReferences - + ", singleTargetReferences=" + singleTargetReferences + ", errorOccurred=" + errorOccurred + '}'; + + ", singleTargetReferences=" + singleTargetReferences + "}"; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 06de2aa397..24facce722 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -94,21 +94,9 @@ public T sourceMethod(Method sourceMethod) { return super.method( sourceMethod ); } - public T targetProperty(PropertyEntry targetProp) { - this.targetReadAccessor = targetProp.getReadAccessor(); - this.targetWriteAccessor = targetProp.getWriteAccessor(); - this.targetType = targetProp.getType(); - this.targetBuilderType = targetProp.getBuilderType(); - this.targetWriteAccessorType = targetWriteAccessor.getAccessorType(); - return (T) this; - } - - public T targetReadAccessor(Accessor targetReadAccessor) { + public T target(String targetPropertyName, Accessor targetReadAccessor, Accessor targetWriteAccessor) { + this.targetPropertyName = targetPropertyName; this.targetReadAccessor = targetReadAccessor; - return (T) this; - } - - public T targetWriteAccessor(Accessor targetWriteAccessor) { this.targetWriteAccessor = targetWriteAccessor; this.targetType = ctx.getTypeFactory().getType( targetWriteAccessor.getAccessedType() ); BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); @@ -122,11 +110,6 @@ T mirror(AnnotationMirror mirror) { return (T) this; } - public T targetPropertyName(String targetPropertyName) { - this.targetPropertyName = targetPropertyName; - return (T) this; - } - public T sourcePropertyName(String sourcePropertyName) { this.sourcePropertyName = sourcePropertyName; return (T) this; @@ -326,6 +309,11 @@ else if ( ( sourceType.isIterableType() && targetType.isStreamType() ) * Report that a mapping could not be created. */ private void reportCannotCreateMapping() { + if ( forgeMethodWithMappingReferences != null && ctx.isErroneous() ) { + // If we arrived here, there is an error it means that we couldn't forge a mapping method + // so skip the cannot create mapping + return; + } if ( method instanceof ForgedMethod && ( (ForgedMethod) method ).getHistory() != null ) { // The history that is part of the ForgedMethod misses the information from the current right hand // side. Therefore we need to extract the most relevant history and use that in the error reporting. @@ -363,9 +351,7 @@ private Assignment getDefaultValueAssignment( Assignment rhs ) { .existingVariableNames( existingVariableNames ) .mappingContext( ctx ) .sourceMethod( method ) - .targetPropertyName( targetPropertyName ) - .targetReadAccessor( targetReadAccessor ) - .targetWriteAccessor( targetWriteAccessor ) + .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) .build(); return build.getAssignment(); } @@ -378,9 +364,7 @@ private Assignment getDefaultValueAssignment( Assignment rhs ) { .existingVariableNames( existingVariableNames ) .mappingContext( ctx ) .sourceMethod( method ) - .targetPropertyName( targetPropertyName ) - .targetReadAccessor( targetReadAccessor ) - .targetWriteAccessor( targetWriteAccessor ) + .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) .build(); return build.getAssignment(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java index 2675e29007..e2501ea952 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java @@ -80,11 +80,7 @@ public int hashCode() { } public boolean isValid( ) { - boolean result = false; - if ( targetReference.isValid() ) { - result = sourceReference != null ? sourceReference.isValid() : true; - } - return result; + return sourceReference == null || sourceReference.isValid(); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java index 63af65f2e2..06f0ed06c9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.SourceMethod; @@ -29,8 +30,11 @@ public static MappingReferences empty() { return EMPTY; } - public static MappingReferences forSourceMethod(SourceMethod sourceMethod, FormattingMessager messager, - TypeFactory typeFactory) { + public static MappingReferences forSourceMethod(SourceMethod sourceMethod, + Type targetType, + Set targetProperties, + FormattingMessager messager, + TypeFactory typeFactory) { Set references = new LinkedHashSet<>(); List targetThisReferences = new ArrayList<>( ); @@ -49,6 +53,8 @@ public static MappingReferences forSourceMethod(SourceMethod sourceMethod, Forma .method( sourceMethod ) .messager( messager ) .typeFactory( typeFactory ) + .targetProperties( targetProperties ) + .targetType( targetType ) .build(); // add when inverse is also valid @@ -121,7 +127,7 @@ public boolean hasNestedTargetReferences() { for ( MappingReference mappingRef : mappingReferences ) { TargetReference targetReference = mappingRef.getTargetReference(); - if ( targetReference.isValid() && targetReference.isNested()) { + if ( targetReference.isNested()) { return true; } @@ -141,7 +147,7 @@ public List getTargetThisReferences() { private static boolean isValidWhenInversed(MappingReference mappingRef) { MappingOptions mapping = mappingRef.getMapping(); if ( mapping.getInheritContext() != null && mapping.getInheritContext().isReversed() ) { - return mappingRef.getTargetReference().isValid() && ( mappingRef.getSourceReference() == null || + return ( mappingRef.getSourceReference() == null || mappingRef.getSourceReference().isValid() ); } return true; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java index f2d087cd93..8ac5e46671 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java @@ -7,55 +7,33 @@ import java.util.Arrays; -import org.mapstruct.ap.internal.model.common.BuilderType; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; /** - * A PropertyEntry contains information on the name, readAccessor (for source), readAccessor and writeAccessor - * (for targets) and return type of a property. + * A PropertyEntry contains information on the name, readAccessor and presenceCheck (for source) + * and return type of a property. */ public class PropertyEntry { private final String[] fullName; private final Accessor readAccessor; - private final Accessor writeAccessor; private final Accessor presenceChecker; private final Type type; - private final BuilderType builderType; /** * Constructor used to create {@link TargetReference} property entries from a mapping * * @param fullName * @param readAccessor - * @param writeAccessor * @param type */ - private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor writeAccessor, - Accessor presenceChecker, Type type, BuilderType builderType) { + private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor presenceChecker, Type type) { this.fullName = fullName; this.readAccessor = readAccessor; - this.writeAccessor = writeAccessor; this.presenceChecker = presenceChecker; this.type = type; - this.builderType = builderType; - } - - /** - * Constructor used to create {@link TargetReference} property entries - * - * @param fullName name of the property (dot separated) - * @param readAccessor its read accessor - * @param writeAccessor its write accessor - * @param type type of the property - * @param builderType the builder for the type - * @return the property entry for given parameters. - */ - public static PropertyEntry forTargetReference(String[] fullName, Accessor readAccessor, - Accessor writeAccessor, Type type, BuilderType builderType ) { - return new PropertyEntry( fullName, readAccessor, writeAccessor, null, type, builderType ); } /** @@ -69,7 +47,7 @@ public static PropertyEntry forTargetReference(String[] fullName, Accessor readA */ public static PropertyEntry forSourceReference(String[] name, Accessor readAccessor, Accessor presenceChecker, Type type) { - return new PropertyEntry( name, readAccessor, null, presenceChecker, type, null ); + return new PropertyEntry( name, readAccessor, presenceChecker, type ); } public String getName() { @@ -80,10 +58,6 @@ public Accessor getReadAccessor() { return readAccessor; } - public Accessor getWriteAccessor() { - return writeAccessor; - } - public Accessor getPresenceChecker() { return presenceChecker; } @@ -92,24 +66,10 @@ public Type getType() { return type; } - public BuilderType getBuilderType() { - return builderType; - } - public String getFullName() { return Strings.join( Arrays.asList( fullName ), "." ); } - public PropertyEntry pop() { - if ( fullName.length > 1 ) { - String[] newFullName = Arrays.copyOfRange( fullName, 1, fullName.length ); - return new PropertyEntry(newFullName, readAccessor, writeAccessor, presenceChecker, type, builderType ); - } - else { - return null; - } - } - @Override public int hashCode() { int hash = 7; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java index 8a2a398232..fee97f840d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java @@ -7,26 +7,21 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; -import javax.lang.model.type.DeclaredType; -import org.mapstruct.ap.internal.model.common.BuilderType; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; -import org.mapstruct.ap.internal.gem.BuilderGem; -import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; -import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.AccessorType; import static org.mapstruct.ap.internal.util.Collections.first; @@ -48,11 +43,77 @@ *

    • {@code propertyEntries[1]} will describe {@code propB}
    • *
    * - * After building, {@link #isValid()} will return true when when no problems are detected during building. - * * @author Sjaak Derksen */ -public class TargetReference extends AbstractReference { +public class TargetReference { + + private final List pathProperties; + private final Parameter parameter; + private final List propertyEntries; + + public TargetReference(Parameter parameter, List propertyEntries) { + this( parameter, propertyEntries, Collections.emptyList() ); + } + + public TargetReference(Parameter parameter, List propertyEntries, List pathProperties) { + this.pathProperties = pathProperties; + this.parameter = parameter; + this.propertyEntries = propertyEntries; + } + + public List getPathProperties() { + return pathProperties; + } + + public List getPropertyEntries() { + return propertyEntries; + } + + public List getElementNames() { + List elementNames = new ArrayList<>(); + if ( parameter != null ) { + // only relevant for source properties + elementNames.add( parameter.getName() ); + } + elementNames.addAll( propertyEntries ); + return elementNames; + } + + /** + * returns the property name on the shallowest nesting level + */ + public String getShallowestPropertyName() { + if ( propertyEntries.isEmpty() ) { + return null; + } + return first( propertyEntries ); + } + + public boolean isNested() { + return propertyEntries.size() > 1; + } + + @Override + public String toString() { + + String result = ""; + if ( propertyEntries.isEmpty() ) { + if ( parameter != null ) { + result = String.format( "parameter \"%s %s\"", parameter.getType(), parameter.getName() ); + } + } + else if ( propertyEntries.size() == 1 ) { + String propertyEntry = propertyEntries.get( 0 ); + result = String.format( "property \"%s\"", propertyEntry ); + } + else { + result = String.format( + "property \"%s\"", + Strings.join( getElementNames(), "." ) + ); + } + return result; + } /** * Builds a {@link TargetReference} from an {@code @Mappping}. @@ -62,21 +123,15 @@ public static class Builder { private Method method; private FormattingMessager messager; private TypeFactory typeFactory; + private Set targetProperties; + private Type targetType; // mapping parameters - private boolean isReversed = false; - private boolean isIgnored = false; private String targetName = null; + private MappingOptions mapping; private AnnotationMirror annotationMirror = null; private AnnotationValue targetAnnotationValue = null; private AnnotationValue sourceAnnotationValue = null; - private Method templateMethod = null; - /** - * During {@link #getTargetEntries(Type, String[])} an error can occur. However, we are invoking - * that multiple times because the first entry can also be the name of the parameter. Therefore we keep - * the error message until the end when we can report it. - */ - private MappingErrorMessage errorMessage; public Builder messager(FormattingMessager messager) { this.messager = messager; @@ -84,11 +139,7 @@ public Builder messager(FormattingMessager messager) { } public Builder mapping(MappingOptions mapping) { - if ( mapping.getInheritContext() != null ) { - this.isReversed = mapping.getInheritContext().isReversed(); - this.templateMethod = mapping.getInheritContext().getTemplateMethod(); - } - this.isIgnored = mapping.isIgnored(); + this.mapping = mapping; this.targetName = mapping.getTargetName(); this.annotationMirror = mapping.getMirror(); this.targetAnnotationValue = mapping.getTargetAnnotationValue(); @@ -106,11 +157,22 @@ public Builder method(Method method) { return this; } + public Builder targetProperties(Set targetProperties) { + this.targetProperties = targetProperties; + return this; + } + + public Builder targetType(Type targetType) { + this.targetType = targetType; + return this; + } + public TargetReference build() { Objects.requireNonNull( method ); Objects.requireNonNull( typeFactory ); Objects.requireNonNull( messager ); + Objects.requireNonNull( targetType ); if ( targetName == null ) { return null; @@ -130,185 +192,27 @@ public TargetReference build() { String[] segments = targetNameTrimmed.split( "\\." ); Parameter parameter = method.getMappingTargetParameter(); - boolean foundEntryMatch; - Type resultType = typeBasedOnMethod( method.getResultType() ); - // there can be 4 situations // 1. Return type // 2. An inverse target reference where the source parameter name is used in the original mapping // 3. @MappingTarget, with // 4. or without parameter name. String[] targetPropertyNames = segments; - List entries = getTargetEntries( resultType, targetPropertyNames ); - foundEntryMatch = (entries.size() == targetPropertyNames.length); - if ( !foundEntryMatch && segments.length > 1 - && matchesSourceOrTargetParameter( segments[0], parameter, isReversed ) ) { - targetPropertyNames = Arrays.copyOfRange( segments, 1, segments.length ); - entries = getTargetEntries( resultType, targetPropertyNames ); - foundEntryMatch = (entries.size() == targetPropertyNames.length); - } - - if ( !foundEntryMatch && errorMessage != null && !isReversed ) { - // This is called only for reporting errors - errorMessage.report( ); - } - - // foundEntryMatch = isValid, errors are handled here, and the BeanMapping uses that to ignore - // the creation of mapping for invalid TargetReferences - return new TargetReference( parameter, entries, foundEntryMatch ); - } - - private List getTargetEntries(Type type, String[] entryNames) { - - // initialize - CollectionMappingStrategyGem cms = method.getOptions().getMapper().getCollectionMappingStrategy(); - List targetEntries = new ArrayList<>(); - Type nextType = type; - - // iterate, establish for each entry the target write accessors. Other than setter is only allowed for - // last entry - for ( int i = 0; i < entryNames.length; i++ ) { - - Type mappingType = typeBasedOnMethod( nextType ); - Accessor targetReadAccessor = mappingType.getPropertyReadAccessors().get( entryNames[i] ); - Accessor targetWriteAccessor = mappingType.getPropertyWriteAccessors( cms ).get( entryNames[i] ); - boolean isLast = i == entryNames.length - 1; - boolean isNotLast = i < entryNames.length - 1; - if ( isWriteAccessorNotValidWhenNotLast( targetWriteAccessor, isNotLast ) - || isWriteAccessorNotValidWhenLast( targetWriteAccessor, targetReadAccessor, isIgnored, isLast ) ) { - // there should always be a write accessor (except for the last when the mapping is ignored and - // there is a read accessor) and there should be read accessor mandatory for all but the last - setErrorMessage( targetWriteAccessor, targetReadAccessor, entryNames, i, nextType ); - break; - } - - if ( isLast || ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER || - targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) ) { - // only intermediate nested properties when they are a true setter or field accessor - // the last may be other readAccessor (setter / getter / adder). - - nextType = findNextType( nextType, targetWriteAccessor, targetReadAccessor ); - - // check if an entry alread exists, otherwise create - String[] fullName = Arrays.copyOfRange( entryNames, 0, i + 1 ); - BuilderType builderType; - PropertyEntry propertyEntry = null; - if ( method.isUpdateMethod() ) { - propertyEntry = PropertyEntry.forTargetReference( fullName, - targetReadAccessor, - targetWriteAccessor, - nextType, - null - ); - } - else { - BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); - builderType = typeFactory.builderTypeFor( nextType, builder ); - propertyEntry = PropertyEntry.forTargetReference( fullName, - targetReadAccessor, - targetWriteAccessor, - nextType, - builderType - ); - + if ( segments.length > 1 ) { + String firstTargetProperty = targetPropertyNames[0]; + // If the first target property is not within the defined target properties, then check if it matches + // the source or target parameter + if ( !targetProperties.contains( firstTargetProperty ) ) { + if ( matchesSourceOrTargetParameter( firstTargetProperty, parameter ) ) { + targetPropertyNames = Arrays.copyOfRange( segments, 1, segments.length ); } - targetEntries.add( propertyEntry ); } - } - return targetEntries; - } - /** - * Finds the next type based on the initial type. - * - * @param initial for which a next type should be found - * @param targetWriteAccessor the write accessor - * @param targetReadAccessor the read accessor - * @return the next type that should be used for finding a property entry - */ - private Type findNextType(Type initial, Accessor targetWriteAccessor, Accessor targetReadAccessor) { - Type nextType; - Accessor toUse = targetWriteAccessor != null ? targetWriteAccessor : targetReadAccessor; - if ( toUse.getAccessorType() == AccessorType.GETTER || toUse.getAccessorType() == AccessorType.FIELD ) { - nextType = typeFactory.getReturnType( - (DeclaredType) typeBasedOnMethod( initial ).getTypeMirror(), - toUse - ); - } - else { - nextType = typeFactory.getSingleParameter( - (DeclaredType) typeBasedOnMethod( initial ).getTypeMirror(), - toUse - ).getType(); - } - return nextType; - } - - private void setErrorMessage(Accessor targetWriteAccessor, Accessor targetReadAccessor, String[] entryNames, - int index, Type nextType) { - if ( targetWriteAccessor == null && targetReadAccessor == null ) { - errorMessage = new NoPropertyErrorMessage( this, entryNames, index, nextType ); - } - else if ( targetWriteAccessor == null ) { - errorMessage = new NoWriteAccessorErrorMessage(this ); - } - else { - //TODO there is no read accessor. What should we do here? - errorMessage = new NoPropertyErrorMessage( this, entryNames, index, nextType ); - } - } - - /** - * When we are in an update method, i.e. source parameter with {@code @MappingTarget} then the type should - * be itself, otherwise, we always get the effective type. The reason is that when doing updates we always - * search for setters and getters within the updating type. - */ - private Type typeBasedOnMethod(Type type) { - if ( method.isUpdateMethod() ) { - return type; - } - else { - BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); - return typeFactory.effectiveResultTypeFor( type, builder ); - } - } + List entries = new ArrayList<>( Arrays.asList( targetPropertyNames ) ); - /** - * A write accessor is not valid if it is {@code null} and it is not last. i.e. for nested target mappings - * there must be a write accessor for all entries except the last one. - * - * @param accessor that needs to be checked - * @param isNotLast whether or not this is the last write accessor in the entry chain - * - * @return {@code true} if the accessor is not valid, {@code false} otherwise - */ - private static boolean isWriteAccessorNotValidWhenNotLast(Accessor accessor, boolean isNotLast) { - return accessor == null && isNotLast; - } - - /** - * For a last accessor to be valid, a read accessor should exist and the mapping should be ignored. All other - * cases represent an invalid write accessor. This method will evaluate to {@code true} if the following is - * {@code true}: - *
      - *
    • {@code writeAccessor} is {@code null}
    • - *
    • It is for the last entry
    • - *
    • A read accessor does not exist, or the mapping is not ignored
    • - *
    - * - * @param writeAccessor that needs to be checked - * @param readAccessor that is used - * @param isIgnored true when ignored - * @param isLast whether or not this is the last write accessor in the entry chain - * - * @return {@code true} if the write accessor is not valid, {@code false} otherwise. See description for more - * information - */ - private static boolean isWriteAccessorNotValidWhenLast(Accessor writeAccessor, Accessor readAccessor, - boolean isIgnored, boolean isLast) { - return writeAccessor == null && isLast && ( readAccessor == null || !isIgnored ); + return new TargetReference( parameter, entries ); } /** @@ -317,16 +221,14 @@ private static boolean isWriteAccessorNotValidWhenLast(Accessor writeAccessor, A * * @param segment that needs to be checked * @param targetParameter the target parameter if it exists - - * @param isInverse whether a inverse {@link TargetReference} is being built * * @return {@code true} if the segment matches the name of the {@code targetParameter} or the name of the * {@code inverseSourceParameter} when this is a inverse {@link TargetReference} is being built, {@code * false} otherwise */ - private boolean matchesSourceOrTargetParameter(String segment, Parameter targetParameter, boolean isInverse) { + private boolean matchesSourceOrTargetParameter(String segment, Parameter targetParameter) { boolean matchesTargetParameter = targetParameter != null && targetParameter.getName().equals( segment ); - return matchesTargetParameter || matchesSourceOnInverseSourceParameter( segment, isInverse ); + return matchesTargetParameter || matchesSourceOnInverseSourceParameter( segment ); } /** @@ -344,13 +246,16 @@ private boolean matchesSourceOrTargetParameter(String segment, Parameter targetP * a inverse is created. * * @param segment that needs to be checked* - * @param isInverse whether a inverse {@link TargetReference} is being built * * @return on match when inverse and segment matches the one and only source parameter */ - private boolean matchesSourceOnInverseSourceParameter( String segment, boolean isInverse ) { + private boolean matchesSourceOnInverseSourceParameter(String segment) { + boolean result = false; - if ( isInverse ) { + MappingOptions.InheritContext inheritContext = mapping.getInheritContext(); + if ( inheritContext != null && inheritContext.isReversed() ) { + + Method templateMethod = inheritContext.getTemplateMethod(); // there is only source parameter by definition when applying @InheritInverseConfiguration Parameter inverseSourceParameter = first( templateMethod.getSourceParameters() ); result = inverseSourceParameter.getName().equals( segment ); @@ -359,87 +264,19 @@ private boolean matchesSourceOnInverseSourceParameter( String segment, boolean i } } - private TargetReference(Parameter sourceParameter, List targetPropertyEntries, boolean isValid) { - super( sourceParameter, targetPropertyEntries, isValid ); - } - public TargetReference pop() { if ( getPropertyEntries().size() > 1 ) { - List newPropertyEntries = new ArrayList<>( getPropertyEntries().size() - 1 ); - for ( PropertyEntry propertyEntry : getPropertyEntries() ) { - PropertyEntry newPropertyEntry = propertyEntry.pop(); - if ( newPropertyEntry != null ) { - newPropertyEntries.add( newPropertyEntry ); - } - } - return new TargetReference( null, newPropertyEntries, isValid() ); + List newPathProperties = new ArrayList<>( this.pathProperties ); + newPathProperties.add( getPropertyEntries().get( 0 ) ); + return new TargetReference( + null, + getPropertyEntries().subList( 1, getPropertyEntries().size() ), + newPathProperties + ); } else { return null; } } - private abstract static class MappingErrorMessage { - private final Builder builder; - - private MappingErrorMessage(Builder builder) { - this.builder = builder; - } - - abstract void report(); - - protected void printErrorMessage(Message message, Object... args) { - Object[] errorArgs = new Object[args.length + 2]; - errorArgs[0] = builder.targetName; - errorArgs[1] = builder.method.getResultType(); - System.arraycopy( args, 0, errorArgs, 2, args.length ); - AnnotationMirror annotationMirror = builder.annotationMirror; - builder.messager.printMessage( builder.method.getExecutable(), - annotationMirror, - builder.sourceAnnotationValue, - message, - errorArgs - ); - } - } - - private static class NoWriteAccessorErrorMessage extends MappingErrorMessage { - - private NoWriteAccessorErrorMessage(Builder builder) { - super( builder ); - } - - @Override - public void report() { - printErrorMessage( Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE ); - } - } - - private static class NoPropertyErrorMessage extends MappingErrorMessage { - - private final String[] entryNames; - private final int index; - private final Type nextType; - - private NoPropertyErrorMessage(Builder builder, String[] entryNames, int index, - Type nextType) { - super( builder ); - this.entryNames = entryNames; - this.index = index; - this.nextType = nextType; - } - - @Override - public void report() { - - Set readAccessors = nextType.getPropertyReadAccessors().keySet(); - String mostSimilarProperty = Strings.getMostSimilarWord( entryNames[index], readAccessors ); - - List elements = new ArrayList<>( Arrays.asList( entryNames ).subList( 0, index ) ); - elements.add( mostSimilarProperty ); - - printErrorMessage( Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE, Strings.join( elements, "." ) ); - } - } - } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index ed88e5107a..b826295f75 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -16,6 +16,7 @@ import java.util.stream.Collectors; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; @@ -50,6 +51,7 @@ public class MappingOptions extends DelegatingOptions { private final boolean isIgnored; private final Set dependsOn; + private final Element element; private final AnnotationValue sourceAnnotationValue; private final AnnotationValue targetAnnotationValue; private final MappingGem mapping; @@ -135,6 +137,7 @@ public static void addInstance(MappingGem mapping, ExecutableElement method, MappingOptions options = new MappingOptions( mapping.target().getValue(), + method, mapping.target().getAnnotationValue(), source, mapping.source().getAnnotationValue(), @@ -169,6 +172,7 @@ public static MappingOptions forIgnore(String targetName) { null, null, null, + null, true, null, null, @@ -249,6 +253,7 @@ else if ( gem.nullValuePropertyMappingStrategy().hasValue() @SuppressWarnings("checkstyle:parameternumber") private MappingOptions(String targetName, + Element element, AnnotationValue targetAnnotationValue, String sourceName, AnnotationValue sourceAnnotationValue, @@ -266,6 +271,7 @@ private MappingOptions(String targetName, ) { super( next ); this.targetName = targetName; + this.element = element; this.targetAnnotationValue = targetAnnotationValue; this.sourceName = sourceName; this.sourceAnnotationValue = sourceAnnotationValue; @@ -377,6 +383,10 @@ public AnnotationMirror getMirror() { return Optional.ofNullable( mapping ).map( MappingGem::mirror ).orElse( null ); } + public Element getElement() { + return element; + } + public AnnotationValue getDependsOnAnnotationValue() { return Optional.ofNullable( mapping ) .map( MappingGem::dependsOn ) @@ -434,6 +444,7 @@ public MappingOptions copyForInverseInheritance(SourceMethod templateMethod, MappingOptions mappingOptions = new MappingOptions( sourceName != null ? sourceName : targetName, + templateMethod.getExecutable(), targetAnnotationValue, sourceName != null ? targetName : null, sourceAnnotationValue, @@ -462,6 +473,7 @@ public MappingOptions copyForForwardInheritance(SourceMethod templateMethod, BeanMappingOptions beanMappingOptions ) { MappingOptions mappingOptions = new MappingOptions( targetName, + templateMethod.getExecutable(), targetAnnotationValue, sourceName, sourceAnnotationValue, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java index bc7374d391..a9f9eff4a4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java @@ -167,6 +167,7 @@ public void note( int level, Message msg, Object... args ) { } } + @Override public boolean isErroneous() { return isErroneous; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 197d9fa63c..7746dd6778 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -368,10 +368,10 @@ else if ( method.isStreamMapping() ) { .returnTypeBuilder( typeFactory.builderTypeFor( method.getReturnType(), builder ) ) .build(); + // We can consider that the bean mapping method can always be constructed. If there is a problem + // it would have been reported in its build + hasFactoryMethod = true; if ( beanMappingMethod != null ) { - // We can consider that the bean mapping method can always be constructed. If there is a problem - // it would have been reported in its build - hasFactoryMethod = true; mappingMethods.add( beanMappingMethod ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/FormattingMessager.java b/processor/src/main/java/org/mapstruct/ap/internal/util/FormattingMessager.java index 2e4d287d4b..6f3b1dc7a9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/FormattingMessager.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/FormattingMessager.java @@ -75,4 +75,6 @@ void printMessage(Element e, * @param args the arguments */ void note(int level, Message log, Object... args); + + boolean isErroneous(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index df47f5bba8..e4b199adfb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -23,7 +23,9 @@ public enum Message { BEANMAPPING_NOT_ASSIGNABLE( "%s not assignable to: %s." ), BEANMAPPING_ABSTRACT( "The result type %s may not be an abstract class nor interface." ), BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE( "Unknown property \"%s\" in result type %s. Did you mean \"%s\"?" ), + BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE( "Unknown property \"%s\" in type %s for target name \"%s\". Did you mean \"%s\"?" ), BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE( "Property \"%s\" has no write accessor in %s." ), + BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_TYPE( "Property \"%s\" has no write accessor in %s for target name \"%s\"." ), BEANMAPPING_SEVERAL_POSSIBLE_SOURCES( "Several possible source properties for target property \"%s\"." ), BEANMAPPING_SEVERAL_POSSIBLE_TARGET_ACCESSORS( "Found several matching getters for property \"%s\"." ), BEANMAPPING_UNMAPPED_TARGETS_WARNING( "Unmapped target %s.", Diagnostic.Kind.WARNING ), diff --git a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java index bc90a7c3c3..2b65030625 100755 --- a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java @@ -161,5 +161,9 @@ public Diagnostic.Kind getLastKindPrinted() { return lastKindPrinted; } + @Override + public boolean isErroneous() { + return false; + } } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java index f4142fe641..bc3e59922f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java @@ -32,14 +32,14 @@ public class Issue1153Test { @Diagnostic(type = ErroneousIssue1153Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - message = "Property \"nestedTarget.readOnly\" has no write accessor in " + - "org.mapstruct.ap.test.bugs._1153.ErroneousIssue1153Mapper.Target."), + message = "Property \"readOnly\" has no write accessor in org.mapstruct.ap.test.bugs._1153" + + ".ErroneousIssue1153Mapper.Target.NestedTarget for target name \"nestedTarget.readOnly\"."), @Diagnostic(type = ErroneousIssue1153Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 23, - message = "Unknown property \"nestedTarget2.writable2\" in result type " + - "org.mapstruct.ap.test.bugs._1153.ErroneousIssue1153Mapper.Target. " + - "Did you mean \"nestedTarget2.writable\"?") + message = "Unknown property \"writable2\" in type org.mapstruct.ap.test.bugs._1153" + + ".ErroneousIssue1153Mapper.Target.NestedTarget for target name \"nestedTarget2.writable2\". Did " + + "you mean \"nestedTarget2.writable\"?") }) @Test public void shouldReportErrorsCorrectly() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java index 9908ea8e4a..94b2fa16a4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java @@ -66,13 +66,21 @@ public void testNameSuggestion() { @Diagnostic(type = PersonGarageWrongTargetMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - message = "Unknown property \"garage.colour.rgb\" in result type org.mapstruct.ap.test.namesuggestion" + - ".Person. Did you mean \"garage.color\"?"), + message = "Unknown property \"colour\" in type org.mapstruct.ap.test.namesuggestion.Garage for target" + + " name \"garage.colour.rgb\". Did you mean \"garage.color\"?"), + @Diagnostic(type = PersonGarageWrongTargetMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 20, + messageRegExp = "Unmapped target properties: \"fullName, fullAge\"\\."), @Diagnostic(type = PersonGarageWrongTargetMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - message = "Unknown property \"garage.colour\" in result type org.mapstruct.ap.test.namesuggestion" + - ".Person. Did you mean \"garage.color\"?") + message = "Unknown property \"colour\" in type org.mapstruct.ap.test.namesuggestion.Garage for" + + " target name \"garage.colour\". Did you mean \"garage.color\"?"), + @Diagnostic(type = PersonGarageWrongTargetMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 23, + messageRegExp = "Unmapped target properties: \"fullName, fullAge\"\\."), } ) public void testGarageTargetSuggestion() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java index 12ba5f111c..b508787cc7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java @@ -172,10 +172,7 @@ public void shouldFailOnSuperBounds2() { }) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = ErroneousSourceTargetMapper6.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 16, message = "org.mapstruct.ap.test.selection.generics.WildCardSuperWrapper does not have an accessible parameterless constructor."), - @Diagnostic(type = ErroneousSourceTargetMapper6.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 16, message = + line = 17, message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.NoProperties " + "foo.wrapped\" to" + " \"org.mapstruct.ap.test.selection.generics.TypeA " diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ErroneousSourceTargetMapper6.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ErroneousSourceTargetMapper6.java index 731b743b82..210cc3692a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ErroneousSourceTargetMapper6.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ErroneousSourceTargetMapper6.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.selection.generics; import org.mapstruct.Mapper; +import org.mapstruct.ObjectFactory; import org.mapstruct.factory.Mappers; @Mapper( uses = GenericTypeMapper.class ) @@ -14,4 +15,11 @@ public interface ErroneousSourceTargetMapper6 { ErroneousSourceTargetMapper6 INSTANCE = Mappers.getMapper( ErroneousSourceTargetMapper6.class ); ErroneousTarget6 sourceToTarget(ErroneousSource6 source); + + // We are testing that we can't create the nested + // not whether or not we can instantiate the WildCardSuperWrapper + @ObjectFactory + default WildCardSuperWrapper createWildCardSuperWrapper() { + return new WildCardSuperWrapper<>( null ); + } } From 3bffe9698333d5bdd364f28fe3c805f5664554f9 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 4 Apr 2020 20:19:43 +0200 Subject: [PATCH 046/597] #1159: Do not fail loading AnnotationProcessorContext if loading AstModifyingAnnotationProcessor fails --- .../itest/tests/MavenIntegrationTest.java | 7 ++ .../generator/pom.xml | 50 ++++++++++++ .../AstModifyingProcessorSaysYes.java | 21 +++++ .../FaultyAstModifyingProcessor.java | 25 ++++++ .../FaultyStaticAstModifyingProcessor.java | 27 +++++++ ...uct.ap.spi.AstModifyingAnnotationProcessor | 7 ++ .../pom.xml | 27 +++++++ .../usage/pom.xml | 52 +++++++++++++ .../usage/Order.java | 22 ++++++ .../usage/OrderDto.java | 22 ++++++ .../usage/OrderMapper.java | 20 +++++ .../usage/FaultyAstModifyingTestTest.java | 27 +++++++ .../util/AnnotationProcessorContext.java | 77 ++++++++++++++++++- ...FaultyAstModifyingAnnotationProcessor.java | 25 ++++++ .../ap/test/bugs/_1159/Issue1159Mapper.java | 52 +++++++++++++ .../ap/test/bugs/_1159/Issue1159Test.java | 60 +++++++++++++++ 16 files changed, 517 insertions(+), 4 deletions(-) create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/AstModifyingProcessorSaysYes.java create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyAstModifyingProcessor.java create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyStaticAstModifyingProcessor.java create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/resources/META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/pom.xml create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/Order.java create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderDto.java create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderMapper.java create mode 100644 integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/FaultyAstModifyingAnnotationProcessor.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 13cd774754..0789e3051d 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -156,4 +156,11 @@ void usesTypeGenerationTest() { void usesTypeGenerationTestEclipse() { } + /** + * Tests usage of MapStruct with faulty provider of AstModifyingAnnotationProcessor. + */ + @ProcessorTest(baseDir = "faultyAstModifyingAnnotationProcessorTest") + void faultyAstModifyingProcessor() { + } + } diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml new file mode 100644 index 0000000000..08390085dd --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + + org.mapstruct.itest + itest-faultyAstModifyingProcessor-aggregator + 1.0.0 + ../pom.xml + + + itest-faultyAstModifyingProcessor-generator + jar + + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + junit + junit + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -proc:none + + + + + + + diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/AstModifyingProcessorSaysYes.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/AstModifyingProcessorSaysYes.java new file mode 100644 index 0000000000..e4a5fe5ead --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/AstModifyingProcessorSaysYes.java @@ -0,0 +1,21 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor; + +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; + +/** + * @author Filip Hrisafov + */ +public class AstModifyingProcessorSaysYes implements AstModifyingAnnotationProcessor { + + @Override + public boolean isTypeComplete(TypeMirror type) { + return true; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyAstModifyingProcessor.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyAstModifyingProcessor.java new file mode 100644 index 0000000000..ba1be9b2cc --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyAstModifyingProcessor.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.itest.faultyAstModifyingProcessor; + +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; + +/** + * @author Filip Hrisafov + */ +public class FaultyAstModifyingProcessor implements AstModifyingAnnotationProcessor { + + public FaultyAstModifyingProcessor() { + throw new RuntimeException( "Failed to create processor" ); + } + + @Override + public boolean isTypeComplete(TypeMirror type) { + return true; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyStaticAstModifyingProcessor.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyStaticAstModifyingProcessor.java new file mode 100644 index 0000000000..5f16165014 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/FaultyStaticAstModifyingProcessor.java @@ -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.itest.faultyAstModifyingProcessor; + +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; + +/** + * @author Filip Hrisafov + */ +public class FaultyStaticAstModifyingProcessor implements AstModifyingAnnotationProcessor { + + static { + if ( true ) { + throw new RuntimeException( "Failed to initialize class" ); + } + } + + @Override + public boolean isTypeComplete(TypeMirror type) { + return true; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/resources/META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/resources/META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor new file mode 100644 index 0000000000..3bc4f6138c --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/src/main/resources/META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor @@ -0,0 +1,7 @@ +# Copyright MapStruct Authors. +# +# Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 +org.mapstruct.itest.faultyAstModifyingProcessor.UnknownAstModifyingProcessor +org.mapstruct.itest.faultyAstModifyingProcessor.AstModifyingProcessorSaysYes +org.mapstruct.itest.faultyAstModifyingProcessor.FaultyAstModifyingProcessor +org.mapstruct.itest.faultyAstModifyingProcessor.FaultyStaticAstModifyingProcessor diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/pom.xml new file mode 100644 index 0000000000..dae561252e --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/pom.xml @@ -0,0 +1,27 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + org.mapstruct.itest + itest-faultyAstModifyingProcessor-aggregator + pom + + + generator + usage + + diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml new file mode 100644 index 0000000000..cc0d0e4f98 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + + org.mapstruct.itest + itest-faultyAstModifyingProcessor-aggregator + 1.0.0 + ../pom.xml + + + itest-faultyAstModifyingProcessor-usage + jar + + + + junit + junit + 4.12 + test + + + org.mapstruct.itest + itest-faultyAstModifyingProcessor-generator + 1.0.0 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -XprintProcessorInfo + -XprintRounds + + -proc:none + + + + + diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/Order.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/Order.java new file mode 100644 index 0000000000..1d7cd6380b --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/Order.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor.usage; + +/** + * @author Filip Hrisafov + */ +public class Order { + + private String item; + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderDto.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderDto.java new file mode 100644 index 0000000000..afc64606b1 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderDto.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor.usage; + +/** + * @author Filip Hrisafov + */ +public class OrderDto { + + private String item; + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderMapper.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderMapper.java new file mode 100644 index 0000000000..75e52eb474 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/main/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/OrderMapper.java @@ -0,0 +1,20 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.faultyAstModifyingProcessor.usage; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface OrderMapper { + + OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); + + OrderDto orderToDto(Order order); +} diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java new file mode 100644 index 0000000000..06bfa3c533 --- /dev/null +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java @@ -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.itest.faultyAstModifyingProcessor.usage; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration test for using MapStruct with a faulty AstModifyingProcessor (i.e. when the processor cannot be loaded) + * + * @author Filip Hrisafov + */ +public class FaultyAstModifyingTestTest { + + @Test + public void testMapping() { + Order order = new Order(); + order.setItem( "my item" ); + + OrderDto dto = OrderMapper.INSTANCE.orderToDto( order ); + assertThat( dto.getItem() ).isEqualTo( "my item" ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java index 2b6f353d07..8fdc6201e1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java @@ -5,7 +5,10 @@ */ package org.mapstruct.ap.internal.util; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; @@ -45,7 +48,7 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen public AnnotationProcessorContext(Elements elementUtils, Types typeUtils, Messager messager, boolean verbose) { astModifyingAnnotationProcessors = java.util.Collections.unmodifiableList( - findAstModifyingAnnotationProcessors() ); + findAstModifyingAnnotationProcessors( messager ) ); this.elementUtils = elementUtils; this.typeUtils = typeUtils; this.messager = messager; @@ -105,20 +108,86 @@ else if ( elementUtils.getTypeElement( FreeBuilderConstants.FREE_BUILDER_FQN ) ! this.initialized = true; } - private static List findAstModifyingAnnotationProcessors() { + private static List findAstModifyingAnnotationProcessors(Messager messager) { List processors = new ArrayList<>(); ServiceLoader loader = ServiceLoader.load( AstModifyingAnnotationProcessor.class, AnnotationProcessorContext.class.getClassLoader() ); - for ( AstModifyingAnnotationProcessor astModifyingAnnotationProcessor : loader ) { - processors.add( astModifyingAnnotationProcessor ); + // Lombok packages an AstModifyingAnnotationProcessor as part of their jar + // this leads to problems within Eclipse when lombok is used as an agent + // Therefore we are wrapping this into an iterator that can handle exceptions by ignoring + // the faulty processor + Iterator loaderIterator = new FaultyDelegatingIterator( + messager, + loader.iterator() + ); + + while ( loaderIterator.hasNext() ) { + AstModifyingAnnotationProcessor processor = loaderIterator.next(); + if ( processor != null ) { + processors.add( processor ); + } } return processors; } + private static class FaultyDelegatingIterator implements Iterator { + + private final Messager messager; + private final Iterator delegate; + + private FaultyDelegatingIterator(Messager messager, + Iterator delegate) { + this.messager = messager; + this.delegate = delegate; + } + + @Override + public boolean hasNext() { + // Check the delegate maximum of 5 times + // before returning false + int failures = 5; + while ( failures > 0 ) { + try { + return delegate.hasNext(); + } + catch ( Throwable t ) { + failures--; + logFailure( t ); + } + } + + return false; + } + + @Override + public AstModifyingAnnotationProcessor next() { + try { + return delegate.next(); + } + catch ( Throwable t ) { + logFailure( t ); + return null; + } + } + + private void logFailure(Throwable t) { + StringWriter sw = new StringWriter(); + t.printStackTrace( new PrintWriter( sw ) ); + + String reportableStacktrace = sw.toString().replace( System.lineSeparator(), " " ); + + messager.printMessage( + Diagnostic.Kind.WARNING, + "Failed to read AstModifyingAnnotationProcessor. Reading next processor. Reason: " + + reportableStacktrace + ); + } + } + @Override public Elements getElementUtils() { return elementUtils; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/FaultyAstModifyingAnnotationProcessor.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/FaultyAstModifyingAnnotationProcessor.java new file mode 100644 index 0000000000..99d88e9883 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/FaultyAstModifyingAnnotationProcessor.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.ap.test.bugs._1159; + +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; + +/** + * @author Filip Hrisafov + */ +public class FaultyAstModifyingAnnotationProcessor implements AstModifyingAnnotationProcessor { + + public FaultyAstModifyingAnnotationProcessor() { + throw new RuntimeException( "Faulty AstModifyingAnnotationProcessor should not be instantiated" ); + } + + @Override + public boolean isTypeComplete(TypeMirror type) { + return false; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Mapper.java new file mode 100644 index 0000000000..ab1441c142 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Mapper.java @@ -0,0 +1,52 @@ +/* + * 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._1159; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue1159Mapper { + + Issue1159Mapper INSTANCE = Mappers.getMapper( Issue1159Mapper.class ); + + CarManualDto translateManual(CarManual manual); + + /** + * @author Filip Hrisafov + */ + class CarManual { + + private String content; + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + } + + /** + * @author Filip Hrisafov + */ + class CarManualDto { + + private String content; + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java new file mode 100644 index 0000000000..b93cd6d069 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java @@ -0,0 +1,60 @@ +/* + * 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._1159; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithServiceImplementation; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import org.mapstruct.ap.testutil.runner.Compiler; +import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("1159") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue1159Mapper.class, +}) +@WithServiceImplementation( + provides = AstModifyingAnnotationProcessor.class, + value = FaultyAstModifyingAnnotationProcessor.class +) +public class Issue1159Test { + + @Test + // The warning is not present in the Eclipse compilation for some reason + @DisabledOnCompiler({ + Compiler.ECLIPSE, + Compiler.ECLIPSE11 + }) + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.WARNING, + messageRegExp = "Failed to read AstModifyingAnnotationProcessor. Reading next processor. Reason:.*" + + "Faulty AstModifyingAnnotationProcessor should not be instantiated" + ) + }) + public void shouldIgnoreFaultyAstModifyingProcessor() { + + Issue1159Mapper.CarManual manual = new Issue1159Mapper.CarManual(); + manual.setContent( "test" ); + + Issue1159Mapper.CarManualDto manualDto = Issue1159Mapper.INSTANCE.translateManual( manual ); + + assertThat( manualDto ).isNotNull(); + assertThat( manualDto.getContent() ).isEqualTo( "test" ); + } +} From 7c62aec28195950ac6feafd4b2345a8c06ecf6c8 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 25 Apr 2020 17:01:22 +0200 Subject: [PATCH 047/597] #2077 nullpointer due to no-getter source (#2078) --- .../ap/internal/model/BeanMappingMethod.java | 49 ++++++++++--------- .../bugs/_2077/Issue2077ErroneousMapper.java | 37 ++++++++++++++ .../ap/test/bugs/_2077/Issue2077Test.java | 39 +++++++++++++++ 3 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077ErroneousMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 5cb2192aae..53a271858b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -721,30 +721,33 @@ else if ( mapping.getJavaExpression() != null ) { sourceRef = getSourceRef( method.getSourceParameters().get( 0 ), targetPropertyName ); } - if ( sourceRef.isValid() ) { + if ( sourceRef != null ) { + // sourceRef == null is not considered an error here + if ( sourceRef.isValid() ) { - // targetProperty == null can occur: we arrived here because we want as many errors - // as possible before we stop analysing - propertyMapping = new PropertyMappingBuilder() - .mappingContext( ctx ) - .sourceMethod( method ) - .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) - .sourcePropertyName( mapping.getSourceName() ) - .sourceReference( sourceRef ) - .selectionParameters( mapping.getSelectionParameters() ) - .formattingParameters( mapping.getFormattingParameters() ) - .existingVariableNames( existingVariableNames ) - .dependsOn( mapping.getDependsOn() ) - .defaultValue( mapping.getDefaultValue() ) - .defaultJavaExpression( mapping.getDefaultJavaExpression() ) - .mirror( mapping.getMirror() ) - .options( mapping ) - .build(); - handledTargets.add( targetPropertyName ); - unprocessedSourceParameters.remove( sourceRef.getParameter() ); - } - else { - errorOccured = true; + // targetProperty == null can occur: we arrived here because we want as many errors + // as possible before we stop analysing + propertyMapping = new PropertyMappingBuilder() + .mappingContext( ctx ) + .sourceMethod( method ) + .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) + .sourcePropertyName( mapping.getSourceName() ) + .sourceReference( sourceRef ) + .selectionParameters( mapping.getSelectionParameters() ) + .formattingParameters( mapping.getFormattingParameters() ) + .existingVariableNames( existingVariableNames ) + .dependsOn( mapping.getDependsOn() ) + .defaultValue( mapping.getDefaultValue() ) + .defaultJavaExpression( mapping.getDefaultJavaExpression() ) + .mirror( mapping.getMirror() ) + .options( mapping ) + .build(); + handledTargets.add( targetPropertyName ); + unprocessedSourceParameters.remove( sourceRef.getParameter() ); + } + else { + errorOccured = true; + } } } // remaining are the mappings without a 'source' so, 'only' a date format or qualifiers diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077ErroneousMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077ErroneousMapper.java new file mode 100644 index 0000000000..ecbff63802 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077ErroneousMapper.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.bugs._2077; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper( unmappedTargetPolicy = ReportingPolicy.ERROR ) +public interface Issue2077ErroneousMapper { + + Issue2077ErroneousMapper INSTANCE = Mappers.getMapper( Issue2077ErroneousMapper.class ); + + @Mapping(target = "s1", defaultValue = "xyz" ) + Target map(String source); + + class Target { + + private String s1; + + public String getS1() { + return s1; + } + + public void setS1(String s1) { + this.s1 = s1; + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java new file mode 100644 index 0000000000..d8c3a1e5d7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java @@ -0,0 +1,39 @@ +/* + * 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._2077; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static javax.tools.Diagnostic.Kind.ERROR; + +/** + * @author Sjaak Derksen + */ +@IssueKey("2077") +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2077Test { + + @Test + @WithClasses(Issue2077ErroneousMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = Issue2077ErroneousMapper.class, + kind = ERROR, + line = 22, + message = "Unmapped target property: \"s1\".") + } + ) + public void shouldNotCompile() { + } +} From d6ff5204d743795816fa4a343b2ebff8c2b7c15c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 13 Apr 2020 20:14:11 +0200 Subject: [PATCH 048/597] Accessor#getSimpleName should return a String --- .../ap/internal/model/NestedPropertyMappingMethod.java | 2 +- .../org/mapstruct/ap/internal/model/PropertyMapping.java | 6 +++--- .../java/org/mapstruct/ap/internal/model/common/Type.java | 2 +- .../java/org/mapstruct/ap/internal/util/ValueProvider.java | 2 +- .../ap/internal/util/accessor/AbstractAccessor.java | 5 ++--- .../org/mapstruct/ap/internal/util/accessor/Accessor.java | 2 +- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java index f698f83459..f5616d1278 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java @@ -150,7 +150,7 @@ public SafePropertyEntry(PropertyEntry entry, String safeName) { this.safeName = safeName; this.readAccessorName = ValueProvider.of( entry.getReadAccessor() ).getValue(); if ( entry.getPresenceChecker() != null ) { - this.presenceCheckerName = entry.getPresenceChecker().getSimpleName().toString(); + this.presenceCheckerName = entry.getPresenceChecker().getSimpleName(); } else { this.presenceCheckerName = null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 24facce722..1558fc7f8a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -273,7 +273,7 @@ else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.get return new PropertyMapping( targetPropertyName, rightHandSide.getSourceParameterName(), - targetWriteAccessor.getSimpleName().toString(), + targetWriteAccessor.getSimpleName(), ValueProvider.of( targetReadAccessor ), targetType, assignment, @@ -869,7 +869,7 @@ else if ( errorMessageDetails == null ) { return new PropertyMapping( targetPropertyName, - targetWriteAccessor.getSimpleName().toString(), + targetWriteAccessor.getSimpleName(), ValueProvider.of( targetReadAccessor ), targetType, assignment, @@ -934,7 +934,7 @@ public PropertyMapping build() { return new PropertyMapping( targetPropertyName, - targetWriteAccessor.getSimpleName().toString(), + targetWriteAccessor.getSimpleName(), ValueProvider.of( targetReadAccessor ), targetType, assignment, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 6f3a7b79c3..52a076270b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -488,7 +488,7 @@ public Map getPropertyReadAccessors() { if ( modifiableGetters.containsKey( propertyName ) ) { // In the DefaultAccessorNamingStrategy, this can only be the case for Booleans: isFoo() and // getFoo(); The latter is preferred. - if ( !getter.getSimpleName().toString().startsWith( "is" ) ) { + if ( !getter.getSimpleName().startsWith( "is" ) ) { modifiableGetters.put( propertyName, getter ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java index 31a3a9c6cd..0f64c2b375 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java @@ -45,7 +45,7 @@ public static ValueProvider of(Accessor accessor) { if ( accessor == null ) { return null; } - String value = accessor.getSimpleName().toString(); + String value = accessor.getSimpleName(); if ( accessor.getAccessorType() != AccessorType.FIELD ) { value += "()"; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java index 5a16e45fa8..04c4c99992 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java @@ -9,7 +9,6 @@ import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; -import javax.lang.model.element.Name; /** * This is an abstract implementation of an {@link Accessor} that provides the common implementation. @@ -25,8 +24,8 @@ abstract class AbstractAccessor implements Accessor { } @Override - public Name getSimpleName() { - return element.getSimpleName(); + public String getSimpleName() { + return element.getSimpleName().toString(); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java index f9dd435d5f..917d1a216b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java @@ -33,7 +33,7 @@ public interface Accessor { /** * @return the simple name of the accessor */ - Name getSimpleName(); + String getSimpleName(); /** * @return the set of modifiers that the accessor has From 2b2299a730f9b8f6ee48cef3b8c84d7e7604039c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 26 Apr 2020 12:08:57 +0200 Subject: [PATCH 049/597] #73 Add support for using constructor arguments when instantiating mapping targets By default the constructor argument names are used to extract the target properties. If a constructor is annotated with an annotation named `@ConstructorProperties` (from any package) then it would be used to extract the target properties. If a mapping target has a parameterless empty constructor it would be used to instantiate the target. When there are multiple constructors then an annotation named `@Default` (from any package) can be used to mark a constructor that should be used by default when instantiating the target. Supports mapping into Java 14 Records and Kotlin data classes out of the box --- .../resources/build-config/checkstyle.xml | 3 - .../chapter-3-defining-a-mapper.asciidoc | 77 ++++ .../chapter-5-data-type-conversions.asciidoc | 6 + .../itest/tests/MavenIntegrationTest.java | 8 + .../ProcessorInvocationInterceptor.java | 3 + .../testutil/extension/ProcessorTest.java | 3 + .../extension/ProcessorTestContext.java | 9 +- ...TestTemplateInvocationContextProvider.java | 3 +- .../src/test/resources/kotlinDataTest/pom.xml | 102 +++++ .../itest/kotlin/data/CustomerEntity.java | 31 ++ .../itest/kotlin/data/CustomerMapper.java | 27 ++ .../itest/kotlin/data/CustomerDto.kt | 10 + .../itest/kotlin/data/KotlinDataTest.java | 38 ++ .../itest/records/CustomerMapper.java | 4 + .../mapstruct/itest/records/RecordsTest.java | 13 + .../ap/internal/model/BeanMappingMethod.java | 387 ++++++++++++++++-- .../model/CollectionAssignmentBuilder.java | 14 +- .../ap/internal/model/MethodReference.java | 43 ++ .../model/NormalTypeMappingMethod.java | 3 + .../model/ObjectFactoryMethodResolver.java | 37 +- .../ap/internal/model/PropertyMapping.java | 39 +- .../ap/internal/model/common/Parameter.java | 23 +- .../model/common/ParameterBinding.java | 11 + .../ap/internal/model/common/Type.java | 41 +- .../ap/internal/model/common/TypeFactory.java | 13 +- .../ap/internal/util/AccessorNamingUtils.java | 5 - .../mapstruct/ap/internal/util/Filters.java | 25 +- .../mapstruct/ap/internal/util/Message.java | 4 +- .../ap/internal/util/ValueProvider.java | 3 +- .../ap/internal/util/accessor/Accessor.java | 3 +- .../internal/util/accessor/AccessorType.java | 5 + ...ccessor.java => FieldElementAccessor.java} | 4 +- .../accessor/ParameterElementAccessor.java | 46 +++ .../ap/internal/model/BeanMappingMethod.ftl | 57 ++- .../ap/internal/model/MethodReference.ftl | 2 + .../model/assignment/AdderWrapper.ftl | 2 +- .../model/assignment/ArrayCopyWrapper.ftl | 2 +- .../model/assignment/SetterWrapper.ftl | 2 +- .../SetterWrapperForCollectionsAndMaps.ftl | 2 +- ...pperForCollectionsAndMapsWithNullCheck.ftl | 6 +- .../ap/internal/model/macro/CommonMacros.ftl | 2 +- .../ap/test/bugs/_1242/Issue1242Test.java | 7 +- .../ap/test/bugs/_1283/Issue1283Test.java | 6 +- .../mapstruct/ap/test/bugs/_1283/Source.java | 2 +- .../constructor/ConstructorProperties.java | 23 ++ .../ap/test/constructor/Default.java | 21 + .../mapstruct/ap/test/constructor/Person.java | 55 +++ .../ap/test/constructor/PersonDto.java | 69 ++++ .../PersonWithConstructorProperties.java | 58 +++ .../SimpleConstructorPropertiesMapper.java | 30 ++ .../SimpleConstructorPropertiesTest.java | 90 ++++ ...PersonWithDefaultAnnotatedConstructor.java | 35 ++ ...mpleDefaultAnnotatedConstructorMapper.java | 29 ++ ...SimpleDefaultAnnotatedConstructorTest.java | 80 ++++ .../ErroneousAmbiguousConstructorsMapper.java | 33 ++ .../ErroneousConstructorPropertiesMapper.java | 31 ++ .../erroneous/ErroneousConstructorTest.java | 59 +++ .../ConstructorMixedWithSettersMapper.java | 21 + .../ConstructorMixedWithSettersTest.java | 48 +++ .../test/constructor/mixed/PersonMixed.java | 67 +++ .../nestedsource/ArtistToChartEntry.java | 54 +++ .../ArtistToChartEntryComposedReverse.java | 49 +++ .../ArtistToChartEntryConfig.java | 28 ++ .../ArtistToChartEntryReverse.java | 38 ++ .../ArtistToChartEntryWithConfigReverse.java | 37 ++ .../ArtistToChartEntryWithFactoryReverse.java | 39 ++ .../ArtistToChartEntryWithIgnoresReverse.java | 41 ++ .../ArtistToChartEntryWithMappingReverse.java | 59 +++ ...NestedSourcePropertiesConstructorTest.java | 132 ++++++ ...NestedSourcePropertiesConstructorTest.java | 222 ++++++++++ .../_target/AdderUsageObserver.java | 26 ++ .../nestedsource/_target/BaseChartEntry.java | 34 ++ .../nestedsource/_target/ChartEntry.java | 54 +++ .../_target/ChartEntryComposed.java | 58 +++ .../nestedsource/_target/ChartEntryLabel.java | 34 ++ .../_target/ChartEntryWithBase.java | 36 ++ .../_target/ChartEntryWithMapping.java | 54 +++ .../nestedsource/_target/ChartPositions.java | 25 ++ .../nestedsource/source/Artist.java | 29 ++ .../nestedsource/source/Chart.java | 35 ++ .../nestedsource/source/Label.java | 29 ++ .../constructor/nestedsource/source/Song.java | 37 ++ .../nestedsource/source/Studio.java | 29 ++ .../nestedtarget/ChartEntryToArtist.java | 59 +++ ...estedProductPropertiesConstructorTest.java | 98 +++++ .../simple/SimpleConstructorMapper.java | 31 ++ .../simple/SimpleConstructorTest.java | 89 ++++ .../ap/test/constructor/unmapped/Order.java | 30 ++ .../test/constructor/unmapped/OrderDto.java | 22 + .../unmapped/UnmappedConstructorMapper.java | 20 + .../unmapped/UnmappedConstructorTest.java | 45 ++ .../AmbiguousAnnotatedFactoryTest.java | 7 +- .../ambiguousfactorymethod/FactoryTest.java | 7 +- .../ArtistToChartEntryErroneous.java | 17 +- .../NestedSourcePropertiesTest.java | 19 +- .../ap/test/selection/resulttype/Citrus.java | 21 + ...ultTypeNoAccessibleConstructorMapper.java} | 9 +- .../resulttype/InheritanceSelectionTest.java | 73 +++- ...uctorConstructingFruitInterfaceMapper.java | 23 ++ ...ithConstructorConstructingFruitMapper.java | 24 ++ .../ErroneousOrganizationMapper2.java | 7 + .../test/updatemethods/UpdateMethodsTest.java | 4 +- .../testutil/runner/CompilingStatement.java | 6 +- .../nestedsource/ArtistToChartEntryImpl.java | 170 ++++++++ .../nestedtarget/ChartEntryToArtistImpl.java | 236 +++++++++++ 105 files changed, 3905 insertions(+), 173 deletions(-) create mode 100644 integrationtest/src/test/resources/kotlinDataTest/pom.xml create mode 100644 integrationtest/src/test/resources/kotlinDataTest/src/main/java/org/mapstruct/itest/kotlin/data/CustomerEntity.java create mode 100644 integrationtest/src/test/resources/kotlinDataTest/src/main/java/org/mapstruct/itest/kotlin/data/CustomerMapper.java create mode 100644 integrationtest/src/test/resources/kotlinDataTest/src/main/kotlin/org/mapstruct/itest/kotlin/data/CustomerDto.kt create mode 100644 integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java rename processor/src/main/java/org/mapstruct/ap/internal/util/accessor/{VariableElementAccessor.java => FieldElementAccessor.java} (83%) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/ConstructorProperties.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/Default.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/Person.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/PersonDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/PersonWithConstructorProperties.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/PersonWithDefaultAnnotatedConstructor.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousAmbiguousConstructorsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorPropertiesMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/PersonMixed.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntry.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryComposedReverse.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryReverse.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithConfigReverse.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithFactoryReverse.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithIgnoresReverse.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithMappingReverse.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/NestedSourcePropertiesConstructorTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ReversingNestedSourcePropertiesConstructorTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/AdderUsageObserver.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/BaseChartEntry.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntry.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryComposed.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryLabel.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryWithBase.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryWithMapping.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartPositions.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Artist.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Chart.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Label.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Song.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Studio.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtist.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/NestedProductPropertiesConstructorTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/Order.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/OrderDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/Citrus.java rename processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/{ErroneousResultTypeNoEmptyConstructorMapper.java => ErroneousResultTypeNoAccessibleConstructorMapper.java} (71%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ResultTypeWithConstructorConstructingFruitInterfaceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ResultTypeWithConstructorConstructingFruitMapper.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java diff --git a/build-config/src/main/resources/build-config/checkstyle.xml b/build-config/src/main/resources/build-config/checkstyle.xml index 945015d5cd..a4591c39de 100644 --- a/build-config/src/main/resources/build-config/checkstyle.xml +++ b/build-config/src/main/resources/build-config/checkstyle.xml @@ -122,9 +122,6 @@ - - - diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 5255ac6f1b..ad5d8111f7 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -528,3 +528,80 @@ Otherwise, you would need to write a custom `BuilderProvider` ==== In case you want to disable using builders then you can use the `NoOpBuilderProvider` by creating a `org.mapstruct.ap.spi.BuilderProvider` file in the `META-INF/services` directory with `org.mapstruct.ap.spi.NoOpBuilderProvider` as it's content. ==== + +[[mapping-with-constructors]] +=== Using Constructors + +MapStruct supports using constructors for mapping target types. +When doing a mapping MapStruct checks if there is a builder for the type being mapped. +If there is no builder, then MapStruct looks for a single accessible constructor. +When there are multiple constructors then the following is done to pick the one which should be used: + +* If a parameterless constructor exists then it would be used to construct the object, and the other constructors will be ignored +* If there are multiple constructors then the one annotated with annotation named `@Default` (from any package) will be used + +When using a constructor then the names of the parameters of the constructor will be used and matched to the target properties. +When the constructor has an annotation named `@ConstructorProperties` (from any package) then this annotation will be used to get the names of the parameters. + +[NOTE] +==== +When an object factory method or a method annotated with `@ObjectFactory` exists, it will take precedence over any constructor defined in the target. +The target object constructor will not be used in that case. +==== + + +.Person with constructor parameters +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public class Person { + + private final String name; + private final String surname; + + public Person(String name, String surname) { + this.name = name; + this.surname = surname; + } +} +---- +==== + +.Person With Constructor Mapper definition +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public interface PersonMapper { + + Person map(PersonDto dto); +} +---- +==== + +.Generated mapper with constructor +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class PersonMapperImpl implements PersonMapper { + + public Person map(PersonDto dto) { + if (dto == null) { + return null; + } + + String name; + String surname; + name = dto.getName(); + surname = dto.getSurname(); + + Person person = new Person( name, surname ); + + return person; + } +} +---- +==== diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 7a22aad559..725c69bad0 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -174,6 +174,12 @@ During the generation of automatic sub-mapping methods <> Follow issue https://github.com/mapstruct/mapstruct/issues/1086[#1086] for more information. ==== +[NOTE] +==== +Constructor properties of the target object are also considered as target properties. +You can read more about that in <> +==== + [[controlling-nested-bean-mappings]] === Controlling nested bean mappings diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 0789e3051d..76a72b4b7f 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -107,6 +107,14 @@ void protobufBuilderTest() { void recordsTest() { } + @ProcessorTest(baseDir = "kotlinDataTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC + }, forkJvm = true) + // We have to fork the jvm because there is an NPE in com.intellij.openapi.util.SystemInfo.getRtVersion + // and the kotlin-maven-plugin uses that. See also https://youtrack.jetbrains.com/issue/IDEA-238907 + void kotlinDataTest() { + } + @ProcessorTest(baseDir = "simpleTest") void simpleTest() { } diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java index f6f9186ba6..839bf86f18 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java @@ -71,6 +71,9 @@ private void doExecute(ExtensionContext extensionContext) throws Exception { } else { verifier = new Verifier( destination.getCanonicalPath() ); + if ( processorTestContext.isForkJvm() ) { + verifier.setForkJvm( true ); + } } List goals = new ArrayList<>( 3 ); diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java index 1db77ae6b2..eb8553960c 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java @@ -106,4 +106,7 @@ ProcessorType[] processorTypes() default { * @return the {@link CommandLineEnhancer} implementation. Must have a default constructor. */ Class commandLineEnhancer() default CommandLineEnhancer.class; + + boolean forkJvm() default false; + } diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestContext.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestContext.java index be1e4647c7..feb5c7dc02 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestContext.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestContext.java @@ -13,13 +13,16 @@ public class ProcessorTestContext { private final String baseDir; private final ProcessorTest.ProcessorType processor; private final Class cliEnhancerClass; + private final boolean forkJvm; public ProcessorTestContext(String baseDir, ProcessorTest.ProcessorType processor, - Class cliEnhancerClass) { + Class cliEnhancerClass, + boolean forkJvm) { this.baseDir = baseDir; this.processor = processor; this.cliEnhancerClass = cliEnhancerClass; + this.forkJvm = forkJvm; } public String getBaseDir() { @@ -33,4 +36,8 @@ public ProcessorTest.ProcessorType getProcessor() { public Class getCliEnhancerClass() { return cliEnhancerClass; } + + public boolean isForkJvm() { + return forkJvm; + } } diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContextProvider.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContextProvider.java index b7a4f331cf..5ceb44804d 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContextProvider.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTestTemplateInvocationContextProvider.java @@ -34,7 +34,8 @@ public Stream provideTestTemplateInvocationContex .map( processorType -> new ProcessorTestTemplateInvocationContext( new ProcessorTestContext( processorTest.baseDir(), processorType, - processorTest.commandLineEnhancer() + processorTest.commandLineEnhancer(), + processorTest.forkJvm() ) ) ); } } diff --git a/integrationtest/src/test/resources/kotlinDataTest/pom.xml b/integrationtest/src/test/resources/kotlinDataTest/pom.xml new file mode 100644 index 0000000000..8a43a04194 --- /dev/null +++ b/integrationtest/src/test/resources/kotlinDataTest/pom.xml @@ -0,0 +1,102 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + kotlinDataTest + jar + + + 1.3.70 + + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + + + + generate-via-compiler-plugin + + false + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + + \${project.basedir}/src/main/kotlin + \${project.basedir}/src/main/java + + + + + test-compile + test-compile + + + \${project.basedir}/src/test/kotlin + \${project.basedir}/src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + compile + + + java-test-compile + test-compile + testCompile + + + + + + + + + diff --git a/integrationtest/src/test/resources/kotlinDataTest/src/main/java/org/mapstruct/itest/kotlin/data/CustomerEntity.java b/integrationtest/src/test/resources/kotlinDataTest/src/main/java/org/mapstruct/itest/kotlin/data/CustomerEntity.java new file mode 100644 index 0000000000..e992eb0e60 --- /dev/null +++ b/integrationtest/src/test/resources/kotlinDataTest/src/main/java/org/mapstruct/itest/kotlin/data/CustomerEntity.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.kotlin.data; + +/** + * @author Filip Hrisafov + */ +public class CustomerEntity { + + private String name; + private String mail; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } +} diff --git a/integrationtest/src/test/resources/kotlinDataTest/src/main/java/org/mapstruct/itest/kotlin/data/CustomerMapper.java b/integrationtest/src/test/resources/kotlinDataTest/src/main/java/org/mapstruct/itest/kotlin/data/CustomerMapper.java new file mode 100644 index 0000000000..b2157252db --- /dev/null +++ b/integrationtest/src/test/resources/kotlinDataTest/src/main/java/org/mapstruct/itest/kotlin/data/CustomerMapper.java @@ -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.itest.kotlin.data; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CustomerMapper { + + CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class ); + + @Mapping(target = "mail", source = "email") + CustomerEntity fromRecord(CustomerDto record); + + @InheritInverseConfiguration + CustomerDto toRecord(CustomerEntity entity); + +} diff --git a/integrationtest/src/test/resources/kotlinDataTest/src/main/kotlin/org/mapstruct/itest/kotlin/data/CustomerDto.kt b/integrationtest/src/test/resources/kotlinDataTest/src/main/kotlin/org/mapstruct/itest/kotlin/data/CustomerDto.kt new file mode 100644 index 0000000000..820a8b56c3 --- /dev/null +++ b/integrationtest/src/test/resources/kotlinDataTest/src/main/kotlin/org/mapstruct/itest/kotlin/data/CustomerDto.kt @@ -0,0 +1,10 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.kotlin.data; + +data class CustomerDto(var name: String?, var email: String?) { + +} diff --git a/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java b/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java new file mode 100644 index 0000000000..513f080a8b --- /dev/null +++ b/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java @@ -0,0 +1,38 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.kotlin.data; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.mapstruct.itest.kotlin.data.CustomerDto; +import org.mapstruct.itest.kotlin.data.CustomerEntity; +import org.mapstruct.itest.kotlin.data.CustomerMapper; + +public class KotlinDataTest { + + @Test + public void shouldMapData() { + CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) ); + + assertThat( customer ).isNotNull(); + assertThat( customer.getName() ).isEqualTo( "Kermit" ); + assertThat( customer.getMail() ).isEqualTo( "kermit@test.com" ); + } + + @Test + public void shouldMapIntoData() { + CustomerEntity entity = new CustomerEntity(); + entity.setName( "Kermit" ); + entity.setMail( "kermit@test.com" ); + + CustomerDto customer = CustomerMapper.INSTANCE.toRecord( entity ); + + assertThat( customer ).isNotNull(); + assertThat( customer.getName() ).isEqualTo( "Kermit" ); + assertThat( customer.getEmail() ).isEqualTo( "kermit@test.com" ); + } +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java index 0e8a0d2567..a3c7037f03 100644 --- a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java @@ -5,6 +5,7 @@ */ package org.mapstruct.itest.records; +import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @@ -20,4 +21,7 @@ public interface CustomerMapper { @Mapping(target = "mail", source = "email") CustomerEntity fromRecord(CustomerDto record); + @InheritInverseConfiguration + CustomerDto toRecord(CustomerEntity entity); + } diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java index 5a7a62f780..824a763fc7 100644 --- a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java @@ -22,4 +22,17 @@ public void shouldMapRecord() { assertThat( customer.getName() ).isEqualTo( "Kermit" ); assertThat( customer.getMail() ).isEqualTo( "kermit@test.com" ); } + + @Test + public void shouldMapIntoRecord() { + CustomerEntity entity = new CustomerEntity(); + entity.setName( "Kermit" ); + entity.setMail( "kermit@test.com" ); + + CustomerDto customer = CustomerMapper.INSTANCE.toRecord( entity ); + + assertThat( customer ).isNotNull(); + assertThat( customer.name() ).isEqualTo( "Kermit" ); + assertThat( customer.email() ).isEqualTo( "kermit@test.com" ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 53a271858b..ea4f3fbdee 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -8,18 +8,26 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; import javax.lang.model.type.DeclaredType; +import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; @@ -33,6 +41,7 @@ import org.mapstruct.ap.internal.model.beanmapping.TargetReference; import org.mapstruct.ap.internal.model.common.BuilderType; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.ParameterBinding; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder; @@ -41,15 +50,20 @@ import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; +import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; +import org.mapstruct.ap.internal.util.accessor.ParameterElementAccessor; import static org.mapstruct.ap.internal.model.beanmapping.MappingReferences.forSourceMethod; import static org.mapstruct.ap.internal.util.Collections.first; import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_ABSTRACT; import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_NOT_ASSIGNABLE; import static org.mapstruct.ap.internal.util.Message.GENERAL_ABSTRACT_RETURN_TYPE; +import static org.mapstruct.ap.internal.util.Message.GENERAL_AMBIGIOUS_CONSTRUCTORS; +import static org.mapstruct.ap.internal.util.Message.GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS; /** * A {@link MappingMethod} implemented by a {@link Mapper} class which maps one bean type to another, optionally @@ -61,7 +75,9 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { private final List propertyMappings; private final Map> mappingsByParameter; + private final Map> constructorMappingsByParameter; private final List constantMappings; + private final List constructorConstantMappings; private final Type returnTypeToConstruct; private final BuilderType returnTypeBuilder; private final MethodReference finalizerMethod; @@ -73,6 +89,7 @@ public static class Builder { /* returnType to construct can have a builder */ private BuilderType returnTypeBuilder; + private Map unprocessedConstructorProperties; private Map unprocessedTargetProperties; private Map unprocessedSourceProperties; private Set targetProperties; @@ -82,6 +99,8 @@ public static class Builder { private final Map> unprocessedDefinedTargets = new LinkedHashMap<>(); private MappingReferences mappingReferences; + private MethodReference factoryMethod; + private boolean hasFactoryMethod; public Builder mappingContext(MappingBuilderContext mappingContext) { this.ctx = mappingContext; @@ -125,15 +144,12 @@ public BeanMappingMethod build() { /* 3) or the builder whenever the return type is immutable */ Type returnTypeToConstruct = null; - /* factory or builder method to construct the returnTypeToConstruct */ - MethodReference factoryMethod = null; - // determine which return type to construct boolean cannotConstructReturnType = false; if ( !method.getReturnType().isVoid() ) { Type returnTypeImpl = getReturnTypeToConstructFromSelectionParameters( selectionParameters ); if ( returnTypeImpl != null ) { - factoryMethod = getFactoryMethod( returnTypeImpl, selectionParameters ); + initializeFactoryMethod( returnTypeImpl, selectionParameters ); if ( factoryMethod != null || canResultTypeFromBeanMappingBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } @@ -143,7 +159,7 @@ public BeanMappingMethod build() { } else if ( isBuilderRequired() ) { returnTypeImpl = returnTypeBuilder.getBuilder(); - factoryMethod = getFactoryMethod( returnTypeImpl, selectionParameters ); + initializeFactoryMethod( returnTypeImpl, selectionParameters ); if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } @@ -153,7 +169,7 @@ else if ( isBuilderRequired() ) { } else if ( !method.isUpdateMethod() ) { returnTypeImpl = method.getReturnType(); - factoryMethod = getFactoryMethod( returnTypeImpl, selectionParameters ); + initializeFactoryMethod( returnTypeImpl, selectionParameters ); if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } @@ -171,13 +187,39 @@ else if ( !method.isUpdateMethod() ) { /* the type that needs to be used in the mapping process as target */ Type resultTypeToMap = returnTypeToConstruct == null ? method.getResultType() : returnTypeToConstruct; + existingVariableNames.addAll( method.getParameterNames() ); + CollectionMappingStrategyGem cms = this.method.getOptions().getMapper().getCollectionMappingStrategy(); // determine accessors Map accessors = resultTypeToMap.getPropertyWriteAccessors( cms ); - this.targetProperties = accessors.keySet(); + this.targetProperties = new LinkedHashSet<>( accessors.keySet() ); this.unprocessedTargetProperties = new LinkedHashMap<>( accessors ); + + if ( !method.isUpdateMethod() && !hasFactoryMethod ) { + ConstructorAccessor constructorAccessor = getConstructorAccessor( resultTypeToMap ); + if ( constructorAccessor != null ) { + + this.unprocessedConstructorProperties = constructorAccessor.constructorAccessors; + + factoryMethod = MethodReference.forConstructorInvocation( + resultTypeToMap, + constructorAccessor.parameterBindings + ); + + } + else { + this.unprocessedConstructorProperties = new LinkedHashMap<>(); + } + + this.targetProperties.addAll( this.unprocessedConstructorProperties.keySet() ); + this.unprocessedTargetProperties.putAll( this.unprocessedConstructorProperties ); + } + else { + unprocessedConstructorProperties = new LinkedHashMap<>(); + } + this.unprocessedSourceProperties = new LinkedHashMap<>(); for ( Parameter sourceParameter : method.getSourceParameters() ) { unprocessedSourceParameters.add( sourceParameter ); @@ -191,7 +233,6 @@ else if ( !method.isUpdateMethod() ) { unprocessedSourceProperties.put( entry.getKey(), entry.getValue() ); } } - existingVariableNames.addAll( method.getParameterNames() ); // get bean mapping (when specified as annotation ) if ( beanMapping != null ) { @@ -223,6 +264,9 @@ else if ( !method.isUpdateMethod() ) { // Process the unprocessed defined targets handleUnprocessedDefinedTargets(); + // Initialize unprocessed constructor properties + handleUnmappedConstructorProperties(); + // report errors on unmapped properties reportErrorForUnmappedTargetPropertiesIfRequired(); reportErrorForUnmappedSourcePropertiesIfRequired(); @@ -364,6 +408,7 @@ private void handleUnprocessedDefinedTargets() { if ( propertyMapping != null ) { unprocessedTargetProperties.remove( propertyName ); + unprocessedConstructorProperties.remove( propertyName ); unprocessedSourceProperties.remove( propertyName ); iterator.remove(); propertyMappings.add( propertyMapping ); @@ -374,6 +419,28 @@ private void handleUnprocessedDefinedTargets() { } } + private void handleUnmappedConstructorProperties() { + for ( Entry entry : unprocessedConstructorProperties.entrySet() ) { + Accessor accessor = entry.getValue(); + Type accessedType = ctx.getTypeFactory() + .getType( accessor.getAccessedType() ); + String targetPropertyName = entry.getKey(); + + propertyMappings.add( new JavaExpressionMappingBuilder() + .mappingContext( ctx ) + .sourceMethod( method ) + .javaExpression( accessedType.getNull() ) + .existingVariableNames( existingVariableNames ) + .target( targetPropertyName, null, accessor ) + .dependsOn( Collections.emptySet() ) + .mirror( null ) + .build() + ); + } + + unprocessedConstructorProperties.clear(); + } + /** * Sources the given mappings as per the dependency relationships given via {@code dependsOn()}. If a cycle is * detected, an error is reported. @@ -434,7 +501,7 @@ else if ( !resultType.isAssignableTo( method.getResultType() ) ) { ); error = false; } - else if ( !resultType.hasEmptyAccessibleConstructor() ) { + else if ( !resultType.hasAccessibleConstructor() ) { ctx.getMessager().printMessage( method.getExecutable(), method.getOptions().getBeanMapping().getMirror(), @@ -456,7 +523,7 @@ private boolean canReturnTypeBeConstructed(Type returnType) { ); error = false; } - else if ( !returnType.hasEmptyAccessibleConstructor() ) { + else if ( !returnType.hasAccessibleConstructor() ) { ctx.getMessager().printMessage( method.getExecutable(), Message.GENERAL_NO_SUITABLE_CONSTRUCTOR, @@ -470,20 +537,225 @@ else if ( !returnType.hasEmptyAccessibleConstructor() ) { /** * Find a factory method for a return type or for a builder. * @param returnTypeImpl the return type implementation to construct - * @param selectionParameters + * @param @selectionParameters * @return */ - private MethodReference getFactoryMethod(Type returnTypeImpl, SelectionParameters selectionParameters) { - MethodReference factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method, - returnTypeImpl, - selectionParameters, - ctx + private void initializeFactoryMethod(Type returnTypeImpl, SelectionParameters selectionParameters) { + List> matchingFactoryMethods = + ObjectFactoryMethodResolver.getMatchingFactoryMethods( + method, + returnTypeImpl, + selectionParameters, + ctx + ); + + if ( matchingFactoryMethods.isEmpty() ) { + if ( factoryMethod == null && returnTypeBuilder != null ) { + factoryMethod = ObjectFactoryMethodResolver.getBuilderFactoryMethod( method, returnTypeBuilder ); + hasFactoryMethod = factoryMethod != null; + } + } + else if ( matchingFactoryMethods.size() == 1 ) { + factoryMethod = ObjectFactoryMethodResolver.getFactoryMethodReference( + method, + first( matchingFactoryMethods ), + ctx + ); + hasFactoryMethod = true; + } + else { + ctx.getMessager().printMessage( + method.getExecutable(), + Message.GENERAL_AMBIGIOUS_FACTORY_METHOD, + returnTypeImpl, + Strings.join( matchingFactoryMethods, ", " ) + ); + hasFactoryMethod = true; + } + } + + private ConstructorAccessor getConstructorAccessor(Type type) { + + if ( type.isRecord() ) { + // If the type is a record then just get the record components and use then + List recordComponents = type.getRecordComponents(); + List parameterBindings = new ArrayList<>( recordComponents.size() ); + Map constructorAccessors = new LinkedHashMap<>(); + for ( Element recordComponent : recordComponents ) { + String parameterName = recordComponent.getSimpleName().toString(); + Accessor accessor = createConstructorAccessor( recordComponent, parameterName ); + constructorAccessors.put( + parameterName, + accessor + ); + + parameterBindings.add( ParameterBinding.fromTypeAndName( + ctx.getTypeFactory().getType( recordComponent.asType() ), + accessor.getSimpleName() + ) ); + } + return new ConstructorAccessor( parameterBindings, constructorAccessors ); + } + + List constructors = ElementFilter.constructorsIn( type.getTypeElement() + .getEnclosedElements() ); + + ExecutableElement defaultConstructor = null; + List accessibleConstructors = new ArrayList<>( constructors.size() ); + + for ( ExecutableElement constructor : constructors ) { + if ( constructor.getModifiers().contains( Modifier.PRIVATE ) ) { + continue; + } + + if ( constructor.getParameters().isEmpty() ) { + defaultConstructor = constructor; + } + else { + accessibleConstructors.add( constructor ); + } + } + + if ( defaultConstructor != null ) { + return null; + } + + if ( accessibleConstructors.isEmpty() ) { + return null; + } + + ExecutableElement constructor = null; + if ( accessibleConstructors.size() > 1 ) { + + for ( ExecutableElement accessibleConstructor : accessibleConstructors ) { + for ( AnnotationMirror annotationMirror : accessibleConstructor.getAnnotationMirrors() ) { + if ( annotationMirror.getAnnotationType() + .asElement() + .getSimpleName() + .contentEquals( "Default" ) ) { + constructor = accessibleConstructor; + break; + } + } + } + + if ( constructor == null ) { + ctx.getMessager().printMessage( + method.getExecutable(), + GENERAL_AMBIGIOUS_CONSTRUCTORS, + type, + Strings.join( constructors, ", " ) + ); + return null; + } + } + else { + constructor = accessibleConstructors.get( 0 ); + } + + List constructorParameters = ctx.getTypeFactory() + .getParameters( (DeclaredType) type.getTypeMirror(), constructor ); + + + List constructorProperties = null; + for ( AnnotationMirror annotationMirror : constructor.getAnnotationMirrors() ) { + if ( annotationMirror.getAnnotationType() + .asElement() + .getSimpleName() + .contentEquals( "ConstructorProperties" ) ) { + for ( Entry entry : annotationMirror + .getElementValues() + .entrySet() ) { + if ( entry.getKey().getSimpleName().contentEquals( "value" ) ) { + constructorProperties = getArrayValues( entry.getValue() ); + break; + } + } + break; + } + } + + if ( constructorProperties == null ) { + Map constructorAccessors = new LinkedHashMap<>(); + List parameterBindings = new ArrayList<>( constructorParameters.size() ); + for ( Parameter constructorParameter : constructorParameters ) { + String parameterName = constructorParameter.getName(); + Element parameterElement = constructorParameter.getElement(); + Accessor constructorAccessor = createConstructorAccessor( parameterElement, parameterName ); + constructorAccessors.put( + parameterName, + constructorAccessor + ); + parameterBindings.add( ParameterBinding.fromTypeAndName( + constructorParameter.getType(), + constructorAccessor.getSimpleName() + ) ); + } + + return new ConstructorAccessor( parameterBindings, constructorAccessors ); + } + else if ( constructorProperties.size() != constructorParameters.size() ) { + ctx.getMessager().printMessage( + method.getExecutable(), + GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS, + type + ); + return null; + } + else { + Map constructorAccessors = new LinkedHashMap<>(); + List parameterBindings = new ArrayList<>( constructorProperties.size() ); + for ( int i = 0; i < constructorProperties.size(); i++ ) { + String parameterName = constructorProperties.get( i ); + Parameter constructorParameter = constructorParameters.get( i ); + Element parameterElement = constructorParameter.getElement(); + Accessor constructorAccessor = createConstructorAccessor( parameterElement, parameterName ); + constructorAccessors.put( + parameterName, + constructorAccessor + ); + parameterBindings.add( ParameterBinding.fromTypeAndName( + constructorParameter.getType(), + constructorAccessor.getSimpleName() + ) ); + } + + return new ConstructorAccessor( parameterBindings, constructorAccessors ); + } + } + + private Accessor createConstructorAccessor(Element element, String parameterName) { + String safeParameterName = Strings.getSafeVariableName( + parameterName, + existingVariableNames ); - if ( factoryMethod == null && returnTypeBuilder != null ) { - factoryMethod = ObjectFactoryMethodResolver.getBuilderFactoryMethod( method, returnTypeBuilder ); + existingVariableNames.add( safeParameterName ); + return new ParameterElementAccessor( element, safeParameterName ); + } + + private List getArrayValues(AnnotationValue av) { + + if ( av.getValue() instanceof List ) { + List result = new ArrayList<>(); + for ( AnnotationValue v : getValueAsList( av ) ) { + Object value = v.getValue(); + if ( value instanceof String ) { + result.add( (String) value ); + } + else { + return null; + } + } + return result; + } + else { + return null; } + } - return factoryMethod; + @SuppressWarnings("unchecked") + private List getValueAsList(AnnotationValue av) { + return (List) av.getValue(); } /** @@ -531,6 +803,7 @@ private boolean handleDefinedMappings(Type resultTypeToMap) { // remove the remaining name based properties for ( String handledTarget : handledTargets ) { unprocessedTargetProperties.remove( handledTarget ); + unprocessedConstructorProperties.remove( handledTarget ); unprocessedDefinedTargets.remove( handledTarget ); } @@ -672,7 +945,22 @@ else if ( !mapping.isIgnored() ) { // check the mapping options // its an ignored property mapping if ( mapping.isIgnored() ) { - propertyMapping = null; + if ( targetWriteAccessor != null && targetWriteAccessor.getAccessorType() == AccessorType.PARAMETER ) { + // Even though the property is ignored this is a constructor parameter. + // Therefore we have to initialize it + Type accessedType = ctx.getTypeFactory() + .getType( targetWriteAccessor.getAccessedType() ); + + propertyMapping = new JavaExpressionMappingBuilder() + .mappingContext( ctx ) + .sourceMethod( method ) + .javaExpression( accessedType.getNull() ) + .existingVariableNames( existingVariableNames ) + .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) + .dependsOn( mapping.getDependsOn() ) + .mirror( mapping.getMirror() ) + .build(); + } handledTargets.add( targetPropertyName ); } @@ -821,6 +1109,7 @@ private void applyPropertyNameBasedMapping(List sourceReference String targetPropertyName = sourceRef.getDeepestPropertyName(); Accessor targetPropertyWriteAccessor = unprocessedTargetProperties.remove( targetPropertyName ); + unprocessedConstructorProperties.remove( targetPropertyName ); if ( targetPropertyWriteAccessor == null ) { // TODO improve error message ctx.getMessager() @@ -1056,6 +1345,18 @@ private void reportErrorForUnmappedSourcePropertiesIfRequired() { } } + private static class ConstructorAccessor { + private final List parameterBindings; + private final Map constructorAccessors; + + private ConstructorAccessor( + List parameterBindings, + Map constructorAccessors) { + this.parameterBindings = parameterBindings; + this.constructorAccessors = constructorAccessors; + } + } + private BeanMappingMethod(Method method, Collection existingVariableNames, List propertyMappings, @@ -1082,15 +1383,30 @@ private BeanMappingMethod(Method method, // intialize constant mappings as all mappings, but take out the ones that can be contributed to a // parameter mapping. this.mappingsByParameter = new HashMap<>(); - this.constantMappings = new ArrayList<>( propertyMappings ); - for ( Parameter sourceParameter : getSourceParameters() ) { - ArrayList mappingsOfParameter = new ArrayList<>(); - mappingsByParameter.put( sourceParameter.getName(), mappingsOfParameter ); - for ( PropertyMapping mapping : propertyMappings ) { - if ( sourceParameter.getName().equals( mapping.getSourceBeanName() ) ) { - mappingsOfParameter.add( mapping ); - constantMappings.remove( mapping ); + this.constantMappings = new ArrayList<>( propertyMappings.size() ); + this.constructorMappingsByParameter = new LinkedHashMap<>(); + this.constructorConstantMappings = new ArrayList<>(); + Set sourceParameterNames = getSourceParameters().stream() + .map( Parameter::getName ) + .collect( Collectors.toSet() ); + for ( PropertyMapping mapping : propertyMappings ) { + if ( mapping.isConstructorMapping() ) { + if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) { + constructorMappingsByParameter.computeIfAbsent( + mapping.getSourceBeanName(), + key -> new ArrayList<>() + ).add( mapping ); } + else { + constructorConstantMappings.add( mapping ); + } + } + else if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) { + mappingsByParameter.computeIfAbsent( mapping.getSourceBeanName(), key -> new ArrayList<>() ) + .add( mapping ); + } + else { + constantMappings.add( mapping ); } } this.returnTypeToConstruct = returnTypeToConstruct; @@ -1100,15 +1416,28 @@ public List getConstantMappings() { return constantMappings; } + public List getConstructorConstantMappings() { + return constructorConstantMappings; + } + public List propertyMappingsByParameter(Parameter parameter) { // issues: #909 and #1244. FreeMarker has problem getting values from a map when the search key is size or value - return mappingsByParameter.get( parameter.getName() ); + return mappingsByParameter.getOrDefault( parameter.getName(), Collections.emptyList() ); + } + + public List constructorPropertyMappingsByParameter(Parameter parameter) { + // issues: #909 and #1244. FreeMarker has problem getting values from a map when the search key is size or value + return constructorMappingsByParameter.getOrDefault( parameter.getName(), Collections.emptyList() ); } public Type getReturnTypeToConstruct() { return returnTypeToConstruct; } + public boolean hasConstructorMappings() { + return !constructorMappingsByParameter.isEmpty() || !constructorConstantMappings.isEmpty(); + } + public MethodReference getFinalizerMethod() { return finalizerMethod; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index 04331c4e14..7ab9814e23 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -130,7 +130,7 @@ public Assignment build() { CollectionMappingStrategyGem cms = method.getOptions().getMapper().getCollectionMappingStrategy(); boolean targetImmutable = cms == CollectionMappingStrategyGem.TARGET_IMMUTABLE || targetReadAccessor == null; - if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) { + if ( targetAccessorType == AccessorType.SETTER || targetAccessorType.isFieldAssignment() ) { if ( result.isCallingUpdateMethod() && !targetImmutable ) { @@ -149,7 +149,7 @@ public Assignment build() { result, method.getThrownTypes(), factoryMethod, - targetAccessorType == AccessorType.FIELD, + targetAccessorType.isFieldAssignment(), targetType, true, nvpms == SET_TO_NULL && !targetType.isPrimitive(), @@ -165,7 +165,7 @@ else if ( method.isUpdateMethod() && !targetImmutable ) { nvcs, nvpms, ctx.getTypeFactory(), - targetAccessorType == AccessorType.FIELD + targetAccessorType.isFieldAssignment() ); } else if ( result.getType() == Assignment.AssignmentType.DIRECT || @@ -176,16 +176,18 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT || method.getThrownTypes(), targetType, ctx.getTypeFactory(), - targetAccessorType == AccessorType.FIELD + targetAccessorType.isFieldAssignment() ); } else { + //TODO init default value + // target accessor is setter, so wrap the setter in setter map/ collection handling result = new SetterWrapperForCollectionsAndMaps( result, method.getThrownTypes(), targetType, - targetAccessorType == AccessorType.FIELD + targetAccessorType.isFieldAssignment() ); } } @@ -203,7 +205,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT || result, method.getThrownTypes(), targetType, - targetAccessorType == AccessorType.FIELD + targetAccessorType.isFieldAssignment() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java index dc2bf52182..58c4ba890a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -57,6 +58,7 @@ public class MethodReference extends ModelElement implements Assignment { private final List parameterBindings; private final Parameter providingParameter; private final boolean isStatic; + private final boolean isConstructor; /** * Creates a new reference to the given method. @@ -91,6 +93,7 @@ protected MethodReference(Method method, MapperReference declaringMapper, Parame this.definingType = method.getDefiningType(); this.isStatic = method.isStatic(); this.name = method.getName(); + this.isConstructor = false; } private MethodReference(BuiltInMethod method, ConversionContext contextParam) { @@ -106,6 +109,7 @@ private MethodReference(BuiltInMethod method, ConversionContext contextParam) { this.parameterBindings = ParameterBinding.fromParameters( method.getParameters() ); this.isStatic = method.isStatic(); this.name = method.getName(); + this.isConstructor = false; } private MethodReference(String name, Type definingType, boolean isStatic) { @@ -121,6 +125,37 @@ private MethodReference(String name, Type definingType, boolean isStatic) { this.parameterBindings = Collections.emptyList(); this.providingParameter = null; this.isStatic = isStatic; + this.isConstructor = false; + } + + private MethodReference(Type definingType, List parameterBindings) { + this.name = null; + this.definingType = definingType; + this.sourceParameters = Collections.emptyList(); + this.returnType = null; + this.declaringMapper = null; + this.thrownTypes = Collections.emptyList(); + this.isUpdateMethod = false; + this.contextParam = null; + this.parameterBindings = parameterBindings; + this.providingParameter = null; + this.isStatic = false; + this.isConstructor = true; + + if ( parameterBindings.isEmpty() ) { + this.importTypes = Collections.emptySet(); + } + else { + Set imported = new LinkedHashSet<>(); + + for ( ParameterBinding binding : parameterBindings ) { + imported.add( binding.getType() ); + } + + imported.add( definingType ); + + this.importTypes = Collections.unmodifiableSet( imported ); + } } public MapperReference getDeclaringMapper() { @@ -271,6 +306,10 @@ public boolean isStatic() { return isStatic; } + public boolean isConstructor() { + return isConstructor; + } + public List getParameterBindings() { return parameterBindings; } @@ -341,6 +380,10 @@ public static MethodReference forMethodCall(String methodName) { return new MethodReference( methodName, null, false ); } + public static MethodReference forConstructorInvocation(Type type, List parameterBindings) { + return new MethodReference( type, parameterBindings ); + } + @Override public String toString() { String mapper = declaringMapper != null ? declaringMapper.getType().getName() : ""; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java index 4687a626d1..fcd8d70d52 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java @@ -42,6 +42,9 @@ public Set getImportTypes() { types.addAll( getReturnType().getImplementationType().getImportTypes() ); } } + else if ( factoryMethod != null ) { + types.addAll( factoryMethod.getImportTypes() ); + } return types; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index f4b39ab1a6..34d2944276 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -67,16 +67,12 @@ public static MethodReference getFactoryMethod( Method method, MappingBuilderContext ctx) { - MethodSelectors selectors = - new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() ); - - List> matchingFactoryMethods = - selectors.getMatchingMethods( - method, - getAllAvailableMethods( method, ctx.getSourceModel() ), - java.util.Collections.emptyList(), - alternativeTarget, - SelectionCriteria.forFactoryMethods( selectionParameters ) ); + List> matchingFactoryMethods = getMatchingFactoryMethods( + method, + alternativeTarget, + selectionParameters, + ctx + ); if (matchingFactoryMethods.isEmpty()) { return null; @@ -94,6 +90,11 @@ public static MethodReference getFactoryMethod( Method method, SelectedMethod matchingFactoryMethod = first( matchingFactoryMethods ); + return getFactoryMethodReference( method, matchingFactoryMethod, ctx ); + } + + public static MethodReference getFactoryMethodReference(Method method, + SelectedMethod matchingFactoryMethod, MappingBuilderContext ctx) { Parameter providingParameter = method.getContextProvidedMethods().getParameterForProvidedMethod( matchingFactoryMethod.getMethod() ); @@ -115,6 +116,22 @@ public static MethodReference getFactoryMethod( Method method, } } + public static List> getMatchingFactoryMethods( Method method, + Type alternativeTarget, + SelectionParameters selectionParameters, + MappingBuilderContext ctx) { + + MethodSelectors selectors = + new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() ); + + return selectors.getMatchingMethods( + method, + getAllAvailableMethods( method, ctx.getSourceModel() ), + java.util.Collections.emptyList(), + alternativeTarget, + SelectionCriteria.forFactoryMethods( selectionParameters ) ); + } + public static MethodReference getBuilderFactoryMethod(Method method, BuilderType builder ) { if ( builder == null ) { return null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 1558fc7f8a..571c3530f1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -70,6 +70,7 @@ public class PropertyMapping extends ModelElement { private final Assignment assignment; private final Set dependsOn; private final Assignment defaultValueAssignment; + private final boolean constructorMapping; @SuppressWarnings("unchecked") private static class MappingBuilderBase> extends AbstractBaseBuilder { @@ -126,7 +127,7 @@ public T existingVariableNames(Set existingVariableNames) { } protected boolean isFieldAssignment() { - return targetWriteAccessorType == AccessorType.FIELD; + return targetWriteAccessorType.isFieldAssignment(); } } @@ -278,7 +279,8 @@ else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.get targetType, assignment, dependsOn, - getDefaultValueAssignment( assignment ) + getDefaultValueAssignment( assignment ), + targetWriteAccessorType == AccessorType.PARAMETER ); } @@ -376,7 +378,7 @@ private Assignment assignToPlain(Type targetType, AccessorType targetAccessorTyp Assignment result; - if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) { + if ( targetAccessorType == AccessorType.SETTER || targetAccessorType.isFieldAssignment() ) { result = assignToPlainViaSetter( targetType, rightHandSide ); } else { @@ -473,6 +475,7 @@ private Assignment assignToCollection(Type targetType, AccessorType targetAccess private Assignment assignToArray(Type targetType, Assignment rightHandSide) { Type arrayType = ctx.getTypeFactory().getType( Arrays.class ); + //TODO init default value return new ArrayCopyWrapper( rightHandSide, targetPropertyName, @@ -803,7 +806,7 @@ public PropertyMapping build() { if ( assignment != null ) { if ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER || - targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) { + targetWriteAccessor.getAccessorType().isFieldAssignment() ) { // target accessor is setter, so decorate assignment as setter if ( assignment.isCallingUpdateMethod() ) { @@ -874,7 +877,8 @@ else if ( errorMessageDetails == null ) { targetType, assignment, dependsOn, - null + null, + targetWriteAccessorType == AccessorType.PARAMETER ); } @@ -919,7 +923,7 @@ public PropertyMapping build() { Assignment assignment = new SourceRHS( javaExpression, null, existingVariableNames, "" ); if ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER || - targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) { + targetWriteAccessor.getAccessorType().isFieldAssignment() ) { // setter, so wrap in setter assignment = new SetterWrapper( assignment, method.getThrownTypes(), isFieldAssignment() ); } @@ -939,7 +943,8 @@ public PropertyMapping build() { targetType, assignment, dependsOn, - null + null, + targetWriteAccessorType == AccessorType.PARAMETER ); } @@ -947,18 +952,19 @@ public PropertyMapping build() { // Constructor for creating mappings of constant expressions. private PropertyMapping(String name, String targetWriteAccessorName, - ValueProvider targetReadAccessorProvider, - Type targetType, Assignment propertyAssignment, - Set dependsOn, Assignment defaultValueAssignment ) { + ValueProvider targetReadAccessorProvider, + Type targetType, Assignment propertyAssignment, + Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) { this( name, null, targetWriteAccessorName, targetReadAccessorProvider, - targetType, propertyAssignment, dependsOn, defaultValueAssignment + targetType, propertyAssignment, dependsOn, defaultValueAssignment, + constructorMapping ); } private PropertyMapping(String name, String sourceBeanName, String targetWriteAccessorName, - ValueProvider targetReadAccessorProvider, Type targetType, - Assignment assignment, - Set dependsOn, Assignment defaultValueAssignment) { + ValueProvider targetReadAccessorProvider, Type targetType, + Assignment assignment, + Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) { this.name = name; this.sourceBeanName = sourceBeanName; this.targetWriteAccessorName = targetWriteAccessorName; @@ -968,6 +974,7 @@ private PropertyMapping(String name, String sourceBeanName, String targetWriteAc this.assignment = assignment; this.dependsOn = dependsOn != null ? dependsOn : Collections.emptySet(); this.defaultValueAssignment = defaultValueAssignment; + this.constructorMapping = constructorMapping; } /** @@ -1001,6 +1008,10 @@ public Assignment getDefaultValueAssignment() { return defaultValueAssignment; } + public boolean isConstructorMapping() { + return constructorMapping; + } + @Override public Set getImportTypes() { if ( defaultValueAssignment == null ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java index 958725c966..2fd2b69eeb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java @@ -9,6 +9,7 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import javax.lang.model.element.Element; import javax.lang.model.element.VariableElement; import org.mapstruct.ap.internal.gem.ContextGem; @@ -23,6 +24,7 @@ */ public class Parameter extends ModelElement { + private final Element element; private final String name; private final String originalName; private final Type type; @@ -32,8 +34,20 @@ public class Parameter extends ModelElement { private final boolean varArgs; + private Parameter(Element element, Type type, boolean varArgs) { + this.element = element; + this.name = element.getSimpleName().toString(); + this.originalName = name; + this.type = type; + this.mappingTarget = MappingTargetGem.instanceOn( element ) != null; + this.targetType = TargetTypeGem.instanceOn( element ) != null; + this.mappingContext = ContextGem.instanceOn( element ) != null; + this.varArgs = varArgs; + } + private Parameter(String name, Type type, boolean mappingTarget, boolean targetType, boolean mappingContext, boolean varArgs) { + this.element = null; this.name = name; this.originalName = name; this.type = type; @@ -47,6 +61,10 @@ public Parameter(String name, Type type) { this( name, type, false, false, false, false ); } + public Element getElement() { + return element; + } + public String getName() { return name; } @@ -115,11 +133,8 @@ public boolean equals(Object o) { public static Parameter forElementAndType(VariableElement element, Type parameterType, boolean isVarArgs) { return new Parameter( - element.getSimpleName().toString(), + element, parameterType, - MappingTargetGem.instanceOn( element ) != null, - TargetTypeGem.instanceOn( element ) != null, - ContextGem.instanceOn( element ) != null, isVarArgs ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java index 5f807578e8..36e4d3e864 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java @@ -111,6 +111,17 @@ public static List fromParameters(List parameters) return result; } + public static ParameterBinding fromTypeAndName(Type parameterType, String parameterName) { + return new ParameterBinding( + parameterType, + parameterName, + false, + false, + false, + null + ); + } + /** * @param classTypeOf the type representing {@code Class} for the target type {@code X} * @return a parameter binding representing a target type parameter diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 52a076270b..0974fe3477 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -90,14 +90,16 @@ public class Type extends ModelElement implements Comparable { private List allMethods = null; private List allFields = null; + private List recordComponents = null; private List setters = null; private List adders = null; private List alternativeTargetAccessors = null; + private Map constructorAccessors = null; private Type boundingBase = null; - private Boolean hasEmptyAccessibleConstructor; + private Boolean hasAccessibleConstructor; private final Filters filters; @@ -290,6 +292,10 @@ public boolean isJavaLangType() { return packageName != null && packageName.startsWith( "java." ); } + public boolean isRecord() { + return typeElement.getKind().name().equals( "RECORD" ); + } + /** * Whether this type is a sub-type of {@link java.util.stream.Stream}. * @@ -498,7 +504,7 @@ public Map getPropertyReadAccessors() { } } - Map recordAccessors = filters.recordsIn( typeElement ); + Map recordAccessors = filters.recordAccessorsIn( getRecordComponents() ); for ( Map.Entry recordEntry : recordAccessors.entrySet() ) { modifiableGetters.putIfAbsent( recordEntry.getKey(), recordEntry.getValue() ); } @@ -600,6 +606,14 @@ else if ( candidate.getAccessorType() == AccessorType.FIELD && ( Executables.is return result; } + public List getRecordComponents() { + if ( recordComponents == null ) { + recordComponents = filters.recordComponentsIn( typeElement ); + } + + return recordComponents; + } + private Type determinePreferredType(Accessor readAccessor) { if ( readAccessor != null ) { return typeFactory.getReturnType( (DeclaredType) typeMirror, readAccessor ); @@ -613,7 +627,7 @@ private Type determineTargetType(Accessor candidate) { return parameter.getType(); } else if ( candidate.getAccessorType() == AccessorType.GETTER - || candidate.getAccessorType() == AccessorType.FIELD ) { + || candidate.getAccessorType().isFieldAssignment() ) { return typeFactory.getReturnType( (DeclaredType) typeMirror, candidate ); } return null; @@ -636,11 +650,12 @@ private List getAllFields() { } private String getPropertyName(Accessor accessor ) { - if ( accessor.getAccessorType() == AccessorType.FIELD ) { - return accessorNaming.getPropertyName( (VariableElement) accessor.getElement() ); + Element accessorElement = accessor.getElement(); + if ( accessorElement instanceof ExecutableElement ) { + return accessorNaming.getPropertyName( (ExecutableElement) accessorElement ); } else { - return accessorNaming.getPropertyName( (ExecutableElement) accessor.getElement() ); + return accessor.getSimpleName(); } } @@ -1009,20 +1024,18 @@ public Type getTypeBound() { return boundingBase; } - public boolean hasEmptyAccessibleConstructor() { - - if ( this.hasEmptyAccessibleConstructor == null ) { - hasEmptyAccessibleConstructor = false; + public boolean hasAccessibleConstructor() { + if ( hasAccessibleConstructor == null ) { + hasAccessibleConstructor = false; List constructors = ElementFilter.constructorsIn( typeElement.getEnclosedElements() ); for ( ExecutableElement constructor : constructors ) { - if ( !constructor.getModifiers().contains( Modifier.PRIVATE ) - && constructor.getParameters().isEmpty() ) { - hasEmptyAccessibleConstructor = true; + if ( !constructor.getModifiers().contains( Modifier.PRIVATE ) ) { + hasAccessibleConstructor = true; break; } } } - return hasEmptyAccessibleConstructor; + return hasAccessibleConstructor; } /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 594c2c7f70..a74c02fd7d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -50,7 +50,6 @@ import org.mapstruct.ap.internal.util.RoundContext; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.AccessorType; import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; import org.mapstruct.ap.spi.BuilderInfo; import org.mapstruct.ap.spi.MoreThanOneBuilderCreationMethodException; @@ -360,7 +359,7 @@ public TypeMirror getMethodType(DeclaredType includingType, Element method) { } public Parameter getSingleParameter(DeclaredType includingType, Accessor method) { - if ( method.getAccessorType() == AccessorType.FIELD ) { + if ( method.getAccessorType().isFieldAssignment() ) { return null; } ExecutableElement executable = (ExecutableElement) method.getElement(); @@ -376,11 +375,15 @@ public Parameter getSingleParameter(DeclaredType includingType, Accessor method) public List getParameters(DeclaredType includingType, Accessor accessor) { ExecutableElement method = (ExecutableElement) accessor.getElement(); - TypeMirror methodType = getMethodType( includingType, accessor.getElement() ); + return getParameters( includingType, method ); + } + + public List getParameters(DeclaredType includingType, ExecutableElement method) { + ExecutableType methodType = getMethodType( includingType, method ); if ( method == null || methodType.getKind() != TypeKind.EXECUTABLE ) { return new ArrayList<>(); } - return getParameters( (ExecutableType) methodType, method ); + return getParameters( methodType, method ); } public List getParameters(ExecutableType methodType, ExecutableElement method) { @@ -433,7 +436,7 @@ public List getThrownTypes(ExecutableType method) { } public List getThrownTypes(Accessor accessor) { - if (accessor.getAccessorType() == AccessorType.FIELD) { + if (accessor.getAccessorType().isFieldAssignment()) { return new ArrayList<>(); } return extractTypes( ( (ExecutableElement) accessor.getElement() ).getThrownTypes() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java index f060385a04..0f17946f72 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java @@ -7,7 +7,6 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -68,10 +67,6 @@ public String getPropertyName(ExecutableElement executable) { return accessorNamingStrategy.getPropertyName( executable ); } - public String getPropertyName(VariableElement variable) { - return variable.getSimpleName().toString(); - } - /** * @param adderMethod the adder method * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java index 6981a1ebb5..f4d52d7095 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java @@ -7,6 +7,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -23,7 +24,7 @@ import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; -import org.mapstruct.ap.internal.util.accessor.VariableElementAccessor; +import org.mapstruct.ap.internal.util.accessor.FieldElementAccessor; import static org.mapstruct.ap.internal.util.Collections.first; import static org.mapstruct.ap.internal.util.accessor.AccessorType.ADDER; @@ -75,13 +76,25 @@ public List getterMethodsIn(List elements) { .collect( Collectors.toCollection( LinkedList::new ) ); } - public Map recordsIn(TypeElement typeElement) { - if ( RECORD_COMPONENTS_METHOD == null || RECORD_COMPONENT_ACCESSOR_METHOD == null ) { + @SuppressWarnings("unchecked") + public List recordComponentsIn(TypeElement typeElement) { + if ( RECORD_COMPONENTS_METHOD == null ) { + return java.util.Collections.emptyList(); + } + + try { + return (List) RECORD_COMPONENTS_METHOD.invoke( typeElement ); + } + catch ( IllegalAccessException | InvocationTargetException e ) { + return java.util.Collections.emptyList(); + } + } + + public Map recordAccessorsIn(Collection recordComponents) { + if ( RECORD_COMPONENT_ACCESSOR_METHOD == null ) { return java.util.Collections.emptyMap(); } try { - @SuppressWarnings("unchecked") - List recordComponents = (List) RECORD_COMPONENTS_METHOD.invoke( typeElement ); Map recordAccessors = new LinkedHashMap<>(); for ( Element recordComponent : recordComponents ) { ExecutableElement recordExecutableElement = @@ -109,7 +122,7 @@ private TypeMirror getReturnType(ExecutableElement executableElement) { public List fieldsIn(List accessors) { return accessors.stream() .filter( Fields::isFieldAccessor ) - .map( VariableElementAccessor::new ) + .map( FieldElementAccessor::new ) .collect( Collectors.toCollection( LinkedList::new ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index e4b199adfb..d2cb9857ef 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -110,12 +110,14 @@ public enum Message { GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ), GENERAL_AMBIGIOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s." ), GENERAL_AMBIGIOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s." ), + GENERAL_AMBIGIOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ), + GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ), GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ), GENERAL_VALID_DATE( "Given date format \"%s\" is valid.", Diagnostic.Kind.NOTE ), GENERAL_INVALID_DATE( "Given date format \"%s\" is invalid. Message: \"%s\"." ), GENERAL_JODA_NOT_ON_CLASSPATH( "Cannot validate Joda dateformat, no Joda on classpath. Consider adding Joda to the annotation processorpath.", Diagnostic.Kind.WARNING ), GENERAL_NOT_ALL_FORGED_CREATED( "Internal Error in creation of Forged Methods, it was expected all Forged Methods to finished with creation, but %s did not" ), - GENERAL_NO_SUITABLE_CONSTRUCTOR( "%s does not have an accessible parameterless constructor." ), + GENERAL_NO_SUITABLE_CONSTRUCTOR( "%s does not have an accessible constructor." ), GENERAL_NO_QUALIFYING_METHOD( "No qualifying method found for qualifiers: %s and / or qualifying names: %s" ), BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD( "More than one builder creation method for \"%s\". Found methods: \"%s\". Builder will not be used. Consider implementing a custom BuilderProvider SPI.", Diagnostic.Kind.WARNING ), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java index 0f64c2b375..bbd73943db 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java @@ -6,7 +6,6 @@ package org.mapstruct.ap.internal.util; import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.AccessorType; /** * This a wrapper class which provides the value that needs to be used in the models. @@ -46,7 +45,7 @@ public static ValueProvider of(Accessor accessor) { return null; } String value = accessor.getSimpleName(); - if ( accessor.getAccessorType() != AccessorType.FIELD ) { + if ( !accessor.getAccessorType().isFieldAssignment() ) { value += "()"; } return new ValueProvider( value ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java index 917d1a216b..314302a14e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java @@ -8,9 +8,8 @@ import java.util.Set; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.VariableElement; import javax.lang.model.element.Modifier; -import javax.lang.model.element.Name; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java index 9348de878c..23448a9e98 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java @@ -7,9 +7,14 @@ public enum AccessorType { + PARAMETER, FIELD, GETTER, SETTER, ADDER, PRESENCE_CHECKER; + + public boolean isFieldAssignment() { + return this == FIELD || this == PARAMETER; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/VariableElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/FieldElementAccessor.java similarity index 83% rename from processor/src/main/java/org/mapstruct/ap/internal/util/accessor/VariableElementAccessor.java rename to processor/src/main/java/org/mapstruct/ap/internal/util/accessor/FieldElementAccessor.java index 8b1b01dbd6..5174900532 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/VariableElementAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/FieldElementAccessor.java @@ -13,9 +13,9 @@ * * @author Filip Hrisafov */ -public class VariableElementAccessor extends AbstractAccessor { +public class FieldElementAccessor extends AbstractAccessor { - public VariableElementAccessor(VariableElement element) { + public FieldElementAccessor(VariableElement element) { super( element ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java new file mode 100644 index 0000000000..e1877cb0bb --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java @@ -0,0 +1,46 @@ +/* + * 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.internal.util.accessor; + +import javax.lang.model.element.Element; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; + +/** + * An {@link Accessor} that wraps a {@link VariableElement}. + * + * @author Filip Hrisafov + */ +public class ParameterElementAccessor extends AbstractAccessor { + + protected final String name; + + public ParameterElementAccessor(Element element, String name) { + super( element ); + this.name = name; + } + + @Override + public String getSimpleName() { + return name != null ? name : super.getSimpleName(); + } + + @Override + public TypeMirror getAccessedType() { + return element.asType(); + } + + @Override + public String toString() { + return element.toString(); + } + + @Override + public AccessorType getAccessorType() { + return AccessorType.PARAMETER; + } + +} diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index b8ffd0a179..45825b4fd6 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -25,7 +25,62 @@ <#if !existingInstanceMapping> - <@includeModel object=returnTypeToConstruct/> ${resultName} = <#if factoryMethod??><@includeModel object=factoryMethod targetType=returnTypeToConstruct/><#else>new <@includeModel object=returnTypeToConstruct/>(); + <#if hasConstructorMappings()> + <#if (sourceParameters?size > 1)> + <#list sourceParametersExcludingPrimitives as sourceParam> + <#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)> + <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> + <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName}; + + if ( ${sourceParam.name} != null ) { + <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> + <@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> + + } + else { + <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> + ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; + + } + + + <#list sourcePrimitiveParameters as sourceParam> + <#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)> + <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> + <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName}; + <@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> + + + + <#else> + <#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping> + <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName}; + + <#if mapNullToDefault>if ( ${sourceParameters[0].name} != null ) { + <#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping> + <@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> + + <#if mapNullToDefault> + } + else { + <#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping> + ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; + + } + + + <#list constructorConstantMappings as constantMapping> + + <@compress single_line=true> + <@includeModel object=constantMapping.targetType /> <@includeModel object=constantMapping existingInstanceMapping=existingInstanceMapping/> + + + + + <@includeModel object=returnTypeToConstruct/> ${resultName} = <@includeModel object=factoryMethod targetType=returnTypeToConstruct/>; + <#else > + <@includeModel object=returnTypeToConstruct/> ${resultName} = <#if factoryMethod??><@includeModel object=factoryMethod targetType=returnTypeToConstruct/><#else>new <@includeModel object=returnTypeToConstruct/>(); + <#list beforeMappingReferencesWithMappingTarget as callback> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl index 30a830bc57..d0ee7bef3b 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl @@ -16,6 +16,8 @@ <#-- method is referenced java8 static method in the mapper to implement (interface) --> <#elseif static> <@includeModel object=definingType/>.<@methodCall/> + <#elseif constructor> + new <@includeModel object=definingType/><#if (parameterBindings?size > 0)>( <@arguments/> )<#else>() <#else> <@methodCall/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/AdderWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/AdderWrapper.ftl index 361fedaa58..9160a62d9e 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/AdderWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/AdderWrapper.ftl @@ -11,7 +11,7 @@ <@lib.sourceLocalVarAssignment/> <@lib.handleSourceReferenceNullCheck> for ( <@includeModel object=adderType.typeBound/> ${sourceLoopVarName} : <#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference} ) { - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/>; + <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/>; } \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ArrayCopyWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ArrayCopyWrapper.ftl index edb0d3cad3..2ce8736f96 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ArrayCopyWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ArrayCopyWrapper.ftl @@ -10,6 +10,6 @@ <@lib.handleExceptions> <@lib.sourceLocalVarAssignment/> <@lib.handleSourceReferenceNullCheck> - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite>Arrays.copyOf( ${sourceLocalVarName}, ${sourceLocalVarName}.length ); + <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite>Arrays.copyOf( ${sourceLocalVarName}, ${sourceLocalVarName}.length ); \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapper.ftl index 613e8f6abd..c8f18b1c7f 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapper.ftl @@ -10,6 +10,6 @@ <@lib.handleExceptions> <@lib.sourceLocalVarAssignment/> <@lib.handleSourceReferenceNullCheck> - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/>; + <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/>; \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.ftl index 122c76bc62..beb6bb1f3a 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.ftl @@ -9,5 +9,5 @@ <#import "../macro/CommonMacros.ftl" as lib> <@lib.sourceLocalVarAssignment/> <@lib.handleExceptions> - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/>; + <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/>; \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl index c26557af94..92e2e9d09d 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl @@ -16,8 +16,12 @@ --> <#macro callTargetWriteAccessor> <@lib.handleLocalVarNullCheck needs_explicit_local_var=directAssignment> - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if directAssignment><@wrapLocalVarInCollectionInitializer/><#else><@lib.handleWithAssignmentOrNullCheckVar/>; + <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if directAssignment><@wrapLocalVarInCollectionInitializer/><#else><@lib.handleWithAssignmentOrNullCheckVar/>; + <#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && !ext.targetBeanName?has_content>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleLocalVarNullCheck --> + ${ext.targetWriteAccessorName}<@lib.handleWrite>null; + } + <#-- wraps the local variable in a collection initializer (new collection, or EnumSet.copyOf) diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index 96ecd953b9..de456ca773 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -40,7 +40,7 @@ } <#elseif setExplicitlyToDefault || setExplicitlyToNull> else { - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>null; + <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>null; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java index 6cf0ba401f..0cf59e7267 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java @@ -65,12 +65,7 @@ public void factoryMethodWithSourceParamIsChosen() { ".lang.Class clazz), org.mapstruct.ap.test.bugs._1242" + ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB(@TargetType java.lang" + ".Class clazz), org.mapstruct.ap.test.bugs._1242" + - ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB()."), - @Diagnostic(type = ErroneousIssue1242MapperMultipleSources.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 20, - message = "org.mapstruct.ap.test.bugs._1242.TargetB does not have an accessible parameterless " + - "constructor.") + ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB().") }) public void ambiguousMethodErrorForTwoFactoryMethodsWithSourceParam() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java index 4d875017f8..b4ce1447f8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java @@ -33,8 +33,7 @@ public class Issue1283Test { @Diagnostic(type = ErroneousInverseTargetHasNoSuitableConstructorMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22L, - message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible parameterless " + - "constructor." + message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible constructor." ) } ) @@ -49,8 +48,7 @@ public void inheritInverseConfigurationReturnTypeHasNoSuitableConstructor() { @Diagnostic(type = ErroneousTargetHasNoSuitableConstructorMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 18L, - message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible parameterless " + - "constructor." + message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible constructor." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Source.java index a34c8d4a53..227296ad25 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Source.java @@ -12,7 +12,7 @@ public class Source { private String source; - public Source(String source) { + private Source(String source) { this.source = source; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/ConstructorProperties.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/ConstructorProperties.java new file mode 100644 index 0000000000..affbded7ff --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/ConstructorProperties.java @@ -0,0 +1,23 @@ +/* + * 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.constructor; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Filip Hrisafov + */ +@Documented +@Target(ElementType.CONSTRUCTOR) +@Retention(RetentionPolicy.SOURCE) +public @interface ConstructorProperties { + + String[] value(); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/Default.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/Default.java new file mode 100644 index 0000000000..96b30c02ea --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/Default.java @@ -0,0 +1,21 @@ +/* + * 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.constructor; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Filip Hrisafov + */ +@Documented +@Target(ElementType.CONSTRUCTOR) +@Retention(RetentionPolicy.SOURCE) +public @interface Default { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/Person.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/Person.java new file mode 100644 index 0000000000..8fc9e7ce30 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/Person.java @@ -0,0 +1,55 @@ +/* + * 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.constructor; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class Person { + + private final String name; + private final int age; + private final String job; + private final String city; + private final String address; + private final List children; + + public Person(String name, int age, String job, String city, String address, + List children) { + this.name = name; + this.age = age; + this.job = job; + this.city = city; + this.address = address; + this.children = children; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public String getJob() { + return job; + } + + public String getCity() { + return city; + } + + public String getAddress() { + return address; + } + + public List getChildren() { + return children; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/PersonDto.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/PersonDto.java new file mode 100644 index 0000000000..0b2cba09cd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/PersonDto.java @@ -0,0 +1,69 @@ +/* + * 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.constructor; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class PersonDto { + + private String name; + private int age; + private String job; + private String city; + private String address; + private List children; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getJob() { + return job; + } + + public void setJob(String job) { + this.job = job; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/PersonWithConstructorProperties.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/PersonWithConstructorProperties.java new file mode 100644 index 0000000000..8419442cf1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/PersonWithConstructorProperties.java @@ -0,0 +1,58 @@ +/* + * 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.constructor.constructorproperties; + +import java.util.List; + +import org.mapstruct.ap.test.constructor.ConstructorProperties; + +/** + * @author Filip Hrisafov + */ +public class PersonWithConstructorProperties { + + private final String name; + private final int age; + private final String job; + private final String city; + private final String address; + private final List children; + + @ConstructorProperties({"name", "age", "job", "city", "address", "children"}) + public PersonWithConstructorProperties(String var1, int var2, String var3, String var4, String var5, + List var6) { + this.name = var1; + this.age = var2; + this.job = var3; + this.city = var4; + this.address = var5; + this.children = var6; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public String getJob() { + return job; + } + + public String getCity() { + return city; + } + + public String getAddress() { + return address; + } + + public List getChildren() { + return children; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesMapper.java new file mode 100644 index 0000000000..3d32d8b31c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesMapper.java @@ -0,0 +1,30 @@ +/* + * 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.constructor.constructorproperties; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SimpleConstructorPropertiesMapper { + + SimpleConstructorPropertiesMapper INSTANCE = Mappers.getMapper( SimpleConstructorPropertiesMapper.class ); + + PersonWithConstructorProperties map(PersonDto dto); + + @Mapping(target = "age", constant = "25") + @Mapping(target = "job", constant = "Software Developer") + PersonWithConstructorProperties mapWithConstants(PersonDto dto); + + @Mapping(target = "age", expression = "java(25 - 5)") + @Mapping(target = "job", expression = "java(\"Software Developer\".toLowerCase())") + PersonWithConstructorProperties mapWithExpression(PersonDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesTest.java new file mode 100644 index 0000000000..b58d55f1ee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesTest.java @@ -0,0 +1,90 @@ +/* + * 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.constructor.constructorproperties; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.ConstructorProperties; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + ConstructorProperties.class, + PersonWithConstructorProperties.class, + PersonDto.class, + SimpleConstructorPropertiesMapper.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class SimpleConstructorPropertiesTest { + + @Test + public void mapDefault() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + PersonWithConstructorProperties target = SimpleConstructorPropertiesMapper.INSTANCE.map( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 30 ); + assertThat( target.getJob() ).isEqualTo( "Software Engineer" ); + assertThat( target.getCity() ).isEqualTo( "Zurich" ); + assertThat( target.getAddress() ).isEqualTo( "Plaza 1" ); + assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); + } + + @Test + public void mapWithConstants() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + PersonWithConstructorProperties target = SimpleConstructorPropertiesMapper.INSTANCE.mapWithConstants( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 25 ); + assertThat( target.getJob() ).isEqualTo( "Software Developer" ); + assertThat( target.getCity() ).isEqualTo( "Zurich" ); + assertThat( target.getAddress() ).isEqualTo( "Plaza 1" ); + assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); + } + + @Test + public void mapWithExpressions() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + PersonWithConstructorProperties target = SimpleConstructorPropertiesMapper.INSTANCE.mapWithExpression( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 20 ); + assertThat( target.getJob() ).isEqualTo( "software developer" ); + assertThat( target.getCity() ).isEqualTo( "Zurich" ); + assertThat( target.getAddress() ).isEqualTo( "Plaza 1" ); + assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/PersonWithDefaultAnnotatedConstructor.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/PersonWithDefaultAnnotatedConstructor.java new file mode 100644 index 0000000000..190ab818ea --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/PersonWithDefaultAnnotatedConstructor.java @@ -0,0 +1,35 @@ +/* + * 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.constructor.defaultannotated; + +import org.mapstruct.ap.test.constructor.Default; + +/** + * @author Filip Hrisafov + */ +public class PersonWithDefaultAnnotatedConstructor { + + private final String name; + private final int age; + + public PersonWithDefaultAnnotatedConstructor(String name) { + this( name, -1 ); + } + + @Default + public PersonWithDefaultAnnotatedConstructor(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorMapper.java new file mode 100644 index 0000000000..6934c34fe4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorMapper.java @@ -0,0 +1,29 @@ +/* + * 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.constructor.defaultannotated; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SimpleDefaultAnnotatedConstructorMapper { + + SimpleDefaultAnnotatedConstructorMapper INSTANCE = + Mappers.getMapper( SimpleDefaultAnnotatedConstructorMapper.class ); + + PersonWithDefaultAnnotatedConstructor map(PersonDto dto); + + @Mapping(target = "age", constant = "25") + PersonWithDefaultAnnotatedConstructor mapWithConstants(PersonDto dto); + + @Mapping(target = "age", expression = "java(25 - 5)") + PersonWithDefaultAnnotatedConstructor mapWithExpression(PersonDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorTest.java new file mode 100644 index 0000000000..ac447d7878 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorTest.java @@ -0,0 +1,80 @@ +/* + * 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.constructor.defaultannotated; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.Default; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Default.class, + PersonWithDefaultAnnotatedConstructor.class, + PersonDto.class, + SimpleDefaultAnnotatedConstructorMapper.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class SimpleDefaultAnnotatedConstructorTest { + + @Test + public void mapDefault() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + PersonWithDefaultAnnotatedConstructor target = SimpleDefaultAnnotatedConstructorMapper.INSTANCE.map( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 30 ); + } + + @Test + public void mapWithConstants() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + PersonWithDefaultAnnotatedConstructor target = + SimpleDefaultAnnotatedConstructorMapper.INSTANCE.mapWithConstants( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 25 ); + } + + @Test + public void mapWithExpressions() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + PersonWithDefaultAnnotatedConstructor target = + SimpleDefaultAnnotatedConstructorMapper.INSTANCE.mapWithExpression( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 20 ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousAmbiguousConstructorsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousAmbiguousConstructorsMapper.java new file mode 100644 index 0000000000..afa37ea77b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousAmbiguousConstructorsMapper.java @@ -0,0 +1,33 @@ +/* + * 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.constructor.erroneous; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.constructor.PersonDto; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousAmbiguousConstructorsMapper { + + PersonWithMultipleConstructors map(PersonDto dto); + + class PersonWithMultipleConstructors { + + private final String name; + private final int age; + + public PersonWithMultipleConstructors(String name) { + this( name, -1 ); + } + + public PersonWithMultipleConstructors(String name, int age) { + this.name = name; + this.age = age; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorPropertiesMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorPropertiesMapper.java new file mode 100644 index 0000000000..705421724e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorPropertiesMapper.java @@ -0,0 +1,31 @@ +/* + * 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.constructor.erroneous; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.test.constructor.ConstructorProperties; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousConstructorPropertiesMapper { + + PersonWithIncorrectConstructorProperties map(PersonDto dto); + + class PersonWithIncorrectConstructorProperties { + + private final String name; + private final int age; + + @ConstructorProperties({ "name" }) + public PersonWithIncorrectConstructorProperties(String name, int age) { + this.name = name; + this.age = age; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java new file mode 100644 index 0000000000..eac6188ab6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.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.constructor.erroneous; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.ConstructorProperties; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Filip Hrisafov + */ +@RunWith(AnnotationProcessorTestRunner.class) +public class ErroneousConstructorTest { + + @Test + @WithClasses({ + ErroneousAmbiguousConstructorsMapper.class, + PersonDto.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { + @Diagnostic( + type = ErroneousAmbiguousConstructorsMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + message = "Ambiguous constructors found for creating org.mapstruct.ap.test.constructor.erroneous" + + ".ErroneousAmbiguousConstructorsMapper.PersonWithMultipleConstructors. Either declare parameterless " + + "constructor or annotate the default constructor with an annotation named @Default." + ) + }) + public void shouldUseMultipleConstructors() { + } + + @Test + @WithClasses({ + ConstructorProperties.class, + ErroneousConstructorPropertiesMapper.class, + PersonDto.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { + @Diagnostic( + type = ErroneousConstructorPropertiesMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 18, + message = "Incorrect @ConstructorProperties for org.mapstruct.ap.test.constructor.erroneous" + + ".ErroneousConstructorPropertiesMapper.PersonWithIncorrectConstructorProperties. The size of the " + + "@ConstructorProperties does not match the number of constructor parameters") + }) + public void shouldNotCompileIfConstructorPropertiesDoesNotMatch() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersMapper.java new file mode 100644 index 0000000000..02a6d9af64 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersMapper.java @@ -0,0 +1,21 @@ +/* + * 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.constructor.mixed; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConstructorMixedWithSettersMapper { + + ConstructorMixedWithSettersMapper INSTANCE = Mappers.getMapper( ConstructorMixedWithSettersMapper.class ); + + PersonMixed map(PersonDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersTest.java new file mode 100644 index 0000000000..2c24e33cb5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersTest.java @@ -0,0 +1,48 @@ +/* + * 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.constructor.mixed; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + PersonMixed.class, + PersonDto.class, + ConstructorMixedWithSettersMapper.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class ConstructorMixedWithSettersTest { + + @Test + public void mapDefault() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + PersonMixed target = ConstructorMixedWithSettersMapper.INSTANCE.map( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 30 ); + assertThat( target.getJob() ).isEqualTo( "Software Engineer" ); + assertThat( target.getCity() ).isEqualTo( "Zurich" ); + assertThat( target.getAddress() ).isEqualTo( "Plaza 1" ); + assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/PersonMixed.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/PersonMixed.java new file mode 100644 index 0000000000..deb79e84fc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/PersonMixed.java @@ -0,0 +1,67 @@ +/* + * 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.constructor.mixed; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class PersonMixed { + + private final String name; + private final int age; + private final String job; + private String city; + private String address; + private List children; + + public PersonMixed(String name, int age, String job) { + this.name = name; + this.age = age; + this.job = job; + } + + public String getName() { + return name; + } + + public void setName(String name) { + throw new RuntimeException( "Method is here only to verify that MapStruct won't use it" ); + } + + public int getAge() { + return age; + } + + public String getJob() { + return job; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntry.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntry.java new file mode 100644 index 0000000000..02a82ba8d0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntry.java @@ -0,0 +1,54 @@ +/* + * 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.constructor.nestedsource; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Chart; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ArtistToChartEntry { + + ArtistToChartEntry MAPPER = Mappers.getMapper( ArtistToChartEntry.class ); + + @Mappings({ + @Mapping(target = "chartName", source = "chart.name"), + @Mapping(target = "songTitle", source = "song.title"), + @Mapping(target = "artistName", source = "song.artist.name"), + @Mapping(target = "recordedAt", source = "song.artist.label.studio.name"), + @Mapping(target = "city", source = "song.artist.label.studio.city"), + @Mapping(target = "position", source = "position") + }) + ChartEntry map(Chart chart, Song song, Integer position); + + @Mappings({ + @Mapping(target = "chartName", ignore = true), + @Mapping(target = "songTitle", source = "title"), + @Mapping(target = "artistName", source = "artist.name"), + @Mapping(target = "recordedAt", source = "artist.label.studio.name"), + @Mapping(target = "city", source = "artist.label.studio.city"), + @Mapping(target = "position", ignore = true) + }) + ChartEntry map(Song song); + + @Mappings({ + @Mapping(target = "chartName", source = "name"), + @Mapping(target = "songTitle", ignore = true), + @Mapping(target = "artistName", ignore = true), + @Mapping(target = "recordedAt", ignore = true), + @Mapping(target = "city", ignore = true), + @Mapping(target = "position", ignore = true) + }) + ChartEntry map(Chart name); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryComposedReverse.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryComposedReverse.java new file mode 100644 index 0000000000..7fd9fbdb54 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryComposedReverse.java @@ -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.constructor.nestedsource; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryComposed; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryLabel; +import org.mapstruct.ap.test.constructor.nestedsource.source.Label; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public abstract class ArtistToChartEntryComposedReverse { + + public static final ArtistToChartEntryComposedReverse MAPPER = + Mappers.getMapper( ArtistToChartEntryComposedReverse.class ); + + @Mappings({ + @Mapping(target = "songTitle", source = "title"), + @Mapping(target = "artistName", source = "artist.name"), + @Mapping(target = "label", source = "artist.label"), + @Mapping(target = "position", ignore = true), + @Mapping(target = "chartName", ignore = true ) + }) + abstract ChartEntryComposed mapForward(Song song); + + @Mappings({ + @Mapping(target = "name", source = "name"), + @Mapping(target = "recordedAt", source = "studio.name"), + @Mapping(target = "city", source = "studio.city") + }) + abstract ChartEntryLabel mapForward(Label label); + + @InheritInverseConfiguration + @Mapping(target = "positions", ignore = true) + abstract Song mapReverse(ChartEntryComposed ce); + + @InheritInverseConfiguration + abstract Label mapReverse(ChartEntryLabel label); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryConfig.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryConfig.java new file mode 100644 index 0000000000..c1d0d05228 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryConfig.java @@ -0,0 +1,28 @@ +/* + * 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.constructor.nestedsource; + +import org.mapstruct.MapperConfig; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.ap.test.constructor.nestedsource._target.BaseChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; + +/** + * + * @author Sjaak Derksen + */ +@MapperConfig( unmappedTargetPolicy = ReportingPolicy.ERROR ) +public interface ArtistToChartEntryConfig { + + @Mappings({ + @Mapping(target = "songTitle", source = "title"), + @Mapping(target = "artistName", source = "artist.name"), + @Mapping(target = "chartName", ignore = true ) + }) + BaseChartEntry mapForwardConfig( Song song ); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryReverse.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryReverse.java new file mode 100644 index 0000000000..193d4ea6ec --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryReverse.java @@ -0,0 +1,38 @@ +/* + * 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.constructor.nestedsource; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public abstract class ArtistToChartEntryReverse { + + public static final ArtistToChartEntryReverse MAPPER = Mappers.getMapper( ArtistToChartEntryReverse.class ); + + @Mappings({ + + @Mapping(target = "songTitle", source = "title"), + @Mapping(target = "artistName", source = "artist.name"), + @Mapping(target = "recordedAt", source = "artist.label.studio.name"), + @Mapping(target = "city", source = "artist.label.studio.city"), + @Mapping(target = "position", ignore = true), + @Mapping(target = "chartName", ignore = true ) + }) + abstract ChartEntry mapForward(Song song); + + @InheritInverseConfiguration + @Mapping(target = "positions", ignore = true) + abstract Song mapReverse(ChartEntry ce); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithConfigReverse.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithConfigReverse.java new file mode 100644 index 0000000000..7c21104a6b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithConfigReverse.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.constructor.nestedsource; + +import org.mapstruct.InheritConfiguration; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryWithBase; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper( config = ArtistToChartEntryConfig.class ) +public abstract class ArtistToChartEntryWithConfigReverse { + + public static final ArtistToChartEntryWithConfigReverse MAPPER = + Mappers.getMapper( ArtistToChartEntryWithConfigReverse.class ); + + @InheritConfiguration + @Mappings({ + @Mapping(target = "recordedAt", source = "artist.label.studio.name"), + @Mapping(target = "city", source = "artist.label.studio.city"), + @Mapping(target = "position", ignore = true) + }) + abstract ChartEntryWithBase mapForward(Song song); + + @InheritInverseConfiguration( name = "mapForward" ) + @Mapping(target = "positions", ignore = true) + abstract Song mapReverse(ChartEntryWithBase ce); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithFactoryReverse.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithFactoryReverse.java new file mode 100644 index 0000000000..6583838673 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithFactoryReverse.java @@ -0,0 +1,39 @@ +/* + * 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.constructor.nestedsource; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public abstract class ArtistToChartEntryWithFactoryReverse { + + public static final ArtistToChartEntryWithFactoryReverse MAPPER + = Mappers.getMapper( ArtistToChartEntryWithFactoryReverse.class ); + + @Mappings({ + + @Mapping(target = "songTitle", source = "title"), + @Mapping(target = "artistName", source = "artist.name"), + @Mapping(target = "recordedAt", source = "artist.label.studio.name"), + @Mapping(target = "city", source = "artist.label.studio.city"), + @Mapping(target = "position", ignore = true), + @Mapping(target = "chartName", ignore = true) + }) + abstract ChartEntry mapForward(Song song); + + @InheritInverseConfiguration + @Mapping(target = "positions", ignore = true) + abstract Song mapReverse(ChartEntry ce); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithIgnoresReverse.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithIgnoresReverse.java new file mode 100644 index 0000000000..4d1d35fbe6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithIgnoresReverse.java @@ -0,0 +1,41 @@ +/* + * 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.constructor.nestedsource; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public abstract class ArtistToChartEntryWithIgnoresReverse { + + public static final ArtistToChartEntryWithIgnoresReverse MAPPER = + Mappers.getMapper( ArtistToChartEntryWithIgnoresReverse.class ); + + @Mappings({ + @Mapping(target = "songTitle", source = "title"), + @Mapping(target = "artistName", source = "artist.name"), + @Mapping(target = "recordedAt", source = "artist.label.studio.name"), + @Mapping(target = "city", source = "artist.label.studio.city"), + @Mapping(target = "position", ignore = true), + @Mapping(target = "chartName", ignore = true) + }) + abstract ChartEntry mapForward(Song song); + + @InheritInverseConfiguration + @Mappings({ + @Mapping(target = "positions", ignore = true), + @Mapping(target = "artist", ignore = true) + }) + abstract Song mapReverse(ChartEntry ce); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithMappingReverse.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithMappingReverse.java new file mode 100644 index 0000000000..b6b4cac803 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryWithMappingReverse.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.constructor.nestedsource; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryWithMapping; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public abstract class ArtistToChartEntryWithMappingReverse { + + public static final ArtistToChartEntryWithMappingReverse MAPPER = + Mappers.getMapper( ArtistToChartEntryWithMappingReverse.class ); + + @Mappings({ + @Mapping(target = "songTitle", source = "title"), + @Mapping(target = "artistId", source = "artist.name"), + @Mapping(target = "recordedAt", source = "artist.label.studio.name"), + @Mapping(target = "city", source = "artist.label.studio.city"), + @Mapping(target = "position", ignore = true), + @Mapping(target = "chartName", ignore = true) + }) + abstract ChartEntryWithMapping mapForward(Song song); + + @InheritInverseConfiguration + @Mapping(target = "positions", ignore = true) + abstract Song mapReverse(ChartEntryWithMapping ce); + + int mapArtistToArtistId(String in) { + + if ( "The Beatles".equals( in ) ) { + return 1; + } + else { + return -1; + } + } + + String mapArtistIdToArtist(int in) { + + if ( in == 1 ) { + return "The Beatles"; + } + else { + return "Unknown"; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/NestedSourcePropertiesConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/NestedSourcePropertiesConstructorTest.java new file mode 100644 index 0000000000..4fe43506ab --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/NestedSourcePropertiesConstructorTest.java @@ -0,0 +1,132 @@ +/* + * 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.constructor.nestedsource; + +import java.util.Collections; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Artist; +import org.mapstruct.ap.test.constructor.nestedsource.source.Chart; +import org.mapstruct.ap.test.constructor.nestedsource.source.Label; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.ap.test.constructor.nestedsource.source.Studio; +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; + +/** + * @author Sjaak Derksen + */ +@WithClasses({ Song.class, Artist.class, Chart.class, Label.class, Studio.class, ChartEntry.class }) +@IssueKey("73") +@RunWith(AnnotationProcessorTestRunner.class) +public class NestedSourcePropertiesConstructorTest { + + @Rule + public final GeneratedSource generatedSource = new GeneratedSource(); + + @Test + @WithClasses({ ArtistToChartEntry.class }) + public void shouldGenerateImplementationForPropertyNamesOnly() { + generatedSource.addComparisonToFixtureFor( ArtistToChartEntry.class ); + + Studio studio = new Studio( "Abbey Road", "London" ); + + Label label = new Label( "EMY", studio ); + + Artist artist = new Artist( "The Beatles", label ); + + Song song = new Song( artist, "A Hard Day's Night", Collections.emptyList() ); + + ChartEntry chartEntry = ArtistToChartEntry.MAPPER.map( song ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" ); + assertThat( chartEntry.getChartName() ).isNull(); + assertThat( chartEntry.getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 0 ); + assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + } + + @Test + @WithClasses({ ArtistToChartEntry.class }) + public void shouldGenerateImplementationForMultipleParam() { + + Studio studio = new Studio( "Abbey Road", "London" ); + + Label label = new Label( "EMY", studio ); + + Artist artist = new Artist( "The Beatles", label ); + + Song song = new Song( artist, "A Hard Day's Night", Collections.emptyList() ); + + Chart chart = new Chart( "record-sales", "Billboard", null ); + + ChartEntry chartEntry = ArtistToChartEntry.MAPPER.map( chart, song, 1 ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" ); + assertThat( chartEntry.getChartName() ).isEqualTo( "Billboard" ); + assertThat( chartEntry.getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 1 ); + assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + + chartEntry = ArtistToChartEntry.MAPPER.map( null, song, 10 ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" ); + assertThat( chartEntry.getChartName() ).isNull(); + assertThat( chartEntry.getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 10 ); + assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + + chartEntry = ArtistToChartEntry.MAPPER.map( chart, null, 5 ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isNull(); + assertThat( chartEntry.getChartName() ).isEqualTo( "Billboard" ); + assertThat( chartEntry.getCity() ).isNull(); + assertThat( chartEntry.getPosition() ).isEqualTo( 5 ); + assertThat( chartEntry.getRecordedAt() ).isNull(); + assertThat( chartEntry.getSongTitle() ).isNull(); + + chartEntry = ArtistToChartEntry.MAPPER.map( chart, song, null ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" ); + assertThat( chartEntry.getChartName() ).isEqualTo( "Billboard" ); + assertThat( chartEntry.getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 0 ); + assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + } + + @Test + @WithClasses({ ArtistToChartEntry.class }) + public void shouldPickPropertyNameOverParameterName() { + + Chart chart = new Chart( "record-sales", "Billboard", null ); + + ChartEntry chartEntry = ArtistToChartEntry.MAPPER.map( chart ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isNull(); + assertThat( chartEntry.getChartName() ).isEqualTo( "Billboard" ); + assertThat( chartEntry.getCity() ).isNull(); + assertThat( chartEntry.getPosition() ).isEqualTo( 0 ); + assertThat( chartEntry.getRecordedAt() ).isNull(); + assertThat( chartEntry.getSongTitle() ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ReversingNestedSourcePropertiesConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ReversingNestedSourcePropertiesConstructorTest.java new file mode 100644 index 0000000000..231a4fcad7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ReversingNestedSourcePropertiesConstructorTest.java @@ -0,0 +1,222 @@ +/* + * 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.constructor.nestedsource; + +import java.util.Collections; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.nestedsource._target.BaseChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryComposed; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryLabel; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryWithBase; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryWithMapping; +import org.mapstruct.ap.test.constructor.nestedsource.source.Artist; +import org.mapstruct.ap.test.constructor.nestedsource.source.Chart; +import org.mapstruct.ap.test.constructor.nestedsource.source.Label; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.ap.test.constructor.nestedsource.source.Studio; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("73") +@WithClasses({ Song.class, Artist.class, Chart.class, Label.class, Studio.class, ChartEntry.class }) +@RunWith(AnnotationProcessorTestRunner.class) +public class ReversingNestedSourcePropertiesConstructorTest { + + @Test + @WithClasses({ ArtistToChartEntryReverse.class }) + public void shouldGenerateNestedReverse() { + + Song song1 = prepareSong(); + + ChartEntry chartEntry = ArtistToChartEntryReverse.MAPPER.mapForward( song1 ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" ); + assertThat( chartEntry.getChartName() ).isNull(); + assertThat( chartEntry.getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 0 ); + assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + + // and now in reverse + Song song2 = ArtistToChartEntryReverse.MAPPER.mapReverse( chartEntry ); + + assertThat( song2 ).isNotNull(); + assertThat( song2.getArtist() ).isNotNull(); + assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" ); + assertThat( song2.getArtist().getLabel() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getName() ).isNull(); + assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" ); + assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); + } + + @Test + @WithClasses({ ArtistToChartEntryWithIgnoresReverse.class }) + public void shouldIgnoreEverytingBelowArtist() { + + Song song1 = prepareSong(); + + ChartEntry chartEntry = ArtistToChartEntryWithIgnoresReverse.MAPPER.mapForward( song1 ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" ); + assertThat( chartEntry.getChartName() ).isNull(); + assertThat( chartEntry.getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 0 ); + assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + + // and now in reverse + Song song2 = ArtistToChartEntryWithIgnoresReverse.MAPPER.mapReverse( chartEntry ); + + assertThat( song2 ).isNotNull(); + assertThat( song2.getArtist() ).isNull(); + } + + @Test + @WithClasses({ ArtistToChartEntryWithFactoryReverse.class }) + public void shouldGenerateNestedReverseWithFactory() { + + Song song1 = prepareSong(); + + ChartEntry chartEntry = ArtistToChartEntryWithFactoryReverse.MAPPER.mapForward( song1 ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" ); + assertThat( chartEntry.getChartName() ).isNull(); + assertThat( chartEntry.getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 0 ); + assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + + // and now in reverse + Song song2 = ArtistToChartEntryWithFactoryReverse.MAPPER.mapReverse( chartEntry ); + + assertThat( song2 ).isNotNull(); + assertThat( song2.getArtist() ).isNotNull(); + assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" ); + assertThat( song2.getArtist().getLabel() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getName() ).isNull(); + assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" ); + assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); + + } + + @Test + @WithClasses({ ArtistToChartEntryComposedReverse.class, ChartEntryComposed.class, ChartEntryLabel.class }) + public void shouldGenerateNestedComposedReverse() { + + Song song1 = prepareSong(); + + ChartEntryComposed chartEntry = ArtistToChartEntryComposedReverse.MAPPER.mapForward( song1 ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" ); + assertThat( chartEntry.getChartName() ).isNull(); + assertThat( chartEntry.getLabel().getName() ).isEqualTo( "EMY" ); + assertThat( chartEntry.getLabel().getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getLabel().getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 0 ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + + // and now in reverse + Song song2 = ArtistToChartEntryComposedReverse.MAPPER.mapReverse( chartEntry ); + + assertThat( song2 ).isNotNull(); + assertThat( song2.getArtist() ).isNotNull(); + assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" ); + assertThat( song2.getArtist().getLabel() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getName() ).isEqualTo( "EMY" ); + assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" ); + assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); + } + + @Test + @WithClasses({ ArtistToChartEntryWithMappingReverse.class, ChartEntryWithMapping.class }) + public void shouldGenerateNestedWithMappingReverse() { + + Song song1 = prepareSong(); + + ChartEntryWithMapping chartEntry = ArtistToChartEntryWithMappingReverse.MAPPER.mapForward( song1 ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistId() ).isEqualTo( 1 ); + assertThat( chartEntry.getChartName() ).isNull(); + assertThat( chartEntry.getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 0 ); + assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + + // and now in reverse + Song song2 = ArtistToChartEntryWithMappingReverse.MAPPER.mapReverse( chartEntry ); + + assertThat( song2 ).isNotNull(); + assertThat( song2.getArtist() ).isNotNull(); + assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" ); + assertThat( song2.getArtist().getLabel() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getName() ).isNull(); + assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" ); + assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); + } + + @Test + @WithClasses({ + ArtistToChartEntryWithConfigReverse.class, + ArtistToChartEntryConfig.class, + BaseChartEntry.class, + ChartEntryWithBase.class + }) + public void shouldGenerateNestedWithConfigReverse() { + + Song song1 = prepareSong(); + + ChartEntryWithBase chartEntry = ArtistToChartEntryWithConfigReverse.MAPPER.mapForward( song1 ); + + assertThat( chartEntry ).isNotNull(); + assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" ); + assertThat( chartEntry.getChartName() ).isNull(); + assertThat( chartEntry.getCity() ).isEqualTo( "London" ); + assertThat( chartEntry.getPosition() ).isEqualTo( 0 ); + assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" ); + assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); + + // and now in reverse + Song song2 = ArtistToChartEntryWithConfigReverse.MAPPER.mapReverse( chartEntry ); + + assertThat( song2 ).isNotNull(); + assertThat( song2.getArtist() ).isNotNull(); + assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" ); + assertThat( song2.getArtist().getLabel() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getName() ).isNull(); + assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull(); + assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" ); + assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); + } + + private Song prepareSong() { + Studio studio = new Studio( "Abbey Road", "London" ); + + Label label = new Label( "EMY", studio ); + + Artist artist = new Artist( "The Beatles", label ); + + return new Song( artist, "A Hard Day's Night", Collections.emptyList() ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/AdderUsageObserver.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/AdderUsageObserver.java new file mode 100644 index 0000000000..ff21870b75 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/AdderUsageObserver.java @@ -0,0 +1,26 @@ +/* + * 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.constructor.nestedsource._target; + +/** + * @author Sjaak Derksen + */ +public class AdderUsageObserver { + + private AdderUsageObserver() { + } + + private static boolean used = false; + + public static boolean isUsed() { + return used; + } + + public static void setUsed(boolean used) { + AdderUsageObserver.used = used; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/BaseChartEntry.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/BaseChartEntry.java new file mode 100644 index 0000000000..7b82c77b32 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/BaseChartEntry.java @@ -0,0 +1,34 @@ +/* + * 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.constructor.nestedsource._target; + +/** + * @author Filip Hrisafov + */ +public class BaseChartEntry { + + private final String chartName; + private final String songTitle; + private final String artistName; + + public BaseChartEntry(String chartName, String songTitle, String artistName) { + this.chartName = chartName; + this.songTitle = songTitle; + this.artistName = artistName; + } + + public String getChartName() { + return chartName; + } + + public String getSongTitle() { + return songTitle; + } + + public String getArtistName() { + return artistName; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntry.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntry.java new file mode 100644 index 0000000000..36d7e95601 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntry.java @@ -0,0 +1,54 @@ +/* + * 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.constructor.nestedsource._target; + +/** + * @author Filip Hrisafov + */ +public class ChartEntry { + + private final String chartName; + private final String songTitle; + private final String artistName; + private final String recordedAt; + private final String city; + private final int position; + + public ChartEntry(String chartName, String songTitle, String artistName, String recordedAt, String city, + int position) { + this.chartName = chartName; + this.songTitle = songTitle; + this.artistName = artistName; + this.recordedAt = recordedAt; + this.city = city; + this.position = position; + } + + public String getChartName() { + return chartName; + } + + public String getSongTitle() { + return songTitle; + } + + public String getArtistName() { + return artistName; + } + + public String getRecordedAt() { + return recordedAt; + } + + public String getCity() { + return city; + } + + public int getPosition() { + return position; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryComposed.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryComposed.java new file mode 100644 index 0000000000..2543c70686 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryComposed.java @@ -0,0 +1,58 @@ +/* + * 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.constructor.nestedsource._target; + +/** + * @author Filip Hrisafov + */ +public class ChartEntryComposed { + + private String chartName; + private String songTitle; + private String artistName; + private ChartEntryLabel label; + private int position; + + public String getChartName() { + return chartName; + } + + public void setChartName(String chartName) { + this.chartName = chartName; + } + + public String getSongTitle() { + return songTitle; + } + + public void setSongTitle(String songTitle) { + this.songTitle = songTitle; + } + + public String getArtistName() { + return artistName; + } + + public void setArtistName(String artistName) { + this.artistName = artistName; + } + + public ChartEntryLabel getLabel() { + return label; + } + + public void setLabel(ChartEntryLabel label) { + this.label = label; + } + + public int getPosition() { + return position; + } + + public void setPosition(int position) { + this.position = position; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryLabel.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryLabel.java new file mode 100644 index 0000000000..b639b97b35 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryLabel.java @@ -0,0 +1,34 @@ +/* + * 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.constructor.nestedsource._target; + +/** + * @author Filip Hrisafov + */ +public class ChartEntryLabel { + + private final String name; + private final String city; + private final String recordedAt; + + public ChartEntryLabel(String name, String city, String recordedAt) { + this.name = name; + this.city = city; + this.recordedAt = recordedAt; + } + + public String getName() { + return name; + } + + public String getCity() { + return city; + } + + public String getRecordedAt() { + return recordedAt; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryWithBase.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryWithBase.java new file mode 100644 index 0000000000..4855080fb3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryWithBase.java @@ -0,0 +1,36 @@ +/* + * 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.constructor.nestedsource._target; + +/** + * @author Filip Hrisafov + */ +public class ChartEntryWithBase extends BaseChartEntry { + + private final String recordedAt; + private final String city; + private final int position; + + public ChartEntryWithBase(String chartName, String songTitle, String artistName, String recordedAt, + String city, int position) { + super( chartName, songTitle, artistName ); + this.recordedAt = recordedAt; + this.city = city; + this.position = position; + } + + public String getRecordedAt() { + return recordedAt; + } + + public String getCity() { + return city; + } + + public int getPosition() { + return position; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryWithMapping.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryWithMapping.java new file mode 100644 index 0000000000..f7ba366e17 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartEntryWithMapping.java @@ -0,0 +1,54 @@ +/* + * 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.constructor.nestedsource._target; + +/** + * @author Filip Hrisafov + */ +public class ChartEntryWithMapping { + + private final String chartName; + private final String songTitle; + private final int artistId; + private final String recordedAt; + private final String city; + private final int position; + + public ChartEntryWithMapping(String chartName, String songTitle, int artistId, String recordedAt, + String city, int position) { + this.chartName = chartName; + this.songTitle = songTitle; + this.artistId = artistId; + this.recordedAt = recordedAt; + this.city = city; + this.position = position; + } + + public String getChartName() { + return chartName; + } + + public String getSongTitle() { + return songTitle; + } + + public int getArtistId() { + return artistId; + } + + public String getRecordedAt() { + return recordedAt; + } + + public String getCity() { + return city; + } + + public int getPosition() { + return position; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartPositions.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartPositions.java new file mode 100644 index 0000000000..a14e04ac76 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/_target/ChartPositions.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.ap.test.constructor.nestedsource._target; + +import java.util.Collections; +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class ChartPositions { + + private final List positions; + + public ChartPositions(List positions) { + this.positions = positions; + } + + public List getPositions() { + return Collections.unmodifiableList( positions ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Artist.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Artist.java new file mode 100644 index 0000000000..def4ac697f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Artist.java @@ -0,0 +1,29 @@ +/* + * 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.constructor.nestedsource.source; + +/** + * @author Filip Hrisafov + */ +public class Artist { + + private final String name; + private final Label label; + + public Artist(String name, Label label) { + this.name = name; + this.label = label; + } + + public String getName() { + return name; + } + + public Label getLabel() { + return label; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Chart.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Chart.java new file mode 100644 index 0000000000..064129fc06 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Chart.java @@ -0,0 +1,35 @@ +/* + * 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.constructor.nestedsource.source; + +/** + * @author Filip Hrisafov + */ +public class Chart { + + private final String type; + private final String name; + private final Song song; + + public Chart(String type, String name, Song song) { + this.type = type; + this.name = name; + this.song = song; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public Song getSong() { + return song; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Label.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Label.java new file mode 100644 index 0000000000..6c2a15bbfb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Label.java @@ -0,0 +1,29 @@ +/* + * 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.constructor.nestedsource.source; + +/** + * @author Filip Hrisafov + */ +public class Label { + + private final String name; + private final Studio studio; + + public Label(String name, Studio studio) { + this.name = name; + this.studio = studio; + } + + public String getName() { + return name; + } + + public Studio getStudio() { + return studio; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Song.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Song.java new file mode 100644 index 0000000000..ac6ce20104 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Song.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.constructor.nestedsource.source; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class Song { + + private final Artist artist; + private final String title; + private final List positions; + + public Song(Artist artist, String title, List positions) { + this.artist = artist; + this.title = title; + this.positions = positions; + } + + public Artist getArtist() { + return artist; + } + + public String getTitle() { + return title; + } + + public List getPositions() { + return positions; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Studio.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Studio.java new file mode 100644 index 0000000000..db7bfd7389 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/source/Studio.java @@ -0,0 +1,29 @@ +/* + * 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.constructor.nestedsource.source; + +/** + * @author Filip Hrisafov + */ +public class Studio { + + private final String name; + private final String city; + + public Studio(String name, String city) { + this.name = name; + this.city = city; + } + + public String getName() { + return name; + } + + public String getCity() { + return city; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtist.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtist.java new file mode 100644 index 0000000000..9c1946fa26 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtist.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.constructor.nestedtarget; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Chart; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public abstract class ChartEntryToArtist { + + public static final ChartEntryToArtist MAPPER = Mappers.getMapper( ChartEntryToArtist.class ); + + @Mappings({ + @Mapping(target = "type", ignore = true), + @Mapping(target = "name", source = "chartName"), + @Mapping(target = "song.title", source = "songTitle"), + @Mapping(target = "song.artist.name", source = "artistName"), + @Mapping(target = "song.artist.label.studio.name", source = "recordedAt"), + @Mapping(target = "song.artist.label.studio.city", source = "city"), + @Mapping(target = "song.positions", source = "position") + }) + public abstract Chart map(ChartEntry chartEntry); + + @InheritInverseConfiguration + public abstract ChartEntry map(Chart chart); + + protected List mapPosition(Integer in) { + if ( in != null ) { + return new ArrayList<>( Arrays.asList( in ) ); + } + else { + return new ArrayList<>(); + } + } + + protected Integer mapPosition(List in) { + if ( in != null && !in.isEmpty() ) { + return in.get( 0 ); + } + else { + return null; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/NestedProductPropertiesConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/NestedProductPropertiesConstructorTest.java new file mode 100644 index 0000000000..78d113a8c8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/NestedProductPropertiesConstructorTest.java @@ -0,0 +1,98 @@ +/* + * 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.constructor.nestedtarget; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Artist; +import org.mapstruct.ap.test.constructor.nestedsource.source.Chart; +import org.mapstruct.ap.test.constructor.nestedsource.source.Label; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.ap.test.constructor.nestedsource.source.Studio; +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; + +/** + * @author Sjaak Derksen + */ +@WithClasses({ + Song.class, + Artist.class, + Chart.class, + Label.class, + Studio.class, + ChartEntry.class, + ChartEntryToArtist.class, +}) +@IssueKey("73") +@RunWith(AnnotationProcessorTestRunner.class) +public class NestedProductPropertiesConstructorTest { + + @Rule + public GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + ChartEntryToArtist.class + ); + + @Test + public void shouldMapNestedTarget() { + + ChartEntry chartEntry = new ChartEntry( + "US Billboard Hot Rock Songs", + "Purple Rain", + "Prince", + "Live, First Avenue, Minneapolis", + "Minneapolis", + 1 + ); + + Chart result = ChartEntryToArtist.MAPPER.map( chartEntry ); + + assertThat( result.getName() ).isEqualTo( "US Billboard Hot Rock Songs" ); + assertThat( result.getSong() ).isNotNull(); + assertThat( result.getSong().getArtist() ).isNotNull(); + assertThat( result.getSong().getTitle() ).isEqualTo( "Purple Rain" ); + assertThat( result.getSong().getArtist().getName() ).isEqualTo( "Prince" ); + assertThat( result.getSong().getArtist().getLabel() ).isNotNull(); + assertThat( result.getSong().getArtist().getLabel().getStudio() ).isNotNull(); + assertThat( result.getSong().getArtist().getLabel().getStudio().getName() ) + .isEqualTo( "Live, First Avenue, Minneapolis" ); + assertThat( result.getSong().getArtist().getLabel().getStudio().getCity() ) + .isEqualTo( "Minneapolis" ); + assertThat( result.getSong().getPositions() ).hasSize( 1 ); + assertThat( result.getSong().getPositions().get( 0 ) ).isEqualTo( 1 ); + + } + + @Test + public void shouldReverseNestedTarget() { + + ChartEntry chartEntry = new ChartEntry( + "US Billboard Hot Rock Songs", + "Purple Rain", + "Prince", + "Live, First Avenue, Minneapolis", + "Minneapolis", + 1 + ); + + Chart chart = ChartEntryToArtist.MAPPER.map( chartEntry ); + ChartEntry result = ChartEntryToArtist.MAPPER.map( chart ); + + assertThat( result ).isNotNull(); + assertThat( result.getArtistName() ).isEqualTo( "Prince" ); + assertThat( result.getChartName() ).isEqualTo( "US Billboard Hot Rock Songs" ); + assertThat( result.getCity() ).isEqualTo( "Minneapolis" ); + assertThat( result.getPosition() ).isEqualTo( 1 ); + assertThat( result.getRecordedAt() ).isEqualTo( "Live, First Avenue, Minneapolis" ); + assertThat( result.getSongTitle() ).isEqualTo( "Purple Rain" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorMapper.java new file mode 100644 index 0000000000..2e162d2f23 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorMapper.java @@ -0,0 +1,31 @@ +/* + * 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.constructor.simple; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.constructor.Person; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SimpleConstructorMapper { + + SimpleConstructorMapper INSTANCE = Mappers.getMapper( SimpleConstructorMapper.class ); + + Person map(PersonDto dto); + + @Mapping(target = "age", constant = "25") + @Mapping(target = "job", constant = "Software Developer") + Person mapWithConstants(PersonDto dto); + + @Mapping(target = "age", expression = "java(25 - 5)") + @Mapping(target = "job", expression = "java(\"Software Developer\".toLowerCase())") + Person mapWithExpression(PersonDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorTest.java new file mode 100644 index 0000000000..e24abb84b1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorTest.java @@ -0,0 +1,89 @@ +/* + * 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.constructor.simple; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.Person; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Person.class, + PersonDto.class, + SimpleConstructorMapper.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class SimpleConstructorTest { + + @Test + public void mapDefault() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + Person target = SimpleConstructorMapper.INSTANCE.map( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 30 ); + assertThat( target.getJob() ).isEqualTo( "Software Engineer" ); + assertThat( target.getCity() ).isEqualTo( "Zurich" ); + assertThat( target.getAddress() ).isEqualTo( "Plaza 1" ); + assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); + } + + @Test + public void mapWithConstants() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + Person target = SimpleConstructorMapper.INSTANCE.mapWithConstants( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 25 ); + assertThat( target.getJob() ).isEqualTo( "Software Developer" ); + assertThat( target.getCity() ).isEqualTo( "Zurich" ); + assertThat( target.getAddress() ).isEqualTo( "Plaza 1" ); + assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); + } + + @Test + public void mapWithExpressions() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + source.setJob( "Software Engineer" ); + source.setCity( "Zurich" ); + source.setAddress( "Plaza 1" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + + Person target = SimpleConstructorMapper.INSTANCE.mapWithExpression( source ); + + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 20 ); + assertThat( target.getJob() ).isEqualTo( "software developer" ); + assertThat( target.getCity() ).isEqualTo( "Zurich" ); + assertThat( target.getAddress() ).isEqualTo( "Plaza 1" ); + assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/Order.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/Order.java new file mode 100644 index 0000000000..18fe9b1015 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/Order.java @@ -0,0 +1,30 @@ +/* + * 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.constructor.unmapped; + +import java.util.Date; + +/** + * @author Filip Hrisafov + */ +public class Order { + + private final String name; + private final Date time; + + public Order(String name, Date time) { + this.name = name; + this.time = time; + } + + public String getName() { + return name; + } + + public Date getTime() { + return time; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/OrderDto.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/OrderDto.java new file mode 100644 index 0000000000..259cf8973d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/OrderDto.java @@ -0,0 +1,22 @@ +/* + * 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.constructor.unmapped; + +/** + * @author Filip Hrisafov + */ +public class OrderDto { + + private final String name; + + public OrderDto(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorMapper.java new file mode 100644 index 0000000000..8fcede06f4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorMapper.java @@ -0,0 +1,20 @@ +/* + * 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.constructor.unmapped; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface UnmappedConstructorMapper { + + UnmappedConstructorMapper INSTANCE = Mappers.getMapper( UnmappedConstructorMapper.class ); + + Order map(OrderDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorTest.java new file mode 100644 index 0000000000..7a7f859041 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorTest.java @@ -0,0 +1,45 @@ +/* + * 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.constructor.unmapped; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Order.class, + OrderDto.class, + UnmappedConstructorMapper.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class UnmappedConstructorTest { + + @Test + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = UnmappedConstructorMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 19, + message = "Unmapped target property: \"time\".") + }) + public void shouldGenerateCompilableCodeForUnmappedConstructorProperties() { + OrderDto source = new OrderDto( "truck" ); + + Order target = UnmappedConstructorMapper.INSTANCE.map( source ); + + assertThat( target.getName() ).isEqualTo( "truck" ); + assertThat( target.getTime() ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java index 561b53effd..94993e4a66 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java @@ -36,12 +36,7 @@ public class AmbiguousAnnotatedFactoryTest { ".ambiguousannotatedfactorymethod.Foo foo), org.mapstruct.ap.test.erroneous" + ".ambiguousannotatedfactorymethod.Bar org.mapstruct.ap.test.erroneous" + ".ambiguousannotatedfactorymethod.AmbiguousBarFactory.createBar(org.mapstruct.ap.test.erroneous" + - ".ambiguousannotatedfactorymethod.Foo foo)."), - @Diagnostic(type = SourceTargetMapperAndBarFactory.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 22, - message = "org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod.Bar does not have an " + - "accessible parameterless constructor.") + ".ambiguousannotatedfactorymethod.Foo foo).") } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java index 82886eb038..e4e9ff81cb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java @@ -37,12 +37,7 @@ public class FactoryTest { message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.erroneous" + ".ambiguousfactorymethod.Bar: org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar " + "createBar(), org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar org.mapstruct.ap.test" + - ".erroneous.ambiguousfactorymethod.a.BarFactory.createBar()."), - @Diagnostic(type = SourceTargetMapperAndBarFactory.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 22, - message = "org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar does not have an accessible " + - "parameterless constructor.") + ".erroneous.ambiguousfactorymethod.a.BarFactory.createBar().") } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryErroneous.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryErroneous.java index 5ed5ee57d0..5f8be28763 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryErroneous.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryErroneous.java @@ -28,9 +28,22 @@ public interface ArtistToChartEntryErroneous { @Mapping(target = "city", ignore = true), @Mapping(target = "position", source = "position") } ) - ChartEntry forward(Integer position); + ChartEntry forward(ChartPosition position); @InheritInverseConfiguration - Integer reverse(ChartEntry position); + ChartPosition reverse(ChartEntry position); + + class ChartPosition { + + private final int position; + + private ChartPosition(int position) { + this.position = position; + } + + public int getPosition() { + return position; + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java index 1d61b6e69a..f9d5b11eed 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java @@ -164,17 +164,18 @@ public void shouldUseGetAsTargetAccessor() { } @Test - @IssueKey( "838" ) + @IssueKey("838") @ExpectedCompilationOutcome( - value = CompilationResult.FAILED, - diagnostics = { - @Diagnostic( type = ArtistToChartEntryErroneous.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 34, - message = "java.lang.Integer does not have an accessible parameterless constructor." ) - } + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ArtistToChartEntryErroneous.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 34, + message = "org.mapstruct.ap.test.nestedsourceproperties.ArtistToChartEntryErroneous.ChartPosition " + + "does not have an accessible constructor.") + } ) @WithClasses({ ArtistToChartEntryErroneous.class }) - public void inverseShouldRaiseErrorForEmptyConstructor() { + public void inverseShouldRaiseErrorForNotAccessibleConstructor() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/Citrus.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/Citrus.java new file mode 100644 index 0000000000..5dd69a83f9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/Citrus.java @@ -0,0 +1,21 @@ +/* + * 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.selection.resulttype; + +/** + * @author Filip Hrisafov + */ +public class Citrus extends Fruit implements IsFruit { + + public Citrus(String type) { + super( type ); + } + + @Override + public void setType(String type) { + throw new RuntimeException( "Not allowed to change citrus type" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousResultTypeNoEmptyConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousResultTypeNoAccessibleConstructorMapper.java similarity index 71% rename from processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousResultTypeNoEmptyConstructorMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousResultTypeNoAccessibleConstructorMapper.java index 0e8cb1138d..40612fcf3d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousResultTypeNoEmptyConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousResultTypeNoAccessibleConstructorMapper.java @@ -13,9 +13,16 @@ * @author Filip Hrisafov */ @Mapper -public interface ErroneousResultTypeNoEmptyConstructorMapper { +public interface ErroneousResultTypeNoAccessibleConstructorMapper { @BeanMapping(resultType = Banana.class) @Mapping(target = "type", ignore = true) Fruit map(FruitDto source); + + class Banana extends Fruit { + + private Banana(String type) { + super( type ); + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java index efdea3c0eb..5cd415e56e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java @@ -21,9 +21,9 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** - * * @author Sjaak Derksen */ @IssueKey("385") @@ -48,12 +48,7 @@ public class InheritanceSelectionTest { message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.selection.resulttype" + ".Fruit: org.mapstruct.ap.test.selection.resulttype.Apple org.mapstruct.ap.test.selection" + ".resulttype.ConflictingFruitFactory.createApple(), org.mapstruct.ap.test.selection.resulttype" + - ".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana()."), - @Diagnostic(type = ErroneousFruitMapper.class, - kind = Kind.ERROR, - line = 23, - message = "org.mapstruct.ap.test.selection.resulttype.Fruit does not have an accessible parameterless" + - " constructor.") + ".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana().") } ) public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() { @@ -61,22 +56,22 @@ public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() @IssueKey("1283") @Test - @WithClasses({ ErroneousResultTypeNoEmptyConstructorMapper.class, Banana.class }) + @WithClasses({ ErroneousResultTypeNoAccessibleConstructorMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { - @Diagnostic(type = ErroneousResultTypeNoEmptyConstructorMapper.class, + @Diagnostic(type = ErroneousResultTypeNoAccessibleConstructorMapper.class, kind = Kind.ERROR, line = 18, - message = "org.mapstruct.ap.test.selection.resulttype.Banana does not have an accessible " + - "parameterless constructor.") + message = "org.mapstruct.ap.test.selection.resulttype" + + ".ErroneousResultTypeNoAccessibleConstructorMapper.Banana does not have an accessible constructor.") } ) - public void testResultTypeHasNoSuitableEmptyConstructor() { + public void testResultTypeHasNoSuitableAccessibleConstructor() { } @Test - @WithClasses( { ConflictingFruitFactory.class, ResultTypeSelectingFruitMapper.class, Banana.class } ) + @WithClasses({ ConflictingFruitFactory.class, ResultTypeSelectingFruitMapper.class, Banana.class }) public void testResultTypeBasedFactoryMethodSelection() { FruitDto fruitDto = new FruitDto( null ); @@ -88,7 +83,7 @@ public void testResultTypeBasedFactoryMethodSelection() { @Test @IssueKey("434") - @WithClasses( { ResultTypeConstructingFruitMapper.class } ) + @WithClasses({ ResultTypeConstructingFruitMapper.class }) public void testResultTypeBasedConstructionOfResult() { FruitDto fruitDto = new FruitDto( null ); @@ -99,7 +94,7 @@ public void testResultTypeBasedConstructionOfResult() { @Test @IssueKey("657") - @WithClasses( { ResultTypeConstructingFruitInterfaceMapper.class } ) + @WithClasses({ ResultTypeConstructingFruitInterfaceMapper.class }) public void testResultTypeBasedConstructionOfResultForInterface() { FruitDto fruitDto = new FruitDto( null ); @@ -143,7 +138,7 @@ public void testResultTypeBasedConstructionOfResultNonAssignable() { @Test @IssueKey("433") - @WithClasses( { + @WithClasses({ FruitFamilyMapper.class, GoldenDeliciousDto.class, GoldenDelicious.class, @@ -151,11 +146,11 @@ public void testResultTypeBasedConstructionOfResultNonAssignable() { AppleFamilyDto.class, AppleFactory.class, Banana.class - } ) + }) public void testShouldSelectResultTypeInCaseOfAmbiguity() { AppleFamilyDto appleFamilyDto = new AppleFamilyDto(); - appleFamilyDto.setApple( new AppleDto("AppleDto") ); + appleFamilyDto.setApple( new AppleDto( "AppleDto" ) ); AppleFamily result = FruitFamilyMapper.INSTANCE.map( appleFamilyDto ); assertThat( result ).isNotNull(); @@ -167,7 +162,7 @@ public void testShouldSelectResultTypeInCaseOfAmbiguity() { @Test @IssueKey("433") - @WithClasses( { + @WithClasses({ FruitFamilyMapper.class, GoldenDeliciousDto.class, GoldenDelicious.class, @@ -175,7 +170,7 @@ public void testShouldSelectResultTypeInCaseOfAmbiguity() { AppleFamilyDto.class, AppleFactory.class, Banana.class - } ) + }) public void testShouldSelectResultTypeInCaseOfAmbiguityForIterable() { List source = Arrays.asList( new AppleDto( "AppleDto" ) ); @@ -189,7 +184,7 @@ public void testShouldSelectResultTypeInCaseOfAmbiguityForIterable() { @Test @IssueKey("433") - @WithClasses( { + @WithClasses({ FruitFamilyMapper.class, GoldenDeliciousDto.class, GoldenDelicious.class, @@ -197,7 +192,7 @@ public void testShouldSelectResultTypeInCaseOfAmbiguityForIterable() { AppleFamilyDto.class, AppleFactory.class, Banana.class - } ) + }) public void testShouldSelectResultTypeInCaseOfAmbiguityForMap() { Map source = new HashMap(); @@ -214,4 +209,38 @@ public void testShouldSelectResultTypeInCaseOfAmbiguityForMap() { assertThat( entry.getValue().getType() ).isEqualTo( "AppleDto" ); } + + @Test + @IssueKey("73") + @WithClasses({ + Citrus.class, + ResultTypeWithConstructorConstructingFruitInterfaceMapper.class + }) + public void testShouldUseConstructorFromResultTypeForInterface() { + FruitDto orange = new FruitDto( "orange" ); + IsFruit citrus = ResultTypeWithConstructorConstructingFruitInterfaceMapper.INSTANCE.map( orange ); + + assertThat( citrus ).isInstanceOf( Citrus.class ); + assertThat( citrus.getType() ).isEqualTo( "orange" ); + assertThatThrownBy( () -> citrus.setType( "lemon" ) ) + .hasMessage( "Not allowed to change citrus type" ); + assertThat( citrus.getType() ).isEqualTo( "orange" ); + } + + @Test + @IssueKey("73") + @WithClasses({ + Citrus.class, + ResultTypeWithConstructorConstructingFruitInterfaceMapper.class + }) + public void testShouldUseConstructorFromResultType() { + FruitDto lemon = new FruitDto( "lemon" ); + IsFruit citrus = ResultTypeWithConstructorConstructingFruitInterfaceMapper.INSTANCE.map( lemon ); + + assertThat( citrus ).isInstanceOf( Citrus.class ); + assertThat( citrus.getType() ).isEqualTo( "lemon" ); + assertThatThrownBy( () -> citrus.setType( "orange" ) ) + .hasMessage( "Not allowed to change citrus type" ); + assertThat( citrus.getType() ).isEqualTo( "lemon" ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ResultTypeWithConstructorConstructingFruitInterfaceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ResultTypeWithConstructorConstructingFruitInterfaceMapper.java new file mode 100644 index 0000000000..b1670772e1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ResultTypeWithConstructorConstructingFruitInterfaceMapper.java @@ -0,0 +1,23 @@ +/* + * 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.selection.resulttype; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ResultTypeWithConstructorConstructingFruitInterfaceMapper { + + ResultTypeWithConstructorConstructingFruitInterfaceMapper INSTANCE = Mappers.getMapper( + ResultTypeWithConstructorConstructingFruitInterfaceMapper.class ); + + @BeanMapping(resultType = Citrus.class) + IsFruit map(FruitDto source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ResultTypeWithConstructorConstructingFruitMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ResultTypeWithConstructorConstructingFruitMapper.java new file mode 100644 index 0000000000..7448cbb7ac --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ResultTypeWithConstructorConstructingFruitMapper.java @@ -0,0 +1,24 @@ +/* + * 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.selection.resulttype; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ResultTypeWithConstructorConstructingFruitMapper { + + ResultTypeWithConstructorConstructingFruitMapper INSTANCE = Mappers.getMapper( + ResultTypeWithConstructorConstructingFruitMapper.class ); + + @BeanMapping(resultType = Citrus.class) + Fruit map(FruitDto source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper2.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper2.java index 81eecea982..f57e4c95a4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper2.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper2.java @@ -34,4 +34,11 @@ public interface ErroneousOrganizationMapper2 { }) DepartmentEntity toDepartmentEntity(DepartmentDto dto); + class DepartmentEntity extends org.mapstruct.ap.test.updatemethods.DepartmentEntity { + + private DepartmentEntity() { + super( null ); + } + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java index f04ddf338d..1b93d04a53 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java @@ -175,8 +175,8 @@ public void testShouldFailOnPropertyMappingNoPropertyGetter() { } @Diagnostic(type = ErroneousOrganizationMapper2.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 35, - message = "org.mapstruct.ap.test.updatemethods.DepartmentEntity does not have an accessible " + - "parameterless constructor.") + message = "org.mapstruct.ap.test.updatemethods.ErroneousOrganizationMapper2.DepartmentEntity does not" + + " have an accessible constructor.") }) public void testShouldFailOnConstantMappingNoPropertyGetter() { diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java index e8c09bec98..59916e9818 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java @@ -308,9 +308,11 @@ private void assertDiagnostics(List actualDiagnostics, assertThat( actual.getLine() ).isIn( expected.getLine(), expected.getAlternativeLine() ); } else if ( expected.getLine() != null ) { - assertThat( actual.getLine() ).isEqualTo( expected.getLine() ); + assertThat( actual.getLine() ).as( actual.getMessage() ).isEqualTo( expected.getLine() ); } - assertThat( actual.getKind() ).isEqualTo( expected.getKind() ); + assertThat( actual.getKind() ) + .as( actual.getMessage() ) + .isEqualTo( expected.getKind() ); if ( expected.getMessage() != null && !expected.getMessage().isEmpty() ) { assertThat( actual.getMessage() ).describedAs( String.format( diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java new file mode 100644 index 0000000000..bd8c81a5db --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java @@ -0,0 +1,170 @@ +/* + * 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.constructor.nestedsource; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Artist; +import org.mapstruct.ap.test.constructor.nestedsource.source.Chart; +import org.mapstruct.ap.test.constructor.nestedsource.source.Label; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.ap.test.constructor.nestedsource.source.Studio; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2020-04-19T11:28:54+0200", + comments = "version: , compiler: javac, environment: Java 14.0.1 (Oracle Corporation)" +) +public class ArtistToChartEntryImpl implements ArtistToChartEntry { + + @Override + public ChartEntry map(Chart chart, Song song, Integer position) { + if ( chart == null && song == null && position == null ) { + return null; + } + + String chartName; + if ( chart != null ) { + chartName = chart.getName(); + } + else { + chartName = null; + } + String songTitle; + String artistName; + String recordedAt; + String city; + if ( song != null ) { + songTitle = song.getTitle(); + artistName = songArtistName( song ); + recordedAt = songArtistLabelStudioName( song ); + city = songArtistLabelStudioCity( song ); + } + else { + songTitle = null; + artistName = null; + recordedAt = null; + city = null; + } + int position1; + if ( position != null ) { + position1 = position; + } + else { + position1 = 0; + } + + ChartEntry chartEntry = new ChartEntry( chartName, songTitle, artistName, recordedAt, city, position1 ); + + return chartEntry; + } + + @Override + public ChartEntry map(Song song) { + if ( song == null ) { + return null; + } + + String songTitle; + String artistName; + String recordedAt; + String city; + + songTitle = song.getTitle(); + artistName = songArtistName( song ); + recordedAt = songArtistLabelStudioName( song ); + city = songArtistLabelStudioCity( song ); + + String chartName = null; + int position = 0; + + ChartEntry chartEntry = new ChartEntry( chartName, songTitle, artistName, recordedAt, city, position ); + + return chartEntry; + } + + @Override + public ChartEntry map(Chart name) { + if ( name == null ) { + return null; + } + + String chartName; + + chartName = name.getName(); + + String songTitle = null; + String artistName = null; + String recordedAt = null; + String city = null; + int position = 0; + + ChartEntry chartEntry = new ChartEntry( chartName, songTitle, artistName, recordedAt, city, position ); + + return chartEntry; + } + + private String songArtistName(Song song) { + if ( song == null ) { + return null; + } + Artist artist = song.getArtist(); + if ( artist == null ) { + return null; + } + String name = artist.getName(); + if ( name == null ) { + return null; + } + return name; + } + + private String songArtistLabelStudioName(Song song) { + if ( song == null ) { + return null; + } + Artist artist = song.getArtist(); + if ( artist == null ) { + return null; + } + Label label = artist.getLabel(); + if ( label == null ) { + return null; + } + Studio studio = label.getStudio(); + if ( studio == null ) { + return null; + } + String name = studio.getName(); + if ( name == null ) { + return null; + } + return name; + } + + private String songArtistLabelStudioCity(Song song) { + if ( song == null ) { + return null; + } + Artist artist = song.getArtist(); + if ( artist == null ) { + return null; + } + Label label = artist.getLabel(); + if ( label == null ) { + return null; + } + Studio studio = label.getStudio(); + if ( studio == null ) { + return null; + } + String city = studio.getCity(); + if ( city == null ) { + return null; + } + return city; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java new file mode 100644 index 0000000000..f5deb649be --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java @@ -0,0 +1,236 @@ +/* + * 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.constructor.nestedtarget; + +import java.util.List; +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; +import org.mapstruct.ap.test.constructor.nestedsource.source.Artist; +import org.mapstruct.ap.test.constructor.nestedsource.source.Chart; +import org.mapstruct.ap.test.constructor.nestedsource.source.Label; +import org.mapstruct.ap.test.constructor.nestedsource.source.Song; +import org.mapstruct.ap.test.constructor.nestedsource.source.Studio; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2020-04-19T14:54:28+0200", + comments = "version: , compiler: javac, environment: Java 14.0.1 (Oracle Corporation)" +) +public class ChartEntryToArtistImpl extends ChartEntryToArtist { + + @Override + public Chart map(ChartEntry chartEntry) { + if ( chartEntry == null ) { + return null; + } + + Song song; + String name; + + song = chartEntryToSong( chartEntry ); + name = chartEntry.getChartName(); + + String type = null; + + Chart chart = new Chart( type, name, song ); + + return chart; + } + + @Override + public ChartEntry map(Chart chart) { + if ( chart == null ) { + return null; + } + + String chartName; + String songTitle; + String artistName; + String recordedAt; + String city; + int position; + + chartName = chart.getName(); + songTitle = chartSongTitle( chart ); + artistName = chartSongArtistName( chart ); + recordedAt = chartSongArtistLabelStudioName( chart ); + city = chartSongArtistLabelStudioCity( chart ); + position = mapPosition( chartSongPositions( chart ) ); + + ChartEntry chartEntry = new ChartEntry( chartName, songTitle, artistName, recordedAt, city, position ); + + return chartEntry; + } + + protected Studio chartEntryToStudio(ChartEntry chartEntry) { + if ( chartEntry == null ) { + return null; + } + + String name; + String city; + + name = chartEntry.getRecordedAt(); + city = chartEntry.getCity(); + + Studio studio = new Studio( name, city ); + + return studio; + } + + protected Label chartEntryToLabel(ChartEntry chartEntry) { + if ( chartEntry == null ) { + return null; + } + + Studio studio; + + studio = chartEntryToStudio( chartEntry ); + + String name = null; + + Label label = new Label( name, studio ); + + return label; + } + + protected Artist chartEntryToArtist(ChartEntry chartEntry) { + if ( chartEntry == null ) { + return null; + } + + Label label; + String name; + + label = chartEntryToLabel( chartEntry ); + name = chartEntry.getArtistName(); + + Artist artist = new Artist( name, label ); + + return artist; + } + + protected Song chartEntryToSong(ChartEntry chartEntry) { + if ( chartEntry == null ) { + return null; + } + + Artist artist; + String title; + List positions; + + artist = chartEntryToArtist( chartEntry ); + title = chartEntry.getSongTitle(); + positions = mapPosition( chartEntry.getPosition() ); + + Song song = new Song( artist, title, positions ); + + return song; + } + + private String chartSongTitle(Chart chart) { + if ( chart == null ) { + return null; + } + Song song = chart.getSong(); + if ( song == null ) { + return null; + } + String title = song.getTitle(); + if ( title == null ) { + return null; + } + return title; + } + + private String chartSongArtistName(Chart chart) { + if ( chart == null ) { + return null; + } + Song song = chart.getSong(); + if ( song == null ) { + return null; + } + Artist artist = song.getArtist(); + if ( artist == null ) { + return null; + } + String name = artist.getName(); + if ( name == null ) { + return null; + } + return name; + } + + private String chartSongArtistLabelStudioName(Chart chart) { + if ( chart == null ) { + return null; + } + Song song = chart.getSong(); + if ( song == null ) { + return null; + } + Artist artist = song.getArtist(); + if ( artist == null ) { + return null; + } + Label label = artist.getLabel(); + if ( label == null ) { + return null; + } + Studio studio = label.getStudio(); + if ( studio == null ) { + return null; + } + String name = studio.getName(); + if ( name == null ) { + return null; + } + return name; + } + + private String chartSongArtistLabelStudioCity(Chart chart) { + if ( chart == null ) { + return null; + } + Song song = chart.getSong(); + if ( song == null ) { + return null; + } + Artist artist = song.getArtist(); + if ( artist == null ) { + return null; + } + Label label = artist.getLabel(); + if ( label == null ) { + return null; + } + Studio studio = label.getStudio(); + if ( studio == null ) { + return null; + } + String city = studio.getCity(); + if ( city == null ) { + return null; + } + return city; + } + + private List chartSongPositions(Chart chart) { + if ( chart == null ) { + return null; + } + Song song = chart.getSong(); + if ( song == null ) { + return null; + } + List positions = song.getPositions(); + if ( positions == null ) { + return null; + } + return positions; + } +} From 42e0ec395b2cb7b4042dce75aad52476e90ece6d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 17 May 2020 12:17:36 +0200 Subject: [PATCH 050/597] Add dedicated action for Java EA builds and update main CI to use only 11, 13 and 14 --- .github/workflows/java-ea.yml | 21 +++++++++++++++++++++ .github/workflows/main.yml | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/java-ea.yml diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml new file mode 100644 index 0000000000..c20151eccd --- /dev/null +++ b/.github/workflows/java-ea.yml @@ -0,0 +1,21 @@ +name: Java EA + +on: [push] + +jobs: + test_jdk_ea: + strategy: + fail-fast: false + matrix: + java: [15-ea] + name: 'Linux JDK ${{ matrix.java }}' + runs-on: ubuntu-latest + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + - name: 'Set up JDK' + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + - name: 'Test' + run: ./mvnw -V -B --no-transfer-progress install -DskipDistribution=true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5596cadad9..d1f65d18f2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - java: [11, 12, 13, 14-ea, 15-ea] + java: [11, 13, 14] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: From f7c1182ae6a14f6cb716b4af01dd0cdf1bad4d46 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Sun, 17 May 2020 13:27:32 +0200 Subject: [PATCH 051/597] Updating my developer URL --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 3efe0ad8fb..5da1dfcbc3 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -51,7 +51,7 @@ gunnarmorling Gunnar Morling gunnar@mapstruct.org - http://www.gunnarmorling.de/ + https://www.morling.dev/ From 73a79cf0095e7fbbe11fa85fe24c26e2f73bb889 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 17 May 2020 12:08:00 +0200 Subject: [PATCH 052/597] #1857 Use flatten-maven-plugin to remove test dependencies from released pom For some reason when using annotationProcessorPaths IntelliJ includes the provided and test scoped dependencies on the annotation processor paths. With this change there would be no test dependencies that IntelliJ can add. --- .gitignore | 1 + parent/pom.xml | 34 ++++++++++++++++++++++++++++++++++ processor/pom.xml | 16 ++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/.gitignore b/.gitignore index 0363e0707e..a33f0014ee 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ test-output # Misc. .DS_Store checkstyle.cache +.flattened-pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 5da1dfcbc3..3e78dcfb84 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -578,6 +578,11 @@
    + + org.codehaus.mojo + flatten-maven-plugin + 1.2.2 + @@ -614,6 +619,7 @@ mvnw* **/*.asciidoc **/binding.xjb + **/*.flattened-pom.xml SLASHSTAR_STYLE @@ -702,6 +708,34 @@
    + + org.codehaus.mojo + flatten-maven-plugin + + + + flatten + package + + flatten + + + true + ossrh + + expand + + + + + flatten-clean + clean + + clean + + + +
    diff --git a/processor/pom.xml b/processor/pom.xml index dfe9bdbe69..c047301268 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -209,6 +209,21 @@ + + + org.codehaus.mojo + flatten-maven-plugin + + + + flatten + package + + flatten + + + + org.apache.maven.plugins maven-dependency-plugin @@ -334,6 +349,7 @@ jaxb-api 2.3.1 provided + true From b5fe96c9da4504693db73782b88ecb4fff65c0bc Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 17 May 2020 18:46:33 +0200 Subject: [PATCH 053/597] Add myself as developer --- parent/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/parent/pom.xml b/parent/pom.xml index 3e78dcfb84..b26cf8683d 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -53,6 +53,11 @@ gunnar@mapstruct.org https://www.morling.dev/ + + filiphr + Filip Hrisafov + https://github.com/filiphr/ + From 7b5a54971f9b9695a88b9d95dd9eadec42da5a19 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 18 May 2020 07:17:30 +0200 Subject: [PATCH 054/597] Add EnumTransformationStrategy SPI (#2089) Add a new custom EnumTransformationStrategy SPI which can be used for providing custom way of name based mapping for enums. Add 4 out of the box transformation strategies: * prefix - add a prefix to the name based enum mapping * stripPrefix - remove a prefix from the name based enum mapping * suffix - add a suffix to the name based enum mapping * stripSuffix - remove a suffix from the name based enum mapping This can be achieved by using the new `EnumMapping` e.g. Add suffix `_TYPE` to all enums: `@EnumMapping(nameTransformationStrategy = "suffix", configuration = "_TYPE")` With this it would be possible to achieve what is needed in #796, #1220, #1789. --- .../main/java/org/mapstruct/EnumMapping.java | 123 ++++++++++++++++++ .../java/org/mapstruct/MappingConstants.java | 30 +++++ .../chapter-13-using-mapstruct-spi.asciidoc | 16 +++ .../chapter-8-mapping-values.asciidoc | 103 +++++++++++++++ .../ap/internal/gem/GemGenerator.java | 2 + .../ap/internal/gem/MappingConstantsGem.java | 8 ++ .../internal/model/AbstractBaseBuilder.java | 1 + .../internal/model/MappingBuilderContext.java | 10 ++ .../ap/internal/model/ValueMappingMethod.java | 111 ++++++++++++++-- .../model/source/EnumMappingOptions.java | 99 ++++++++++++++ .../model/source/MappingMethodOptions.java | 23 ++++ .../internal/model/source/SourceMethod.java | 17 ++- .../DefaultModelElementProcessorContext.java | 8 ++ .../processor/MapperCreationProcessor.java | 7 +- .../processor/MethodRetrievalProcessor.java | 13 ++ .../processor/ModelElementProcessor.java | 4 + .../util/AnnotationProcessorContext.java | 31 +++++ .../mapstruct/ap/internal/util/Message.java | 1 + .../ap/spi/EnumTransformationStrategy.java | 44 +++++++ .../spi/PrefixEnumTransformationStrategy.java | 22 ++++ ...StripPrefixEnumTransformationStrategy.java | 25 ++++ ...StripSuffixEnumTransformationStrategy.java | 25 ++++ .../spi/SuffixEnumTransformationStrategy.java | 22 ++++ ...apstruct.ap.spi.EnumTransformationStrategy | 8 ++ .../mapstruct/ap/test/gem/ConstantTest.java | 6 + .../test/value/enum2string/OrderMapper.java | 2 + .../CheeseEnumToStringPrefixMapper.java | 34 +++++ .../CheeseEnumToStringSuffixMapper.java | 36 +++++ .../CheesePrefixMapper.java | 33 +++++ .../CheeseSuffixMapper.java | 36 +++++ .../value/nametransformation/CheeseType.java | 15 +++ .../CheeseTypeCustomSuffix.java | 15 +++ .../CheeseTypePrefixed.java | 16 +++ .../CheeseTypeSuffixed.java | 16 +++ .../CustomEnumTransformationStrategy.java | 30 +++++ .../EnumNameTransformationStrategyTest.java | 115 ++++++++++++++++ .../ErroneousNameTransformStrategyMapper.java | 22 ++++ 37 files changed, 1115 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/org/mapstruct/EnumMapping.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/EnumTransformationStrategy.java create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java create mode 100644 processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseEnumToStringPrefixMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseEnumToStringSuffixMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheesePrefixMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseSuffixMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CustomEnumTransformationStrategy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/ErroneousNameTransformStrategyMapper.java diff --git a/core/src/main/java/org/mapstruct/EnumMapping.java b/core/src/main/java/org/mapstruct/EnumMapping.java new file mode 100644 index 0000000000..c184448aad --- /dev/null +++ b/core/src/main/java/org/mapstruct/EnumMapping.java @@ -0,0 +1,123 @@ +/* + * 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; + +/** + * Configured the mapping between two value types. + *

    Example: Using a suffix for enums

    + *
    
    + * public enum CheeseType {
    + *     BRIE,
    + *     ROQUEFORT
    + * }
    + *
    + * public enum CheeseTypeSuffixed {
    + *     BRIE_TYPE,
    + *     ROQUEFORT_TYPE
    + * }
    + *
    + * @Mapper
    + * public interface CheeseMapper {
    + *
    + *     @EnumMapping(nameTransformationStrategy = "suffix", configuration = "_TYPE")
    + *     CheeseTypeSuffixed map(Cheese cheese);
    + *
    + *     @InheritInverseConfiguration
    + *     Cheese map(CheeseTypeSuffixed cheese);
    + *
    + * }
    + * 
    + *
    
    + * // generates
    + * public class CheeseMapperImpl implements CheeseMapper {
    + *
    + *     @Override
    + *     public CheeseTypeSuffixed map(Cheese cheese) {
    + *         if ( cheese == null ) {
    + *             return null;
    + *         }
    + *
    + *         CheeseTypeSuffixed cheeseTypeSuffixed;
    + *
    + *         switch ( cheese ) {
    + *             case BRIE:
    + *                 cheeseTypeSuffixed = CheeseTypeSuffixed.BRIE_TYPE;
    + *                 break;
    + *             case ROQUEFORT:
    + *                 cheeseTypeSuffixed = CheeseTypeSuffixed.ROQUEFORT_TYPE;
    + *                 break;
    + *             default:
    + *                 throw new IllegalArgumentException( "Unexpected enum constant: " + cheese );
    + *         }
    + *
    + *         return cheeseTypeSuffixed;
    + *     }
    + *
    + *     @Override
    + *     public Cheese map(CheeseTypeSuffixed cheese) {
    + *         if ( cheese == null ) {
    + *             return null;
    + *         }
    + *
    + *         CheeseType cheeseType;
    + *
    + *         switch ( cheese ) {
    + *             case BRIE_TYPE:
    + *                 cheeseType = CheeseType.BRIE;
    + *                 break;
    + *             case ROQUEFORT_TYPE:
    + *                 cheeseType = CheeseType.ROQUEFORT;
    + *                 break;
    + *             default:
    + *                 throw new IllegalArgumentException( "Unexpected enum constant: " + cheese );
    + *         }
    + *
    + *         return cheeseType;
    + *     }
    + * }
    + * 
    + * + * @author Filip Hrisafov + * @since 1.4 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface EnumMapping { + + /** + * Specifies the name transformation strategy that should be used for implicit mapping between enums. + * Known strategies are: + *
      + *
    • {@link MappingConstants#SUFFIX_TRANSFORMATION} - applies the given {@link #configuration()} as a + * suffix to the source enum
    • + *
    • {@link MappingConstants#STRIP_SUFFIX_TRANSFORMATION} - strips the the given {@link #configuration()} + * from the end of the source enum
    • + *
    • {@link MappingConstants#PREFIX_TRANSFORMATION} - applies the given {@link #configuration()} as a + * prefix to the source enum
    • + *
    • {@link MappingConstants#STRIP_PREFIX_TRANSFORMATION} - strips the given {@link #configuration()} from + * the start of the source enum
    • + *
    + * + * It is possible to use custom name transformation strategies by implementing the {@code + * EnumTransformationStrategy} SPI. + * + * @return the name transformation strategy + */ + String nameTransformationStrategy(); + + /** + * The configuration that should be passed on the appropriate name transformation strategy. + * e.g. a suffix that should be applied to the source enum when doing name based mapping. + * + * @return the configuration to use + */ + String configuration(); +} diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java index af52d58f26..2c5757038f 100644 --- a/core/src/main/java/org/mapstruct/MappingConstants.java +++ b/core/src/main/java/org/mapstruct/MappingConstants.java @@ -36,4 +36,34 @@ private MappingConstants() { */ public static final String ANY_UNMAPPED = ""; + /** + * In an {@link EnumMapping} this represent the enum transformation strategy that adds a suffix to the source enum. + * + * @since 1.4 + */ + public static final String SUFFIX_TRANSFORMATION = "suffix"; + + /** + * In an {@link EnumMapping} this represent the enum transformation strategy that strips a suffix from the source + * enum. + * + * @since 1.4 + */ + public static final String STRIP_SUFFIX_TRANSFORMATION = "stripSuffix"; + + /** + * In an {@link EnumMapping} this represent the enum transformation strategy that adds a prefix to the source enum. + * + * @since 1.4 + */ + public static final String PREFIX_TRANSFORMATION = "prefix"; + + /** + * In an {@link EnumMapping} this represent the enum transformation strategy that strips a prefix from the source + * enum. + * + * @since 1.4 + */ + public static final String STRIP_PREFIX_TRANSFORMATION = "stripPrefix"; + } diff --git a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc index b152570475..ddf8327f79 100644 --- a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc +++ b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc @@ -197,4 +197,20 @@ A nice example is to provide support for a custom builder strategy. ---- include::{processor-ap-main}/spi/NoOpBuilderProvider.java[tag=documentation] ---- +==== + + +[[custom-enum-transformation-strategy]] +=== Custom Enum Transformation Strategy + +MapStruct offers the possibility to other transformations strategies by implementing `EnumTransformationStrategy` via the Service Provider Interface (SPI). +A nice example is to provide support for a custom transformation strategy. + +.Custom Enum Transformation Strategy which lower-cases the value and applies a suffix +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +include::{processor-ap-test}/value/nametransformation/CustomEnumTransformationStrategy.java[tag=documentation] +---- ==== \ No newline at end of file diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index 7bfd6afa45..000fd06523 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -159,3 +159,106 @@ MapStruct supports enum to a String mapping along the same lines as is described 2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. 3. Similarity: `` will create a mapping for each target enum constant and proceed to the switch/default clause value. 4. Difference: A switch/default value needs to be provided to have a determined outcome (enum has a limited set of values, `String` has unlimited options). Failing to specify `` or ` will result in a warning. + +=== Custom name transformation + +When no `@ValueMapping`(s) are defined then each constant from the source enum is mapped to a constant with the same name in the target enum type. +However, there are cases where the source enum needs to be transformed before doing the mapping. +E.g. a suffix needs to be applied to map from the source into the target enum. + +.Enum types +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public enum CheeseType { + + BRIE, + ROQUEFORT +} + +public enum CheeseTypeSuffixed { + + BRIE_TYPE, + ROQUEFORT_TYPE +} +---- +==== + +.Enum mapping method with custom name transformation strategy +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface CheeseMapper { + + CheeseMapper INSTANCE = Mappers.getMapper( CheeseMapper.class ); + + @EnumMapping(nameTransformationStrategy = "suffix", configuration = "_TYPE") + CheeseTypeSuffixed map(CheeseType cheese); + + @InheritInverseConfiguration + CheeseType map(CheeseTypeSuffix cheese); +} +---- +==== + +.Enum mapping method with custom name transformation strategy result +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class CheeseSuffixMapperImpl implements CheeseSuffixMapper { + + @Override + public CheeseTypeSuffixed map(CheeseType cheese) { + if ( cheese == null ) { + return null; + } + + CheeseTypeSuffixed cheeseTypeSuffixed; + + switch ( cheese ) { + case BRIE: cheeseTypeSuffixed = CheeseTypeSuffixed.BRIE_TYPE; + break; + case ROQUEFORT: cheeseTypeSuffixed = CheeseTypeSuffixed.ROQUEFORT_TYPE; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); + } + + return cheeseTypeSuffixed; + } + + @Override + public CheeseType map(CheeseTypeSuffixed cheese) { + if ( cheese == null ) { + return null; + } + + CheeseType cheeseType; + + switch ( cheese ) { + case BRIE_TYPE: cheeseType = CheeseType.BRIE; + break; + case ROQUEFORT_TYPE: cheeseType = CheeseType.ROQUEFORT; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); + } + + return cheeseType; + } +} +---- +==== + +MapStruct provides the following out of the box enum name transformation strategies: + +* _suffix_ - Applies a suffix on the source enum +* _stripSuffix_ - Strips a suffix from the source enum +* _prefix_ - Applies a prefix on the source enum +* _stripPrefix_ - Strips a prefix from the source enum + +It is also possible to register custom strategies. +For more information on how to do that have a look at <> diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index b4d7758a10..524ea995d3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -14,6 +14,7 @@ import org.mapstruct.Builder; import org.mapstruct.Context; import org.mapstruct.DecoratedWith; +import org.mapstruct.EnumMapping; import org.mapstruct.InheritConfiguration; import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.IterableMapping; @@ -43,6 +44,7 @@ @GemDefinition(Mappings.class) @GemDefinition(IterableMapping.class) @GemDefinition(BeanMapping.class) +@GemDefinition(EnumMapping.class) @GemDefinition(MapMapping.class) @GemDefinition(TargetType.class) @GemDefinition(MappingTarget.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java index b988ccde40..685f5a543c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java @@ -20,4 +20,12 @@ private MappingConstantsGem() { public static final String ANY_REMAINING = ""; public static final String ANY_UNMAPPED = ""; + + public static final String SUFFIX_TRANSFORMATION = "suffix"; + + public static final String STRIP_SUFFIX_TRANSFORMATION = "stripSuffix"; + + public static final String PREFIX_TRANSFORMATION = "prefix"; + + public static final String STRIP_PREFIX_TRANSFORMATION = "stripPrefix"; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java index 7bf720de71..f2e657f8ef 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java @@ -86,6 +86,7 @@ Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, forgedMappingMethod = new ValueMappingMethod.Builder() .method( forgedMethod ) .valueMappings( forgedMethod.getOptions().getValueMappings() ) + .enumMapping( forgedMethod.getOptions().getEnumMappingOptions() ) .mappingContext( ctx ) .build(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index 2a752b11be..e8d649783c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -30,6 +30,7 @@ import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Services; +import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.MappingExclusionProvider; /** @@ -104,6 +105,7 @@ Assignment getTargetAssignment(Method mappingMethod, Type targetType, private final Types typeUtils; private final FormattingMessager messager; private final AccessorNamingUtils accessorNaming; + private final Map enumTransformationStrategies; private final Options options; private final TypeElement mapperTypeElement; private final List sourceModel; @@ -113,11 +115,13 @@ Assignment getTargetAssignment(Method mappingMethod, Type targetType, private final Map forgedMethodsUnderCreation = new HashMap<>(); + //CHECKSTYLE:OFF public MappingBuilderContext(TypeFactory typeFactory, Elements elementUtils, Types typeUtils, FormattingMessager messager, AccessorNamingUtils accessorNaming, + Map enumTransformationStrategies, Options options, MappingResolver mappingResolver, TypeElement mapper, @@ -128,12 +132,14 @@ public MappingBuilderContext(TypeFactory typeFactory, this.typeUtils = typeUtils; this.messager = messager; this.accessorNaming = accessorNaming; + this.enumTransformationStrategies = enumTransformationStrategies; this.options = options; this.mappingResolver = mappingResolver; this.mapperTypeElement = mapper; this.sourceModel = sourceModel; this.mapperReferences = mapperReferences; } + //CHECKSTYLE:ON /** * Returns a map which is used to track which forged methods are under creation. @@ -180,6 +186,10 @@ public AccessorNamingUtils getAccessorNaming() { return accessorNaming; } + public Map getEnumTransformationStrategies() { + return enumTransformationStrategies; + } + public Options getOptions() { return options; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 700cf4cd23..218c754e3d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -7,19 +7,23 @@ import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.gem.BeanMappingGem; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.source.EnumMappingOptions; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.ValueMappingOptions; -import org.mapstruct.ap.internal.gem.BeanMappingGem; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.spi.EnumTransformationStrategy; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_REMAINING; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_UNMAPPED; @@ -45,6 +49,8 @@ public static class Builder { private Method method; private MappingBuilderContext ctx; private ValueMappings valueMappings; + private EnumMappingOptions enumMapping; + private EnumTransformationStrategyInvoker enumTransformationInvoker; public Builder mappingContext(MappingBuilderContext mappingContext) { this.ctx = mappingContext; @@ -61,7 +67,18 @@ public Builder valueMappings(List valueMappings) { return this; } - public ValueMappingMethod build( ) { + public Builder enumMapping(EnumMappingOptions enumMapping) { + this.enumMapping = enumMapping; + return this; + } + + public ValueMappingMethod build() { + + if ( !enumMapping.isValid() ) { + return null; + } + + initializeEnumTransformationStrategy(); // initialize all relevant parameters List mappingEntries = new ArrayList<>(); @@ -99,6 +116,23 @@ else if ( sourceType.isString() && targetType.isEnumType() ) { ); } + private void initializeEnumTransformationStrategy() { + if ( !enumMapping.hasAnnotation() ) { + enumTransformationInvoker = EnumTransformationStrategyInvoker.DEFAULT; + } + else { + Map enumTransformationStrategies = + ctx.getEnumTransformationStrategies(); + + String nameTransformationStrategy = enumMapping.getNameTransformationStrategy(); + if ( enumTransformationStrategies.containsKey( nameTransformationStrategy ) ) { + enumTransformationInvoker = new EnumTransformationStrategyInvoker( enumTransformationStrategies.get( + nameTransformationStrategy ), enumMapping.getNameTransformationConfiguration() ); + } + } + + } + private List enumToEnumMapping(Method method, Type sourceType, Type targetType ) { List mappings = new ArrayList<>(); @@ -120,11 +154,36 @@ private List enumToEnumMapping(Method method, Type sourceType, Typ // add mappings based on name if ( !valueMappings.hasMapAnyUnmapped ) { - // get all target constants - List targetConstants = method.getReturnType().getEnumConstants(); + // We store the target constants in a map in order to support inherited inverse mapping + // When using a nameTransformationStrategy the transformation should be done on the target enum + // instead of the source enum + Map targetConstants = new LinkedHashMap<>(); + + boolean enumMappingInverse = enumMapping.isInverse(); + for ( String targetEnumConstant : method.getReturnType().getEnumConstants() ) { + if ( enumMappingInverse ) { + // If the mapping is inverse we have to change the target enum constant + targetConstants.put( + enumTransformationInvoker.transform( targetEnumConstant ), + targetEnumConstant + ); + } + else { + targetConstants.put( targetEnumConstant, targetEnumConstant ); + } + } + for ( String sourceConstant : new ArrayList<>( unmappedSourceConstants ) ) { - if ( targetConstants.contains( sourceConstant ) ) { - mappings.add( new MappingEntry( sourceConstant, sourceConstant ) ); + String targetConstant; + if ( !enumMappingInverse ) { + targetConstant = enumTransformationInvoker.transform( sourceConstant ); + } + else { + targetConstant = sourceConstant; + } + + if ( targetConstants.containsKey( targetConstant ) ) { + mappings.add( new MappingEntry( sourceConstant, targetConstants.get( targetConstant ) ) ); unmappedSourceConstants.remove( sourceConstant ); } } @@ -175,7 +234,8 @@ private List enumToStringMapping(Method method, Type sourceType ) // all remaining constants are mapped for ( String sourceConstant : unmappedSourceConstants ) { - mappings.add( new MappingEntry( sourceConstant, sourceConstant ) ); + String targetConstant = enumTransformationInvoker.transform( sourceConstant ); + mappings.add( new MappingEntry( sourceConstant, targetConstant ) ); } } return mappings; @@ -204,7 +264,8 @@ private List stringToEnumMapping(Method method, Type targetType ) // all remaining constants are mapped for ( String sourceConstant : unmappedSourceConstants ) { - mappings.add( new MappingEntry( sourceConstant, sourceConstant ) ); + String stringConstant = enumTransformationInvoker.transform( sourceConstant ); + mappings.add( new MappingEntry( stringConstant, sourceConstant ) ); } } return mappings; @@ -229,7 +290,8 @@ private boolean reportErrorIfMappedSourceEnumConstantsDontExist(Method method, T for ( ValueMappingOptions mappedConstant : valueMappings.regularValueMappings ) { if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { - ctx.getMessager().printMessage( method.getExecutable(), + ctx.getMessager().printMessage( + method.getExecutable(), mappedConstant.getMirror(), mappedConstant.getSourceAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, @@ -279,7 +341,8 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T for ( ValueMappingOptions mappedConstant : valueMappings.regularValueMappings ) { if ( !NULL.equals( mappedConstant.getTarget() ) && !targetEnumConstants.contains( mappedConstant.getTarget() ) ) { - ctx.getMessager().printMessage( method.getExecutable(), + ctx.getMessager().printMessage( + method.getExecutable(), mappedConstant.getMirror(), mappedConstant.getTargetAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, @@ -292,7 +355,8 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T if ( valueMappings.defaultTarget != null && !NULL.equals( valueMappings.defaultTarget.getTarget() ) && !targetEnumConstants.contains( valueMappings.defaultTarget.getTarget() ) ) { - ctx.getMessager().printMessage( method.getExecutable(), + ctx.getMessager().printMessage( + method.getExecutable(), valueMappings.defaultTarget.getMirror(), valueMappings.defaultTarget.getTargetAnnotationValue(), Message.VALUEMAPPING_NON_EXISTING_CONSTANT, @@ -318,6 +382,31 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T } } + private static class EnumTransformationStrategyInvoker { + + private static final EnumTransformationStrategyInvoker DEFAULT = new EnumTransformationStrategyInvoker( + null, + null + ); + + private final EnumTransformationStrategy transformationStrategy; + private final String configuration; + + private EnumTransformationStrategyInvoker( + EnumTransformationStrategy transformationStrategy, String configuration) { + this.transformationStrategy = transformationStrategy; + this.configuration = configuration; + } + + private String transform(String source) { + if ( transformationStrategy == null ) { + return source; + } + + return transformationStrategy.transform( source, configuration ); + } + } + private static class ValueMappings { List regularValueMappings = new ArrayList<>(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java new file mode 100644 index 0000000000..576474e680 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java @@ -0,0 +1,99 @@ +/* + * 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.internal.model.source; + +import java.util.Map; +import javax.lang.model.element.ExecutableElement; + +import org.mapstruct.ap.internal.gem.EnumMappingGem; +import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.spi.EnumTransformationStrategy; + +import static org.mapstruct.ap.internal.util.Message.ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY; + +/** + * @author Filip Hrisafov + */ +public class EnumMappingOptions extends DelegatingOptions { + + private final EnumMappingGem enumMapping; + private final boolean inverse; + private final boolean valid; + + private EnumMappingOptions(EnumMappingGem enumMapping, boolean inverse, boolean valid, DelegatingOptions next) { + super( next ); + this.enumMapping = enumMapping; + this.inverse = inverse; + this.valid = valid; + } + + @Override + public boolean hasAnnotation() { + return enumMapping != null; + } + + public boolean isValid() { + return valid; + } + + public String getNameTransformationStrategy() { + return enumMapping.nameTransformationStrategy().get(); + } + + public String getNameTransformationConfiguration() { + return enumMapping.configuration().get(); + } + + public boolean isInverse() { + return inverse; + } + + public EnumMappingOptions inverse() { + return new EnumMappingOptions( enumMapping, true, valid, next() ); + } + + public static EnumMappingOptions getInstanceOn(ExecutableElement method, MapperOptions mapperOptions, + Map enumTransformationStrategies, FormattingMessager messager) { + + EnumMappingGem enumMapping = EnumMappingGem.instanceOn( method ); + if ( enumMapping == null ) { + return new EnumMappingOptions( null, false, true, mapperOptions ); + } + else if ( !isConsistent( enumMapping, method, enumTransformationStrategies, messager ) ) { + return new EnumMappingOptions( null, false, false, mapperOptions ); + } + + return new EnumMappingOptions( + enumMapping, + false, + true, + mapperOptions + ); + } + + private static boolean isConsistent(EnumMappingGem gem, ExecutableElement method, + Map enumTransformationStrategies, FormattingMessager messager) { + + String strategy = gem.nameTransformationStrategy().getValue(); + + if ( !enumTransformationStrategies.containsKey( strategy ) ) { + String registeredStrategies = Strings.join( enumTransformationStrategies.keySet(), ", " ); + messager.printMessage( + method, + gem.mirror(), + gem.nameTransformationStrategy().getAnnotationValue(), + ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY, + strategy, + registeredStrategies + ); + + return false; + } + + return true; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 587dc5acf4..6c5cc1d04b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -31,6 +31,7 @@ public class MappingMethodOptions { null, null, null, + null, Collections.emptyList() ); @@ -39,18 +40,21 @@ public class MappingMethodOptions { private IterableMappingOptions iterableMapping; private MapMappingOptions mapMapping; private BeanMappingOptions beanMapping; + private EnumMappingOptions enumMappingOptions; private List valueMappings; private boolean fullyInitialized; public MappingMethodOptions(MapperOptions mapper, Set mappings, IterableMappingOptions iterableMapping, MapMappingOptions mapMapping, BeanMappingOptions beanMapping, + EnumMappingOptions enumMappingOptions, List valueMappings) { this.mapper = mapper; this.mappings = mappings; this.iterableMapping = iterableMapping; this.mapMapping = mapMapping; this.beanMapping = beanMapping; + this.enumMappingOptions = enumMappingOptions; this.valueMappings = valueMappings; } @@ -83,6 +87,10 @@ public BeanMappingOptions getBeanMapping() { return beanMapping; } + public EnumMappingOptions getEnumMappingOptions() { + return enumMappingOptions; + } + public List getValueMappings() { return valueMappings; } @@ -99,6 +107,10 @@ public void setBeanMapping(BeanMappingOptions beanMapping) { this.beanMapping = beanMapping; } + public void setEnumMappingOptions(EnumMappingOptions enumMappingOptions) { + this.enumMappingOptions = enumMappingOptions; + } + public void setValueMappings(List valueMappings) { this.valueMappings = valueMappings; } @@ -141,6 +153,17 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse setBeanMapping( BeanMappingOptions.forInheritance( templateOptions.getBeanMapping( ) ) ); } + if ( !getEnumMappingOptions().hasAnnotation() && templateOptions.getEnumMappingOptions().hasAnnotation() ) { + EnumMappingOptions newEnumMappingOptions; + if ( isInverse ) { + newEnumMappingOptions = templateOptions.getEnumMappingOptions().inverse(); + } + else { + newEnumMappingOptions = templateOptions.getEnumMappingOptions(); + } + setEnumMappingOptions( newEnumMappingOptions ); + } + if ( getValueMappings() == null ) { if ( templateOptions.getValueMappings() != null ) { // there were no mappings, so the inherited mappings are the new ones diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 3c1a5e1789..3551db8c51 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -84,6 +84,7 @@ public static class Builder { private MapperOptions mapper = null; private List prototypeMethods = Collections.emptyList(); private List valueMappings; + private EnumMappingOptions enumMappingOptions; private ParameterProvidedMethods contextProvidedMethods; public Builder setDeclaringMapper(Type declaringMapper) { @@ -136,6 +137,11 @@ public Builder setValueMappingOptionss(List valueMappings) return this; } + public Builder setEnumMappingOptions(EnumMappingOptions enumMappingOptions) { + this.enumMappingOptions = enumMappingOptions; + return this; + } + public Builder setTypeUtils(Types typeUtils) { this.typeUtils = typeUtils; return this; @@ -172,8 +178,15 @@ public SourceMethod build() { mappings = Collections.emptySet(); } - MappingMethodOptions mappingMethodOptions = - new MappingMethodOptions( mapper, mappings, iterableMapping, mapMapping, beanMapping, valueMappings ); + MappingMethodOptions mappingMethodOptions = new MappingMethodOptions( + mapper, + mappings, + iterableMapping, + mapMapping, + beanMapping, + enumMappingOptions, + valueMappings + ); return new SourceMethod( this, mappingMethodOptions ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java index a9f9eff4a4..adb48d0877 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java @@ -26,6 +26,7 @@ import org.mapstruct.ap.internal.util.RoundContext; import org.mapstruct.ap.internal.util.workarounds.TypesDecorator; import org.mapstruct.ap.internal.version.VersionInformation; +import org.mapstruct.ap.spi.EnumTransformationStrategy; /** * Default implementation of the processor context. @@ -41,6 +42,7 @@ public class DefaultModelElementProcessorContext implements ProcessorContext { private final VersionInformation versionInformation; private final Types delegatingTypes; private final AccessorNamingUtils accessorNaming; + private final RoundContext roundContext; public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options, RoundContext roundContext, Map notToBeImported) { @@ -50,6 +52,7 @@ public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvir this.accessorNaming = roundContext.getAnnotationProcessorContext().getAccessorNaming(); this.versionInformation = DefaultVersionInformation.fromProcessingEnvironment( processingEnvironment ); this.delegatingTypes = new TypesDecorator( processingEnvironment, versionInformation ); + this.roundContext = roundContext; this.typeFactory = new TypeFactory( processingEnvironment.getElementUtils(), delegatingTypes, @@ -90,6 +93,11 @@ public AccessorNamingUtils getAccessorNaming() { return accessorNaming; } + @Override + public Map getEnumTransformationStrategies() { + return roundContext.getAnnotationProcessorContext().getEnumTransformationStrategies(); + } + @Override public Options getOptions() { return options; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 7746dd6778..6882fba642 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -101,6 +101,7 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, L typeUtils, messager, accessorNaming, + context.getEnumTransformationStrategies(), options, new MappingResolverImpl( messager, @@ -336,8 +337,12 @@ else if ( method.isValueMapping() ) { .mappingContext( mappingContext ) .method( method ) .valueMappings( mappingOptions.getValueMappings() ) + .enumMapping( mappingOptions.getEnumMappingOptions() ) .build(); - mappingMethods.add( valueMappingMethod ); + + if ( valueMappingMethod != null ) { + mappingMethods.add( valueMappingMethod ); + } } else if ( method.isRemovedEnumMapping() ) { messager.printMessage( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 09a0cc95f2..b11c157b66 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -10,6 +10,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; @@ -27,6 +28,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.BeanMappingOptions; +import org.mapstruct.ap.internal.model.source.EnumMappingOptions; import org.mapstruct.ap.internal.model.source.IterableMappingOptions; import org.mapstruct.ap.internal.model.source.MapMappingOptions; import org.mapstruct.ap.internal.model.source.MapperOptions; @@ -47,6 +49,7 @@ import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.spi.EnumTransformationStrategy; import static org.mapstruct.ap.internal.util.Executables.getAllEnclosedExecutableElements; @@ -68,6 +71,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor enumTransformationStrategies; private Types typeUtils; private Elements elementUtils; @@ -78,6 +82,7 @@ public List process(ProcessorContext context, TypeElement mapperTy this.accessorNaming = context.getAccessorNaming(); this.typeUtils = context.getTypeUtils(); this.elementUtils = context.getElementUtils(); + this.enumTransformationStrategies = context.getEnumTransformationStrategies(); this.messager.note( 0, Message.PROCESSING_NOTE, mapperTypeElement ); @@ -273,6 +278,13 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, typeUtils ); + EnumMappingOptions enumMappingOptions = EnumMappingOptions.getInstanceOn( + method, + mapperOptions, + enumTransformationStrategies, + messager + ); + return new SourceMethod.Builder() .setExecutable( method ) .setParameters( parameters ) @@ -284,6 +296,7 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, .setIterableMappingOptions( iterableMappingOptions ) .setMapMappingOptions( mapMappingOptions ) .setValueMappingOptionss( getValueMappings( method ) ) + .setEnumMappingOptions( enumMappingOptions ) .setTypeUtils( typeUtils ) .setTypeFactory( typeFactory ) .setPrototypeMethods( prototypeMethods ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java index 74f0528d6e..4912037cd0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.internal.processor; +import java.util.Map; import javax.annotation.processing.Filer; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; @@ -16,6 +17,7 @@ import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.version.VersionInformation; +import org.mapstruct.ap.spi.EnumTransformationStrategy; /** * A processor which performs one task of the mapper generation, e.g. retrieving @@ -51,6 +53,8 @@ public interface ProcessorContext { AccessorNamingUtils getAccessorNaming(); + Map getEnumTransformationStrategies(); + Options getOptions(); VersionInformation getVersionInformation(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java index 8fdc6201e1..28db59abbe 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java @@ -9,7 +9,9 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.ServiceLoader; import javax.annotation.processing.Messager; @@ -22,6 +24,7 @@ import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy; import org.mapstruct.ap.spi.DefaultBuilderProvider; +import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.FreeBuilderAccessorNamingStrategy; import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; import org.mapstruct.ap.spi.ImmutablesBuilderProvider; @@ -39,6 +42,7 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen private BuilderProvider builderProvider; private AccessorNamingStrategy accessorNamingStrategy; private boolean initialized; + private Map enumTransformationStrategies; private AccessorNamingUtils accessorNaming; private Elements elementUtils; @@ -105,6 +109,28 @@ else if ( elementUtils.getTypeElement( FreeBuilderConstants.FREE_BUILDER_FQN ) ! ); } this.accessorNaming = new AccessorNamingUtils( this.accessorNamingStrategy ); + + + this.enumTransformationStrategies = new LinkedHashMap<>(); + ServiceLoader transformationStrategiesLoader = ServiceLoader.load( + EnumTransformationStrategy.class, + AnnotationProcessorContext.class.getClassLoader() + ); + + for ( EnumTransformationStrategy transformationStrategy : transformationStrategiesLoader ) { + String transformationStrategyName = transformationStrategy.getStrategyName(); + if ( enumTransformationStrategies.containsKey( transformationStrategyName ) ) { + throw new IllegalStateException( + "Multiple EnumTransformationStrategies are using the same ma,e. Found: " + + enumTransformationStrategies.get( transformationStrategyName ) + " and " + + transformationStrategy + " for name " + transformationStrategyName ); + } + + transformationStrategy.init( this ); + enumTransformationStrategies.put( transformationStrategyName, transformationStrategy ); + } + + this.initialized = true; } @@ -216,4 +242,9 @@ public BuilderProvider getBuilderProvider() { initialize(); return builderProvider; } + + public Map getEnumTransformationStrategies() { + initialize(); + return enumTransformationStrategies; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index d2cb9857ef..a26166a7ed 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -100,6 +100,7 @@ public enum Message { ENUMMAPPING_UNDEFINED_TARGET( "A target constant must be specified for mappings of an enum mapping method." ), ENUMMAPPING_UNMAPPED_SOURCES( "The following constants from the source enum have no corresponding constant in the target enum and must be be mapped via adding additional mappings: %s." ), ENUMMAPPING_REMOVED( "Mapping of Enums via @Mapping is removed. Please use @ValueMapping instead!" ), + ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY( "There is no registered EnumTransformationStrategy for '%s'. Registered strategies are: %s." ), LIFECYCLEMETHOD_AMBIGUOUS_PARAMETERS( "Lifecycle method has multiple matching parameters (e. g. same type), in this case please ensure to name the parameters in the lifecycle and mapping method identical. This lifecycle method will not be used for the mapping method '%s'.", Diagnostic.Kind.WARNING), diff --git a/processor/src/main/java/org/mapstruct/ap/spi/EnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/EnumTransformationStrategy.java new file mode 100644 index 0000000000..2c498fc3a8 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/EnumTransformationStrategy.java @@ -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.spi; + +import org.mapstruct.util.Experimental; + +/** + * A service provider interface for transforming name based value mappings. + * + * @author Filip Hrisafov + * @since 1.4 + */ +@Experimental("This SPI can have it's signature changed in subsequent releases") +public interface EnumTransformationStrategy { + + /** + * Initializes the enum transformation strategy with the MapStruct processing environment. + * + * @param processingEnvironment environment for facilities + */ + default void init(MapStructProcessingEnvironment processingEnvironment) { + + } + + /** + * The name of the strategy. + * + * @return the name of the strategy, never {@code null} + */ + String getStrategyName(); + + /** + * Transform the given value by using the given {@code configuration}. + * + * @param value the value that should be transformed + * @param configuration the configuration that should be used for the transformation + * + * @return the transformed value after applying the configuration + */ + String transform(String value, String configuration); +} diff --git a/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java new file mode 100644 index 0000000000..eaa7b75512 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java @@ -0,0 +1,22 @@ +/* + * 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.spi; + +/** + * @author Filip Hrisafov + */ +public class PrefixEnumTransformationStrategy implements EnumTransformationStrategy { + + @Override + public String getStrategyName() { + return "prefix"; + } + + @Override + public String transform(String value, String configuration) { + return configuration + value; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java new file mode 100644 index 0000000000..d7f03c9b7b --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.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.ap.spi; + +/** + * @author Filip Hrisafov + */ +public class StripPrefixEnumTransformationStrategy implements EnumTransformationStrategy { + + @Override + public String getStrategyName() { + return "stripPrefix"; + } + + @Override + public String transform(String value, String configuration) { + if ( value.startsWith( configuration ) ) { + return value.substring( configuration.length() ); + } + return value; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java new file mode 100644 index 0000000000..223837924e --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.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.ap.spi; + +/** + * @author Filip Hrisafov + */ +public class StripSuffixEnumTransformationStrategy implements EnumTransformationStrategy { + + @Override + public String getStrategyName() { + return "stripSuffix"; + } + + @Override + public String transform(String value, String configuration) { + if ( value.endsWith( configuration ) ) { + return value.substring( 0, value.length() - configuration.length() ); + } + return value; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java new file mode 100644 index 0000000000..29a109fadf --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java @@ -0,0 +1,22 @@ +/* + * 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.spi; + +/** + * @author Filip Hrisafov + */ +public class SuffixEnumTransformationStrategy implements EnumTransformationStrategy { + + @Override + public String getStrategyName() { + return "suffix"; + } + + @Override + public String transform(String value, String configuration) { + return value + configuration; + } +} diff --git a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy new file mode 100644 index 0000000000..1f52733ad9 --- /dev/null +++ b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy @@ -0,0 +1,8 @@ +# Copyright MapStruct Authors. +# +# Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +org.mapstruct.ap.spi.PrefixEnumTransformationStrategy +org.mapstruct.ap.spi.StripPrefixEnumTransformationStrategy +org.mapstruct.ap.spi.StripSuffixEnumTransformationStrategy +org.mapstruct.ap.spi.SuffixEnumTransformationStrategy diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java index b33f00964b..a2a6afefde 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java @@ -23,5 +23,11 @@ public void constantsShouldBeEqual() { assertThat( MappingConstants.ANY_REMAINING ).isEqualTo( MappingConstantsGem.ANY_REMAINING ); assertThat( MappingConstants.ANY_UNMAPPED ).isEqualTo( MappingConstantsGem.ANY_UNMAPPED ); assertThat( MappingConstants.NULL ).isEqualTo( MappingConstantsGem.NULL ); + assertThat( MappingConstants.SUFFIX_TRANSFORMATION ).isEqualTo( MappingConstantsGem.SUFFIX_TRANSFORMATION ); + assertThat( MappingConstants.STRIP_SUFFIX_TRANSFORMATION ) + .isEqualTo( MappingConstantsGem.STRIP_SUFFIX_TRANSFORMATION ); + assertThat( MappingConstants.PREFIX_TRANSFORMATION ).isEqualTo( MappingConstantsGem.PREFIX_TRANSFORMATION ); + assertThat( MappingConstants.STRIP_PREFIX_TRANSFORMATION ) + .isEqualTo( MappingConstantsGem.STRIP_PREFIX_TRANSFORMATION ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java index 35d9f57f90..d7e6f1062c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/OrderMapper.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.value.enum2string; +import org.mapstruct.EnumMapping; import org.mapstruct.Mapper; import org.mapstruct.MappingConstants; import org.mapstruct.ValueMapping; @@ -27,6 +28,7 @@ public interface OrderMapper { }) String mapNormal(OrderType orderType); + @EnumMapping(nameTransformationStrategy = "prefix", configuration = "PREFIX_") @ValueMappings({ @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ), @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseEnumToStringPrefixMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseEnumToStringPrefixMapper.java new file mode 100644 index 0000000000..aa6d220711 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseEnumToStringPrefixMapper.java @@ -0,0 +1,34 @@ +/* + * 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.value.nametransformation; + +import org.mapstruct.EnumMapping; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CheeseEnumToStringPrefixMapper { + + CheeseEnumToStringPrefixMapper INSTANCE = Mappers.getMapper( CheeseEnumToStringPrefixMapper.class ); + + @EnumMapping(nameTransformationStrategy = MappingConstants.PREFIX_TRANSFORMATION, configuration = "SWISS_") + String map(CheeseType cheese); + + @InheritInverseConfiguration + @EnumMapping(nameTransformationStrategy = MappingConstants.PREFIX_TRANSFORMATION, configuration = "FRENCH_") + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.NULL) + CheeseType map(String cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.STRIP_PREFIX_TRANSFORMATION, configuration = "SWISS_") + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.NULL) + CheeseTypePrefixed mapStripPrefix(String cheese); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseEnumToStringSuffixMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseEnumToStringSuffixMapper.java new file mode 100644 index 0000000000..a6be3ae2d0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseEnumToStringSuffixMapper.java @@ -0,0 +1,36 @@ +/* + * 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.value.nametransformation; + +import org.mapstruct.EnumMapping; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CheeseEnumToStringSuffixMapper { + + CheeseEnumToStringSuffixMapper INSTANCE = Mappers.getMapper( CheeseEnumToStringSuffixMapper.class ); + + @EnumMapping(nameTransformationStrategy = MappingConstants.SUFFIX_TRANSFORMATION, configuration = "_CHEESE_TYPE") + String map(CheeseType cheese); + + @InheritInverseConfiguration + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.NULL) + CheeseType map(String cheese); + + @EnumMapping( + nameTransformationStrategy = MappingConstants.STRIP_SUFFIX_TRANSFORMATION, + configuration = "_CHEESE_TYPE" + ) + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.NULL) + CheeseTypeSuffixed mapStripSuffix(String cheese); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheesePrefixMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheesePrefixMapper.java new file mode 100644 index 0000000000..7cb441bb08 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheesePrefixMapper.java @@ -0,0 +1,33 @@ +/* + * 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.value.nametransformation; + +import org.mapstruct.EnumMapping; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CheesePrefixMapper { + + CheesePrefixMapper INSTANCE = Mappers.getMapper( CheesePrefixMapper.class ); + + @EnumMapping(nameTransformationStrategy = MappingConstants.PREFIX_TRANSFORMATION, configuration = "SWISS_") + CheeseTypePrefixed map(CheeseType cheese); + + @InheritInverseConfiguration + @ValueMapping(source = "DEFAULT", target = MappingConstants.NULL) + CheeseType mapInheritInverse(CheeseTypePrefixed cheese); + + @ValueMapping(source = "DEFAULT", target = MappingConstants.NULL) + @EnumMapping(nameTransformationStrategy = MappingConstants.STRIP_PREFIX_TRANSFORMATION, configuration = "SWISS_") + CheeseType mapStripPrefix(CheeseTypePrefixed cheese); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseSuffixMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseSuffixMapper.java new file mode 100644 index 0000000000..b50ed0acba --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseSuffixMapper.java @@ -0,0 +1,36 @@ +/* + * 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.value.nametransformation; + +import org.mapstruct.EnumMapping; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CheeseSuffixMapper { + + CheeseSuffixMapper INSTANCE = Mappers.getMapper( CheeseSuffixMapper.class ); + + @EnumMapping(nameTransformationStrategy = MappingConstants.SUFFIX_TRANSFORMATION, configuration = "_CHEESE_TYPE") + CheeseTypeSuffixed map(CheeseType cheese); + + @InheritInverseConfiguration + @ValueMapping(source = "DEFAULT", target = MappingConstants.NULL) + CheeseType mapInheritInverse(CheeseTypeSuffixed cheese); + + @EnumMapping( + nameTransformationStrategy = MappingConstants.STRIP_SUFFIX_TRANSFORMATION, + configuration = "_CHEESE_TYPE" + ) + @ValueMapping(source = "DEFAULT", target = MappingConstants.NULL) + CheeseType mapStripSuffix(CheeseTypeSuffixed cheese); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java new file mode 100644 index 0000000000..d7a1d2e288 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java @@ -0,0 +1,15 @@ +/* + * 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.value.nametransformation; + +/** + * @author Filip Hrisafov + */ +public enum CheeseType { + + BRIE, + ROQUEFORT +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java new file mode 100644 index 0000000000..b7eccd3d42 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java @@ -0,0 +1,15 @@ +/* + * 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.value.nametransformation; + +/** + * @author Filip Hrisafov + */ +public enum CheeseTypeCustomSuffix { + + brie_TYPE, + roquefort_TYPE, +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java new file mode 100644 index 0000000000..5f5b989c7b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java @@ -0,0 +1,16 @@ +/* + * 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.value.nametransformation; + +/** + * @author Filip Hrisafov + */ +public enum CheeseTypePrefixed { + + DEFAULT, + SWISS_BRIE, + SWISS_ROQUEFORT, +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java new file mode 100644 index 0000000000..b2b264b78b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java @@ -0,0 +1,16 @@ +/* + * 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.value.nametransformation; + +/** + * @author Filip Hrisafov + */ +public enum CheeseTypeSuffixed { + + DEFAULT, + BRIE_CHEESE_TYPE, + ROQUEFORT_CHEESE_TYPE, +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CustomEnumTransformationStrategy.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CustomEnumTransformationStrategy.java new file mode 100644 index 0000000000..f7cfe44375 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CustomEnumTransformationStrategy.java @@ -0,0 +1,30 @@ +/* + * 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.value.nametransformation; + +// tag::documentation[] + +import org.mapstruct.ap.spi.EnumTransformationStrategy; + +// end::documentation[] + +/** + * @author Filip Hrisafov + */ +// tag::documentation[] +public class CustomEnumTransformationStrategy implements EnumTransformationStrategy { + + @Override + public String getStrategyName() { + return "custom"; + } + + @Override + public String transform(String value, String configuration) { + return value.toLowerCase() + configuration; + } +} +// end::documentation[] diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java new file mode 100644 index 0000000000..d5de5e872f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java @@ -0,0 +1,115 @@ +/* + * 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.value.nametransformation; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithServiceImplementation; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + CheeseType.class, + CheeseTypeSuffixed.class, + CheeseTypePrefixed.class, + CheeseTypeCustomSuffix.class, +}) +public class EnumNameTransformationStrategyTest { + + @Test + @WithClasses({ + CheeseSuffixMapper.class + }) + public void shouldApplySuffixAndStripSuffixOnEnumToEnumMapping() { + CheeseSuffixMapper mapper = CheeseSuffixMapper.INSTANCE; + + assertThat( mapper.map( CheeseType.BRIE ) ) + .isEqualTo( CheeseTypeSuffixed.BRIE_CHEESE_TYPE ); + assertThat( mapper.mapInheritInverse( CheeseTypeSuffixed.BRIE_CHEESE_TYPE ) ) + .isEqualTo( CheeseType.BRIE ); + assertThat( mapper.mapStripSuffix( CheeseTypeSuffixed.BRIE_CHEESE_TYPE ) ) + .isEqualTo( CheeseType.BRIE ); + } + + @Test + @WithClasses({ + CheesePrefixMapper.class + }) + public void shouldApplyPrefixAndStripPrefixOnEnumToEnumMapping() { + CheesePrefixMapper mapper = CheesePrefixMapper.INSTANCE; + + assertThat( mapper.map( CheeseType.BRIE ) ) + .isEqualTo( CheeseTypePrefixed.SWISS_BRIE ); + assertThat( mapper.mapInheritInverse( CheeseTypePrefixed.SWISS_BRIE ) ) + .isEqualTo( CheeseType.BRIE ); + assertThat( mapper.mapStripPrefix( CheeseTypePrefixed.SWISS_BRIE ) ) + .isEqualTo( CheeseType.BRIE ); + } + + @Test + @WithClasses({ + CheeseEnumToStringSuffixMapper.class + }) + public void shouldApplySuffixAndStripSuffixOnEnumToStringMapping() { + CheeseEnumToStringSuffixMapper mapper = CheeseEnumToStringSuffixMapper.INSTANCE; + + assertThat( mapper.map( CheeseType.BRIE ) ).isEqualTo( "BRIE_CHEESE_TYPE" ); + assertThat( mapper.map( "BRIE_CHEESE_TYPE" ) ).isEqualTo( CheeseType.BRIE ); + assertThat( mapper.mapStripSuffix( "BRIE" ) ).isEqualTo( CheeseTypeSuffixed.BRIE_CHEESE_TYPE ); + assertThat( mapper.mapStripSuffix( "DEFAULT" ) ).isEqualTo( CheeseTypeSuffixed.DEFAULT ); + } + + @Test + @WithClasses({ + CheeseEnumToStringPrefixMapper.class + }) + public void shouldApplyPrefixAndStripPrefixOnEnumToStringMapping() { + CheeseEnumToStringPrefixMapper mapper = CheeseEnumToStringPrefixMapper.INSTANCE; + + assertThat( mapper.map( CheeseType.BRIE ) ).isEqualTo( "SWISS_BRIE" ); + assertThat( mapper.map( "FRENCH_BRIE" ) ).isEqualTo( CheeseType.BRIE ); + assertThat( mapper.mapStripPrefix( "BRIE" ) ).isEqualTo( CheeseTypePrefixed.SWISS_BRIE ); + assertThat( mapper.mapStripPrefix( "DEFAULT" ) ).isEqualTo( CheeseTypePrefixed.DEFAULT ); + } + + @Test + @WithClasses({ + ErroneousNameTransformStrategyMapper.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = ErroneousNameTransformStrategyMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = "There is no registered EnumTransformationStrategy for 'custom'. Registered strategies are:" + + " prefix, stripPrefix, stripSuffix, suffix." + ) + } + ) + public void shouldGiveCompileErrorWhenUsingUnknownNameTransformStrategy() { + } + + @Test + @WithClasses({ + ErroneousNameTransformStrategyMapper.class + }) + @WithServiceImplementation(CustomEnumTransformationStrategy.class) + public void shouldUseCustomEnumTransformationStrategy() { + assertThat( ErroneousNameTransformStrategyMapper.INSTANCE.map( CheeseType.BRIE ) ) + .isEqualTo( CheeseTypeCustomSuffix.brie_TYPE ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/ErroneousNameTransformStrategyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/ErroneousNameTransformStrategyMapper.java new file mode 100644 index 0000000000..cb59089b38 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/ErroneousNameTransformStrategyMapper.java @@ -0,0 +1,22 @@ +/* + * 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.value.nametransformation; + +import org.mapstruct.EnumMapping; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousNameTransformStrategyMapper { + + ErroneousNameTransformStrategyMapper INSTANCE = Mappers.getMapper( ErroneousNameTransformStrategyMapper.class ); + + @EnumMapping(nameTransformationStrategy = "custom", configuration = "_TYPE") + CheeseTypeCustomSuffix map(CheeseType cheese); +} From c23592a7fe30bf4b4a779caadfeb9edd4578c82c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 25 May 2020 21:31:29 +0200 Subject: [PATCH 055/597] Add EnumNamingStrategy SPI (#2100) Add a new EnumNamingStrategy SPI which can be used for customising the way enums are matched by name. It is similar to the AccessorNamingStrategy such that it allows implementors to provide a custom way of defining a property. Related to #796, #1220, #1789 and #1667 --- .../chapter-13-using-mapstruct-spi.asciidoc | 146 ++++++++++++++++++ .../internal/model/MappingBuilderContext.java | 8 + .../ap/internal/model/ValueMappingMethod.java | 77 ++++++--- .../DefaultModelElementProcessorContext.java | 6 + .../processor/MapperCreationProcessor.java | 1 + .../processor/ModelElementProcessor.java | 3 + .../util/AnnotationProcessorContext.java | 17 ++ .../mapstruct/ap/internal/util/Message.java | 1 + .../ap/spi/DefaultEnumNamingStrategy.java | 35 +++++ .../mapstruct/ap/spi/EnumNamingStrategy.java | 48 ++++++ .../ap/test/value/spi/CheeseType.java | 15 ++ .../ap/test/value/spi/CustomCheeseMapper.java | 34 ++++ .../ap/test/value/spi/CustomCheeseType.java | 17 ++ .../ap/test/value/spi/CustomEnumMarker.java | 12 ++ .../value/spi/CustomEnumNamingStrategy.java | 54 +++++++ .../spi/CustomEnumNamingStrategyTest.java | 123 +++++++++++++++ .../CustomErroneousEnumNamingStrategy.java | 54 +++++++ ...CustomErroneousEnumNamingStrategyTest.java | 102 ++++++++++++ .../spi/OverridesCustomCheeseMapper.java | 45 ++++++ .../value/spi/CustomCheeseMapperImpl.java | 138 +++++++++++++++++ 20 files changed, 918 insertions(+), 18 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CheeseType.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseType.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMarker.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategyTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategyTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/OverridesCustomCheeseMapper.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java diff --git a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc index ddf8327f79..bc9cf7646c 100644 --- a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc +++ b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc @@ -199,6 +199,152 @@ include::{processor-ap-main}/spi/NoOpBuilderProvider.java[tag=documentation] ---- ==== +[[custom-enum-naming-strategy]] +=== Custom Enum Naming Strategy + +MapStruct offers the possibility to override the `EnumNamingStrategy` via the Service Provider Interface (SPI). +This can be used when you have certain enums that follow some conventions within your organization. +For example all enums which implement an interface named `CustomEnumMarker` are prefixed with `CUSTOM_` +and the default value for them when mapping from `null` is `UNSPECIFIED` + +.Normal Enum +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public enum CheeseType { + BRIE, + ROQUEFORT; +} +---- +==== + +.Custom marker enum +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public enum CustomCheeseType implements CustomEnumMarker { + + UNSPECIFIED, + CUSTOM_BRIE, + CUSTOM_ROQUEFORT; +} +---- +==== + +We want `CheeseType` and `CustomCheeseType` to be mapped without the need to manually define the value mappings: + +.Custom enum mapping +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface CheeseTypeMapper { + + CheeseType map(CustomCheeseType cheese); + + CustomCheeseType map(CheeseType cheese); +} +---- +==== + +This can be achieved with implementing the SPI `org.mapstruct.ap.spi.EnumNamingStrategy` as in the following example. +Here’s an implemented `org.mapstruct.ap.spi.EnumNamingStrategy`: + +.Custom enum naming strategy +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public class CustomEnumNamingStrategy extends DefaultEnumNamingStrategy { + + @Override + public String getDefaultNullEnumConstant(TypeElement enumType) { + if ( isCustomEnum( enumType ) ) { + return "UNSPECIFIED"; + } + + return super.getDefaultNullEnumConstant( enumType ); + } + + @Override + public String getEnumConstant(TypeElement enumType, String enumConstant) { + if ( isCustomEnum( enumType ) ) { + return getCustomEnumConstant( enumConstant ); + } + return super.getEnumConstant( enumType, enumConstant ); + } + protected String getCustomEnumConstant(String enumConstant) { + if ( "UNSPECIFIED".equals( enumConstant ) ) { + return MappingConstantsGem.NULL; + } + return enumConstant.replace( "CUSTOM_", "" ); + } + protected boolean isCustomEnum(TypeElement enumType) { + for ( TypeMirror enumTypeInterface : enumType.getInterfaces() ) { + if ( typeUtils.asElement( enumTypeInterface ).getSimpleName().contentEquals( "CustomEnumMarker" ) ) { + return true; + } + } + return false; + } +} +---- +==== + +The generated code then for the `CheeseMapper` looks like: + +.Generated CheeseTypeMapper +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public class CheeseTypeMapperImpl implements CheeseTypeMapper { + + @Override + public CheeseType map(CustomCheeseType cheese) { + if ( cheese == null ) { + return null; + } + + CheeseType cheeseType; + + switch ( cheese ) { + case UNRECOGNIZED: cheeseType = null; + break; + case CUSTOM_BRIE: cheeseType = CheeseType.BRIE; + break; + case CUSTOM_ROQUEFORT: cheeseType = CheeseType.ROQUEFORT; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); + } + + return cheeseType; + } + + @Override + public CustomCheeseType map(CheeseType cheese) { + if ( cheese == null ) { + return CustomCheeseType.UNSPECIFIED; + } + + CustomCheeseType customCheeseType; + + switch ( cheese ) { + case BRIE: customCheeseType = CustomCheeseType.CUSTOM_BRIE; + break; + case ROQUEFORT: customCheeseType = CustomCheeseType.CUSTOM_ROQUEFORT; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); + } + + return customCheeseType; + } +} +---- +==== [[custom-enum-transformation-strategy]] === Custom Enum Transformation Strategy diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index e8d649783c..53317aefa0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -30,6 +30,7 @@ import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Services; +import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.MappingExclusionProvider; @@ -105,6 +106,7 @@ Assignment getTargetAssignment(Method mappingMethod, Type targetType, private final Types typeUtils; private final FormattingMessager messager; private final AccessorNamingUtils accessorNaming; + private final EnumNamingStrategy enumNamingStrategy; private final Map enumTransformationStrategies; private final Options options; private final TypeElement mapperTypeElement; @@ -121,6 +123,7 @@ public MappingBuilderContext(TypeFactory typeFactory, Types typeUtils, FormattingMessager messager, AccessorNamingUtils accessorNaming, + EnumNamingStrategy enumNamingStrategy, Map enumTransformationStrategies, Options options, MappingResolver mappingResolver, @@ -132,6 +135,7 @@ public MappingBuilderContext(TypeFactory typeFactory, this.typeUtils = typeUtils; this.messager = messager; this.accessorNaming = accessorNaming; + this.enumNamingStrategy = enumNamingStrategy; this.enumTransformationStrategies = enumTransformationStrategies; this.options = options; this.mappingResolver = mappingResolver; @@ -186,6 +190,10 @@ public AccessorNamingUtils getAccessorNaming() { return accessorNaming; } + public EnumNamingStrategy getEnumNamingStrategy() { + return enumNamingStrategy; + } + public Map getEnumTransformationStrategies() { return enumTransformationStrategies; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 218c754e3d..adfa5779e8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -8,9 +8,11 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; @@ -86,6 +88,12 @@ public ValueMappingMethod build() { Type sourceType = first( method.getSourceParameters() ).getType(); Type targetType = method.getResultType(); + if ( targetType.isEnumType() && valueMappings.nullTarget == null ) { + // If null target is not set it means that the user has not explicitly defined a mapping for null + valueMappings.nullValueTarget = ctx.getEnumNamingStrategy() + .getDefaultNullEnumConstant( targetType.getTypeElement() ); + } + // enum-to-enum if ( sourceType.isEnumType() && targetType.isEnumType() ) { mappingEntries.addAll( enumToEnumMapping( method, sourceType, targetType ) ); @@ -145,9 +153,7 @@ private List enumToEnumMapping(Method method, Type sourceType, Typ // Start to fill the mappings with the defined value mappings for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) { - String target = - NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); - mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); + mappings.add( new MappingEntry( valueMapping.getSource(), valueMapping.getTarget() ) ); unmappedSourceConstants.remove( valueMapping.getSource() ); } @@ -160,32 +166,40 @@ private List enumToEnumMapping(Method method, Type sourceType, Typ Map targetConstants = new LinkedHashMap<>(); boolean enumMappingInverse = enumMapping.isInverse(); + TypeElement targetTypeElement = method.getReturnType().getTypeElement(); for ( String targetEnumConstant : method.getReturnType().getEnumConstants() ) { + String targetNameEnum = getEnumConstant( targetTypeElement, targetEnumConstant ); if ( enumMappingInverse ) { // If the mapping is inverse we have to change the target enum constant targetConstants.put( - enumTransformationInvoker.transform( targetEnumConstant ), + enumTransformationInvoker.transform( targetNameEnum ), targetEnumConstant ); } else { - targetConstants.put( targetEnumConstant, targetEnumConstant ); + targetConstants.put( targetNameEnum, targetEnumConstant ); } } + TypeElement sourceTypeElement = sourceType.getTypeElement(); for ( String sourceConstant : new ArrayList<>( unmappedSourceConstants ) ) { + String sourceNameConstant = getEnumConstant( sourceTypeElement, sourceConstant ); String targetConstant; if ( !enumMappingInverse ) { - targetConstant = enumTransformationInvoker.transform( sourceConstant ); + targetConstant = enumTransformationInvoker.transform( sourceNameConstant ); } else { - targetConstant = sourceConstant; + targetConstant = sourceNameConstant; } if ( targetConstants.containsKey( targetConstant ) ) { mappings.add( new MappingEntry( sourceConstant, targetConstants.get( targetConstant ) ) ); unmappedSourceConstants.remove( sourceConstant ); } + else if ( NULL.equals( targetConstant ) ) { + mappings.add( new MappingEntry( sourceConstant, null ) ); + unmappedSourceConstants.remove( sourceConstant ); + } } if ( valueMappings.defaultTarget == null && !unmappedSourceConstants.isEmpty() ) { @@ -223,18 +237,18 @@ private List enumToStringMapping(Method method, Type sourceType ) // Start to fill the mappings with the defined valuemappings for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) { - String target = - NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); - mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); + mappings.add( new MappingEntry( valueMapping.getSource(), valueMapping.getTarget() ) ); unmappedSourceConstants.remove( valueMapping.getSource() ); } // add mappings based on name if ( !valueMappings.hasMapAnyUnmapped ) { + TypeElement sourceTypeElement = sourceType.getTypeElement(); // all remaining constants are mapped for ( String sourceConstant : unmappedSourceConstants ) { - String targetConstant = enumTransformationInvoker.transform( sourceConstant ); + String sourceNameConstant = getEnumConstant( sourceTypeElement, sourceConstant ); + String targetConstant = enumTransformationInvoker.transform( sourceNameConstant ); mappings.add( new MappingEntry( sourceConstant, targetConstant ) ); } } @@ -250,27 +264,35 @@ private List stringToEnumMapping(Method method, Type targetType ) if ( sourceErrorOccurred || mandatoryMissing ) { return mappings; } + Set mappedSources = new LinkedHashSet<>(); // Start to fill the mappings with the defined valuemappings for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) { - String target = - NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); - mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); + mappedSources.add( valueMapping.getSource() ); + mappings.add( new MappingEntry( valueMapping.getSource(), valueMapping.getTarget() ) ); unmappedSourceConstants.remove( valueMapping.getSource() ); } // add mappings based on name if ( !valueMappings.hasMapAnyUnmapped ) { - + mappedSources.add( NULL ); + TypeElement targetTypeElement = targetType.getTypeElement(); // all remaining constants are mapped for ( String sourceConstant : unmappedSourceConstants ) { - String stringConstant = enumTransformationInvoker.transform( sourceConstant ); - mappings.add( new MappingEntry( stringConstant, sourceConstant ) ); + String sourceNameConstant = getEnumConstant( targetTypeElement, sourceConstant ); + String stringConstant = enumTransformationInvoker.transform( sourceNameConstant ); + if ( !mappedSources.contains( stringConstant ) ) { + mappings.add( new MappingEntry( stringConstant, sourceConstant ) ); + } } } return mappings; } + private String getEnumConstant(TypeElement typeElement, String enumConstant) { + return ctx.getEnumNamingStrategy().getEnumConstant( typeElement, enumConstant ); + } + private SelectionParameters getSelectionParameters(Method method, Types typeUtils) { BeanMappingGem beanMapping = BeanMappingGem.instanceOn( method.getExecutable() ); if ( beanMapping != null ) { @@ -377,6 +399,18 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T ); foundIncorrectMapping = true; } + else if ( valueMappings.nullTarget == null && valueMappings.nullValueTarget != null + && !targetEnumConstants.contains( valueMappings.nullValueTarget ) ) { + // if there is no nullTarget, but nullValueTarget has a value it means that there was an SPI + // which returned an enum for the target enum + ctx.getMessager().printMessage( + method.getExecutable(), + Message.VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI, + valueMappings.nullValueTarget, + method.getReturnType(), + ctx.getEnumNamingStrategy() + ); + } return !foundIncorrectMapping; } @@ -417,6 +451,7 @@ private static class ValueMappings { boolean hasMapAnyUnmapped = false; boolean hasMapAnyRemaining = false; boolean hasDefaultValue = false; + boolean hasNullValue = false; ValueMappings(List valueMappings) { @@ -436,6 +471,7 @@ else if ( ANY_UNMAPPED.equals( valueMapping.getSource() ) ) { else if ( NULL.equals( valueMapping.getSource() ) ) { nullTarget = valueMapping; nullValueTarget = getValue( nullTarget ); + hasNullValue = true; } else { regularValueMappings.add( valueMapping ); @@ -489,7 +525,12 @@ public static class MappingEntry { MappingEntry( String source, String target ) { this.source = source; - this.target = target; + if ( !NULL.equals( target ) ) { + this.target = target; + } + else { + this.target = null; + } } public String getSource() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java index adb48d0877..3b5cb1b036 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java @@ -26,6 +26,7 @@ import org.mapstruct.ap.internal.util.RoundContext; import org.mapstruct.ap.internal.util.workarounds.TypesDecorator; import org.mapstruct.ap.internal.version.VersionInformation; +import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; /** @@ -98,6 +99,11 @@ public Map getEnumTransformationStrategies() return roundContext.getAnnotationProcessorContext().getEnumTransformationStrategies(); } + @Override + public EnumNamingStrategy getEnumNamingStrategy() { + return roundContext.getAnnotationProcessorContext().getEnumNamingStrategy(); + } + @Override public Options getOptions() { return options; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 6882fba642..c22377f984 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -101,6 +101,7 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, L typeUtils, messager, accessorNaming, + context.getEnumNamingStrategy(), context.getEnumTransformationStrategies(), options, new MappingResolverImpl( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java index 4912037cd0..0689af54a5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java @@ -17,6 +17,7 @@ import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.version.VersionInformation; +import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; /** @@ -55,6 +56,8 @@ public interface ProcessorContext { Map getEnumTransformationStrategies(); + EnumNamingStrategy getEnumNamingStrategy(); + Options getOptions(); VersionInformation getVersionInformation(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java index 28db59abbe..4b70bd60ad 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java @@ -24,6 +24,8 @@ import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy; import org.mapstruct.ap.spi.DefaultBuilderProvider; +import org.mapstruct.ap.spi.DefaultEnumNamingStrategy; +import org.mapstruct.ap.spi.EnumNamingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.FreeBuilderAccessorNamingStrategy; import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; @@ -41,6 +43,7 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen private BuilderProvider builderProvider; private AccessorNamingStrategy accessorNamingStrategy; + private EnumNamingStrategy enumNamingStrategy; private boolean initialized; private Map enumTransformationStrategies; @@ -110,6 +113,15 @@ else if ( elementUtils.getTypeElement( FreeBuilderConstants.FREE_BUILDER_FQN ) ! } this.accessorNaming = new AccessorNamingUtils( this.accessorNamingStrategy ); + this.enumNamingStrategy = Services.get( EnumNamingStrategy.class, new DefaultEnumNamingStrategy() ); + this.enumNamingStrategy.init( this ); + if ( verbose ) { + messager.printMessage( + Diagnostic.Kind.NOTE, + "MapStruct: Using enum naming strategy: " + + this.enumNamingStrategy.getClass().getCanonicalName() + ); + } this.enumTransformationStrategies = new LinkedHashMap<>(); ServiceLoader transformationStrategiesLoader = ServiceLoader.load( @@ -238,6 +250,11 @@ public AccessorNamingStrategy getAccessorNamingStrategy() { return accessorNamingStrategy; } + public EnumNamingStrategy getEnumNamingStrategy() { + initialize(); + return enumNamingStrategy; + } + public BuilderProvider getBuilderProvider() { initialize(); return builderProvider; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index a26166a7ed..4c46c09917 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -158,6 +158,7 @@ public enum Message { VALUEMAPPING_UNMAPPED_SOURCES( "The following constants from the %s enum have no corresponding constant in the %s enum and must be be mapped via adding additional mappings: %s." ), VALUEMAPPING_ANY_REMAINING_FOR_NON_ENUM( "Source = \"\" can only be used on targets of type enum and not for %s." ), VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING( "Source = \"\" or \"\" is advisable for mapping of type String to an enum type.", Diagnostic.Kind.WARNING ), + VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI( "Constant %s doesn't exist in enum type %s. Constant was returned from EnumNamingStrategy: %s"), VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." ); // CHECKSTYLE:ON diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java new file mode 100644 index 0000000000..526d0a24f2 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java @@ -0,0 +1,35 @@ +/* + * 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.spi; + +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; + +/** + * @author Filip Hrisafov + */ +public class DefaultEnumNamingStrategy implements EnumNamingStrategy { + + protected Elements elementUtils; + protected Types typeUtils; + + @Override + public void init(MapStructProcessingEnvironment processingEnvironment) { + this.elementUtils = processingEnvironment.getElementUtils(); + this.typeUtils = processingEnvironment.getTypeUtils(); + } + + @Override + public String getDefaultNullEnumConstant(TypeElement enumType) { + return null; + } + + @Override + public String getEnumConstant(TypeElement enumType, String enumConstant) { + return enumConstant; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java new file mode 100644 index 0000000000..c135cc9ab2 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java @@ -0,0 +1,48 @@ +/* + * 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.spi; + +import javax.lang.model.element.TypeElement; + +import org.mapstruct.util.Experimental; + +/** + * A service provider interface for the mapping between different enum constants + * + * @author Arne Seime + */ +@Experimental("This SPI can have it's signature changed in subsequent releases") +public interface EnumNamingStrategy { + + /** + * Initializes the enum value mapping strategy + * + * @param processingEnvironment environment for facilities + */ + default void init(MapStructProcessingEnvironment processingEnvironment) { + + } + + /** + * Return the default enum constant to use if the source is null. + * + * @param enumType the enum + * @return enum value or null if there is no designated enum constant + */ + String getDefaultNullEnumConstant(TypeElement enumType); + + /** + * Map the enum constant to the value use for matching. + * In case you want this enum constant to match to null return {@link org.mapstruct.MappingConstants#NULL} + * + * @param enumType the enum this constant belongs to + * @param enumConstant constant to transform + * + * @return the transformed constant - or the original value from the parameter if no transformation is needed. + * never return null + */ + String getEnumConstant(TypeElement enumType, String enumConstant); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CheeseType.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CheeseType.java new file mode 100644 index 0000000000..71bde4415a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CheeseType.java @@ -0,0 +1,15 @@ +/* + * 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.value.spi; + +/** + * @author Filip Hrisafov + */ +public enum CheeseType { + + BRIE, + ROQUEFORT +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseMapper.java new file mode 100644 index 0000000000..6524f67c36 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseMapper.java @@ -0,0 +1,34 @@ +/* + * 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.value.spi; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CustomCheeseMapper { + + CustomCheeseMapper INSTANCE = Mappers.getMapper( CustomCheeseMapper.class ); + + CheeseType map(CustomCheeseType cheese); + + CustomCheeseType map(CheeseType cheese); + + String mapToString(CustomCheeseType cheeseType); + + String mapToString(CheeseType cheeseType); + + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "CUSTOM_BRIE") + CustomCheeseType mapStringToCustom(String cheese); + + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "BRIE") + CheeseType mapStringToCheese(String cheese); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseType.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseType.java new file mode 100644 index 0000000000..384df9b3d1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseType.java @@ -0,0 +1,17 @@ +/* + * 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.value.spi; + +/** + * @author Filip Hrisafov + */ +public enum CustomCheeseType implements CustomEnumMarker { + + UNSPECIFIED, + CUSTOM_BRIE, + CUSTOM_ROQUEFORT, + UNRECOGNIZED, +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMarker.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMarker.java new file mode 100644 index 0000000000..b12715e25f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMarker.java @@ -0,0 +1,12 @@ +/* + * 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.value.spi; + +/** + * @author Filip Hrisafov + */ +public interface CustomEnumMarker { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategy.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategy.java new file mode 100644 index 0000000000..f84ebaf7b3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategy.java @@ -0,0 +1,54 @@ +/* + * 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.value.spi; + +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.internal.gem.MappingConstantsGem; +import org.mapstruct.ap.spi.DefaultEnumNamingStrategy; +import org.mapstruct.ap.spi.EnumNamingStrategy; + +/** + * @author Filip Hrisafov + */ +public class CustomEnumNamingStrategy extends DefaultEnumNamingStrategy implements EnumNamingStrategy { + + @Override + public String getDefaultNullEnumConstant(TypeElement enumType) { + if ( isCustomEnum( enumType ) ) { + return "UNSPECIFIED"; + } + + return super.getDefaultNullEnumConstant( enumType ); + } + + @Override + public String getEnumConstant(TypeElement enumType, String enumConstant) { + if ( isCustomEnum( enumType ) ) { + return getCustomEnumConstant( enumConstant ); + } + return super.getEnumConstant( enumType, enumConstant ); + } + + protected String getCustomEnumConstant(String enumConstant) { + if ( "UNRECOGNIZED".equals( enumConstant ) || "UNSPECIFIED".equals( enumConstant ) ) { + return MappingConstantsGem.NULL; + } + + return enumConstant.replace( "CUSTOM_", "" ); + } + + protected boolean isCustomEnum(TypeElement enumType) { + for ( TypeMirror enumTypeInterface : enumType.getInterfaces() ) { + if ( typeUtils.asElement( enumTypeInterface ).getSimpleName().contentEquals( "CustomEnumMarker" ) ) { + return true; + } + } + + return false; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategyTest.java new file mode 100644 index 0000000000..47a0a5d64e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategyTest.java @@ -0,0 +1,123 @@ +/* + * 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.value.spi; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithServiceImplementation; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + CheeseType.class, + CustomCheeseType.class, + CustomEnumMarker.class, +}) +@WithServiceImplementation(CustomEnumNamingStrategy.class) +public class CustomEnumNamingStrategyTest { + + @Rule + public final GeneratedSource generatedSource = new GeneratedSource(); + + @Test + @WithClasses({ + CustomCheeseMapper.class + }) + public void shouldApplyCustomEnumNamingStrategy() { + generatedSource.addComparisonToFixtureFor( CustomCheeseMapper.class ); + CustomCheeseMapper mapper = CustomCheeseMapper.INSTANCE; + + // CheeseType -> CustomCheeseType + assertThat( mapper.map( (CheeseType) null ) ).isEqualTo( CustomCheeseType.UNSPECIFIED ); + assertThat( mapper.map( CheeseType.BRIE ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); + assertThat( mapper.map( CheeseType.ROQUEFORT ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + + // CustomCheeseType -> CheeseType + assertThat( mapper.map( (CustomCheeseType) null ) ).isNull(); + assertThat( mapper.map( CustomCheeseType.UNSPECIFIED ) ).isNull(); + assertThat( mapper.map( CustomCheeseType.CUSTOM_BRIE ) ).isEqualTo( CheeseType.BRIE ); + assertThat( mapper.map( CustomCheeseType.CUSTOM_ROQUEFORT ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.map( CustomCheeseType.UNRECOGNIZED ) ).isNull(); + + // CheeseType -> String + assertThat( mapper.mapToString( (CheeseType) null ) ).isNull(); + assertThat( mapper.mapToString( CheeseType.BRIE ) ).isEqualTo( "BRIE" ); + assertThat( mapper.mapToString( CheeseType.ROQUEFORT ) ).isEqualTo( "ROQUEFORT" ); + + // CustomCheeseType -> String + assertThat( mapper.mapToString( (CustomCheeseType) null ) ).isNull(); + assertThat( mapper.mapToString( CustomCheeseType.UNSPECIFIED ) ).isNull(); + assertThat( mapper.mapToString( CustomCheeseType.CUSTOM_BRIE ) ).isEqualTo( "BRIE" ); + assertThat( mapper.mapToString( CustomCheeseType.CUSTOM_ROQUEFORT ) ).isEqualTo( "ROQUEFORT" ); + assertThat( mapper.mapToString( CustomCheeseType.UNRECOGNIZED ) ).isNull(); + + // String - > CheeseType + assertThat( mapper.mapStringToCheese( null ) ).isNull(); + assertThat( mapper.mapStringToCheese( "BRIE" ) ).isEqualTo( CheeseType.BRIE ); + assertThat( mapper.mapStringToCheese( "ROQUEFORT" ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.mapStringToCheese( "UNKNOWN" ) ).isEqualTo( CheeseType.BRIE ); + + // CustomCheeseType -> String + assertThat( mapper.mapStringToCustom( null ) ).isEqualTo( CustomCheeseType.UNSPECIFIED ); + assertThat( mapper.mapStringToCustom( "UNRECOGNIZED" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); + assertThat( mapper.mapStringToCustom( "BRIE" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); + assertThat( mapper.mapStringToCustom( "ROQUEFORT" ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.mapStringToCustom( "UNKNOWN" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); + } + + @Test + @WithClasses({ + OverridesCustomCheeseMapper.class + }) + public void shouldApplyDefinedMappingsInsteadOfCustomEnumNamingStrategy() { + OverridesCustomCheeseMapper mapper = OverridesCustomCheeseMapper.INSTANCE; + + // CheeseType -> CustomCheeseType + assertThat( mapper.map( (CheeseType) null ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.map( CheeseType.BRIE ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.map( CheeseType.ROQUEFORT ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + + // CustomCheeseType -> CheeseType + assertThat( mapper.map( (CustomCheeseType) null ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.map( CustomCheeseType.UNSPECIFIED ) ).isNull(); + assertThat( mapper.map( CustomCheeseType.CUSTOM_BRIE ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.map( CustomCheeseType.CUSTOM_ROQUEFORT ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.map( CustomCheeseType.UNRECOGNIZED ) ).isNull(); + + // CheeseType -> String + assertThat( mapper.mapToString( (CheeseType) null ) ).isNull(); + assertThat( mapper.mapToString( CheeseType.BRIE ) ).isEqualTo( "BRIE" ); + assertThat( mapper.mapToString( CheeseType.ROQUEFORT ) ).isEqualTo( "BRIE" ); + + // CustomCheeseType -> String + assertThat( mapper.mapToString( (CustomCheeseType) null ) ).isEqualTo( "ROQUEFORT" ); + assertThat( mapper.mapToString( CustomCheeseType.UNSPECIFIED ) ).isNull(); + assertThat( mapper.mapToString( CustomCheeseType.CUSTOM_BRIE ) ).isEqualTo( "BRIE" ); + assertThat( mapper.mapToString( CustomCheeseType.CUSTOM_ROQUEFORT ) ).isEqualTo( "BRIE" ); + assertThat( mapper.mapToString( CustomCheeseType.UNRECOGNIZED ) ).isNull(); + + // String - > CheeseType + assertThat( mapper.mapStringToCheese( null ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.mapStringToCheese( "BRIE" ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.mapStringToCheese( "ROQUEFORT" ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.mapStringToCheese( "UNKNOWN" ) ).isEqualTo( CheeseType.BRIE ); + + // CustomCheeseType -> String + assertThat( mapper.mapStringToCustom( null ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.mapStringToCustom( "UNRECOGNIZED" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); + assertThat( mapper.mapStringToCustom( "BRIE" ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.mapStringToCustom( "ROQUEFORT" ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.mapStringToCustom( "UNKNOWN" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategy.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategy.java new file mode 100644 index 0000000000..671ad45c04 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategy.java @@ -0,0 +1,54 @@ +/* + * 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.value.spi; + +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.internal.gem.MappingConstantsGem; +import org.mapstruct.ap.spi.DefaultEnumNamingStrategy; +import org.mapstruct.ap.spi.EnumNamingStrategy; + +/** + * @author Filip Hrisafov + */ +public class CustomErroneousEnumNamingStrategy extends DefaultEnumNamingStrategy implements EnumNamingStrategy { + + @Override + public String getDefaultNullEnumConstant(TypeElement enumType) { + if ( isCustomEnum( enumType ) ) { + return "INCORRECT"; + } + + return super.getDefaultNullEnumConstant( enumType ); + } + + @Override + public String getEnumConstant(TypeElement enumType, String enumConstant) { + if ( isCustomEnum( enumType ) ) { + return getCustomEnumConstant( enumConstant ); + } + return super.getEnumConstant( enumType, enumConstant ); + } + + protected String getCustomEnumConstant(String enumConstant) { + if ( "UNRECOGNIZED".equals( enumConstant ) || "UNSPECIFIED".equals( enumConstant ) ) { + return MappingConstantsGem.NULL; + } + + return enumConstant.replace( "CUSTOM_", "" ); + } + + protected boolean isCustomEnum(TypeElement enumType) { + for ( TypeMirror enumTypeInterface : enumType.getInterfaces() ) { + if ( typeUtils.asElement( enumTypeInterface ).getSimpleName().contentEquals( "CustomEnumMarker" ) ) { + return true; + } + } + + return false; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategyTest.java new file mode 100644 index 0000000000..82d6be4c26 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategyTest.java @@ -0,0 +1,102 @@ +/* + * 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.value.spi; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithServiceImplementation; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + CheeseType.class, + CustomCheeseType.class, + CustomEnumMarker.class, +}) +@WithServiceImplementation(CustomErroneousEnumNamingStrategy.class) +public class CustomErroneousEnumNamingStrategyTest { + + @Test + @WithClasses({ + CustomCheeseMapper.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = CustomCheeseMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 23, + messageRegExp = "Constant INCORRECT doesn't exist in enum type " + + "org\\.mapstruct\\.ap\\.test\\.value\\.spi\\.CustomCheeseType." + + " Constant was returned from EnumNamingStrategy: .*CustomErroneousEnumNamingStrategy@.*" + ), + @Diagnostic( + type = CustomCheeseMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 30, + messageRegExp = "Constant INCORRECT doesn't exist in enum type " + + "org\\.mapstruct\\.ap\\.test\\.value\\.spi\\.CustomCheeseType." + + " Constant was returned from EnumNamingStrategy: .*CustomErroneousEnumNamingStrategy@.*" + ) + } + ) + public void shouldThrowCompileErrorWhenDefaultEnumDoesNotExist() { + } + + @Test + @WithClasses({ + OverridesCustomCheeseMapper.class + }) + public void shouldApplyDefinedMappingsInsteadOfCustomEnumNamingStrategy() { + OverridesCustomCheeseMapper mapper = OverridesCustomCheeseMapper.INSTANCE; + + // CheeseType -> CustomCheeseType + assertThat( mapper.map( (CheeseType) null ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.map( CheeseType.BRIE ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.map( CheeseType.ROQUEFORT ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + + // CustomCheeseType -> CheeseType + assertThat( mapper.map( (CustomCheeseType) null ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.map( CustomCheeseType.UNSPECIFIED ) ).isNull(); + assertThat( mapper.map( CustomCheeseType.CUSTOM_BRIE ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.map( CustomCheeseType.CUSTOM_ROQUEFORT ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.map( CustomCheeseType.UNRECOGNIZED ) ).isNull(); + + // CheeseType -> String + assertThat( mapper.mapToString( (CheeseType) null ) ).isNull(); + assertThat( mapper.mapToString( CheeseType.BRIE ) ).isEqualTo( "BRIE" ); + assertThat( mapper.mapToString( CheeseType.ROQUEFORT ) ).isEqualTo( "BRIE" ); + + // CustomCheeseType -> String + assertThat( mapper.mapToString( (CustomCheeseType) null ) ).isEqualTo( "ROQUEFORT" ); + assertThat( mapper.mapToString( CustomCheeseType.UNSPECIFIED ) ).isNull(); + assertThat( mapper.mapToString( CustomCheeseType.CUSTOM_BRIE ) ).isEqualTo( "BRIE" ); + assertThat( mapper.mapToString( CustomCheeseType.CUSTOM_ROQUEFORT ) ).isEqualTo( "BRIE" ); + assertThat( mapper.mapToString( CustomCheeseType.UNRECOGNIZED ) ).isNull(); + + // String - > CheeseType + assertThat( mapper.mapStringToCheese( null ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.mapStringToCheese( "BRIE" ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.mapStringToCheese( "ROQUEFORT" ) ).isEqualTo( CheeseType.ROQUEFORT ); + assertThat( mapper.mapStringToCheese( "UNKNOWN" ) ).isEqualTo( CheeseType.BRIE ); + + // CustomCheeseType -> String + assertThat( mapper.mapStringToCustom( null ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.mapStringToCustom( "UNRECOGNIZED" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); + assertThat( mapper.mapStringToCustom( "BRIE" ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.mapStringToCustom( "ROQUEFORT" ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); + assertThat( mapper.mapStringToCustom( "UNKNOWN" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/OverridesCustomCheeseMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/OverridesCustomCheeseMapper.java new file mode 100644 index 0000000000..ae40d2d98d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/OverridesCustomCheeseMapper.java @@ -0,0 +1,45 @@ +/* + * 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.value.spi; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface OverridesCustomCheeseMapper { + + OverridesCustomCheeseMapper INSTANCE = Mappers.getMapper( OverridesCustomCheeseMapper.class ); + + @ValueMapping(source = "CUSTOM_BRIE", target = "ROQUEFORT") + @ValueMapping(source = MappingConstants.NULL, target = "ROQUEFORT") + CheeseType map(CustomCheeseType cheese); + + @ValueMapping(source = "BRIE", target = "CUSTOM_ROQUEFORT") + @ValueMapping(source = MappingConstants.NULL, target = "CUSTOM_ROQUEFORT") + CustomCheeseType map(CheeseType cheese); + + @ValueMapping(source = "CUSTOM_ROQUEFORT", target = "BRIE") + @ValueMapping(source = MappingConstants.NULL, target = "ROQUEFORT") + String mapToString(CustomCheeseType cheeseType); + + @ValueMapping(source = "ROQUEFORT", target = "BRIE") + String mapToString(CheeseType cheeseType); + + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "CUSTOM_BRIE") + @ValueMapping(source = "BRIE", target = "CUSTOM_ROQUEFORT") + @ValueMapping(source = MappingConstants.NULL, target = "CUSTOM_ROQUEFORT") + CustomCheeseType mapStringToCustom(String cheese); + + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "BRIE") + @ValueMapping(source = "BRIE", target = "ROQUEFORT") + @ValueMapping(source = MappingConstants.NULL, target = "ROQUEFORT") + CheeseType mapStringToCheese(String cheese); +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java new file mode 100644 index 0000000000..9798c41597 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java @@ -0,0 +1,138 @@ +/* + * 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.value.spi; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2020-05-16T12:53:12+0200", + comments = "version: , compiler: javac, environment: Java 14.0.1 (Oracle Corporation)" +) +public class CustomCheeseMapperImpl implements CustomCheeseMapper { + + @Override + public CheeseType map(CustomCheeseType cheese) { + if ( cheese == null ) { + return null; + } + + CheeseType cheeseType; + + switch ( cheese ) { + case UNSPECIFIED: cheeseType = null; + break; + case CUSTOM_BRIE: cheeseType = CheeseType.BRIE; + break; + case CUSTOM_ROQUEFORT: cheeseType = CheeseType.ROQUEFORT; + break; + case UNRECOGNIZED: cheeseType = null; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); + } + + return cheeseType; + } + + @Override + public CustomCheeseType map(CheeseType cheese) { + if ( cheese == null ) { + return CustomCheeseType.UNSPECIFIED; + } + + CustomCheeseType customCheeseType; + + switch ( cheese ) { + case BRIE: customCheeseType = CustomCheeseType.CUSTOM_BRIE; + break; + case ROQUEFORT: customCheeseType = CustomCheeseType.CUSTOM_ROQUEFORT; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); + } + + return customCheeseType; + } + + @Override + public String mapToString(CustomCheeseType cheeseType) { + if ( cheeseType == null ) { + return null; + } + + String string; + + switch ( cheeseType ) { + case UNSPECIFIED: string = null; + break; + case CUSTOM_BRIE: string = "BRIE"; + break; + case CUSTOM_ROQUEFORT: string = "ROQUEFORT"; + break; + case UNRECOGNIZED: string = null; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheeseType ); + } + + return string; + } + + @Override + public String mapToString(CheeseType cheeseType) { + if ( cheeseType == null ) { + return null; + } + + String string; + + switch ( cheeseType ) { + case BRIE: string = "BRIE"; + break; + case ROQUEFORT: string = "ROQUEFORT"; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheeseType ); + } + + return string; + } + + @Override + public CustomCheeseType mapStringToCustom(String cheese) { + if ( cheese == null ) { + return CustomCheeseType.UNSPECIFIED; + } + + CustomCheeseType customCheeseType; + + switch ( cheese ) { + case "BRIE": customCheeseType = CustomCheeseType.CUSTOM_BRIE; + break; + case "ROQUEFORT": customCheeseType = CustomCheeseType.CUSTOM_ROQUEFORT; + break; + default: customCheeseType = CustomCheeseType.CUSTOM_BRIE; + } + + return customCheeseType; + } + + @Override + public CheeseType mapStringToCheese(String cheese) { + if ( cheese == null ) { + return null; + } + + CheeseType cheeseType; + + switch ( cheese ) { + case "BRIE": cheeseType = CheeseType.BRIE; + break; + case "ROQUEFORT": cheeseType = CheeseType.ROQUEFORT; + break; + default: cheeseType = CheeseType.BRIE; + } + + return cheeseType; + } +} From 850a55cd5dd02687f0c800738a604d5d724084af Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 30 May 2020 12:01:14 +0200 Subject: [PATCH 056/597] #695 Check for direct assignment for iterable, stream or map types should be done on their type parameters instead of the types themselves --- .../creation/MappingResolverImpl.java | 40 ++++++++++++-- .../mappingcontrol/CloningListMapper.java | 19 +++++++ .../ap/test/mappingcontrol/CustomerDto.java | 52 ++++++++++++++++++ .../mappingcontrol/MappingControlTest.java | 53 +++++++++++++++++++ .../ap/test/mappingcontrol/OrderItemDto.java | 31 +++++++++++ .../test/mappingcontrol/OrderItemKeyDto.java | 22 ++++++++ 6 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningListMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CustomerDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/OrderItemDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/OrderItemKeyDto.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 47e3e57f62..2875cef67a 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -190,8 +190,9 @@ private Assignment getTargetAssignment(Type sourceType, Type targetType) { // then direct assignable if ( !hasQualfiers() ) { - if ( ( sourceType.isAssignableTo( targetType ) && allowDirect( sourceType, targetType ) ) || - isAssignableThroughCollectionCopyConstructor( sourceType, targetType ) ) { + if ( ( sourceType.isAssignableTo( targetType ) || + isAssignableThroughCollectionCopyConstructor( sourceType, targetType ) ) + && allowDirect( sourceType, targetType ) ) { Assignment simpleAssignment = sourceRHS; return simpleAssignment; } @@ -280,11 +281,40 @@ private boolean hasQualfiers() { } private boolean allowDirect( Type sourceType, Type targetType ) { - if ( sourceType.isPrimitive() || targetType.isPrimitive() - || sourceType.isJavaLangType() || targetType.isJavaLangType() ) { + if ( selectionCriteria != null && selectionCriteria.isAllowDirect() ) { return true; } - return selectionCriteria != null && selectionCriteria.isAllowDirect(); + + return allowDirect( sourceType ) || allowDirect( targetType ); + } + + private boolean allowDirect(Type type) { + if ( type.isPrimitive() ) { + return true; + } + + if ( type.isArrayType() ) { + return type.isJavaLangType(); + } + + if ( type.isIterableOrStreamType() ) { + List typeParameters = type.getTypeParameters(); + // For iterable or stream direct mapping is enabled when: + // - The type is raw (no type parameters) + // - The type parameter is allowed + return typeParameters.isEmpty() || allowDirect( Collections.first( typeParameters ) ); + } + + if ( type.isMapType() ) { + List typeParameters = type.getTypeParameters(); + // For map type direct mapping is enabled when: + // - The type os raw (no type parameters + // - The key and value are direct assignable + return typeParameters.isEmpty() || + ( allowDirect( typeParameters.get( 0 ) ) && allowDirect( typeParameters.get( 1 ) ) ); + } + + return type.isJavaLangType(); } private boolean allowConversion() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningListMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningListMapper.java new file mode 100644 index 0000000000..5a74527d36 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningListMapper.java @@ -0,0 +1,19 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.Mapper; +import org.mapstruct.control.DeepClone; +import org.mapstruct.factory.Mappers; + +@Mapper(mappingControl = DeepClone.class) +public interface CloningListMapper { + + CloningListMapper INSTANCE = Mappers.getMapper( CloningListMapper.class ); + + CustomerDto clone(CustomerDto in); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CustomerDto.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CustomerDto.java new file mode 100644 index 0000000000..810d53f41e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CustomerDto.java @@ -0,0 +1,52 @@ +/* + * 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.mappingcontrol; + +import java.util.List; +import java.util.Map; + +/** + * @author Sjaak Derksen + */ +public class CustomerDto { + + private Long id; + private String customerName; + private List orders; + private Map stock; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public List getOrders() { + return orders; + } + + public void setOrders(List orders) { + this.orders = orders; + } + + public Map getStock() { + return stock; + } + + public void setStock(Map stock) { + this.stock = stock; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java index a1a1fd6079..5807931ce1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java @@ -5,6 +5,11 @@ */ package org.mapstruct.ap.test.mappingcontrol; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import org.junit.Test; import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; @@ -15,6 +20,7 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; /** * @author Sjaak Derksen @@ -25,6 +31,9 @@ ShelveDTO.class, Fridge.class, FridgeDTO.class, + CustomerDto.class, + OrderItemDto.class, + OrderItemKeyDto.class, UseDirect.class, UseComplex.class }) @@ -63,6 +72,49 @@ public void testDeepCloning() { assertThat( out.getShelve().getCoolBeer().getBeerCount() ).isEqualTo( "5" ); } + /** + * Test the deep cloning annotation with lists + */ + @Test + @WithClasses(CloningListMapper.class) + public void testDeepCloningListsAndMaps() { + + CustomerDto in = new CustomerDto(); + in.setId( 10L ); + in.setCustomerName( "Jaques" ); + OrderItemDto order1 = new OrderItemDto(); + order1.setName( "Table" ); + order1.setQuantity( 2L ); + in.setOrders( new ArrayList<>( Collections.singleton( order1 ) ) ); + OrderItemKeyDto key = new OrderItemKeyDto(); + key.setStockNumber( 5 ); + Map stock = new HashMap<>(); + stock.put( key, order1 ); + in.setStock( stock ); + + CustomerDto out = CloningListMapper.INSTANCE.clone( in ); + + assertThat( out.getId() ).isEqualTo( 10 ); + assertThat( out.getCustomerName() ).isEqualTo( "Jaques" ); + assertThat( out.getOrders() ) + .extracting( "name", "quantity" ) + .containsExactly( tuple( "Table", 2L ) ); + assertThat( out.getStock() ).isNotNull(); + assertThat( out.getStock() ).hasSize( 1 ); + + Map.Entry entry = out.getStock().entrySet().iterator().next(); + assertThat( entry.getKey().getStockNumber() ).isEqualTo( 5 ); + assertThat( entry.getValue().getName() ).isEqualTo( "Table" ); + assertThat( entry.getValue().getQuantity() ).isEqualTo( 2L ); + + // check mapper really created new objects + assertThat( out ).isNotSameAs( in ); + assertThat( out.getOrders().get( 0 ) ).isNotSameAs( order1 ); + assertThat( entry.getKey() ).isNotSameAs( key ); + assertThat( entry.getValue() ).isNotSameAs( order1 ); + assertThat( entry.getValue() ).isNotSameAs( out.getOrders().get( 0 ) ); + } + /** * This is a nice test. MapStruct looks for a way to map ShelveDto to ShelveDto. *

    @@ -95,6 +147,7 @@ public void methodSelectionAllowed() { assertThat( fridge ).isNotNull(); assertThat( fridge.getBeerCount() ).isEqualTo( 5 ); + assertThat( fridge.getBeerCount() ).isEqualTo( 5 ); } @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/OrderItemDto.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/OrderItemDto.java new file mode 100644 index 0000000000..dbaad654d9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/OrderItemDto.java @@ -0,0 +1,31 @@ +/* + * 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.mappingcontrol; + +/** + * @author Sjaak Derksen + */ +public class OrderItemDto { + + private String name; + private Long quantity; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getQuantity() { + return quantity; + } + + public void setQuantity(Long quantity) { + this.quantity = quantity; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/OrderItemKeyDto.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/OrderItemKeyDto.java new file mode 100644 index 0000000000..6154f4450b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/OrderItemKeyDto.java @@ -0,0 +1,22 @@ +/* + * 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.mappingcontrol; + +/** + * @author Sjaak Derksen + */ +public class OrderItemKeyDto { + + private long stockNumber; + + public long getStockNumber() { + return stockNumber; + } + + public void setStockNumber(long stockNumber) { + this.stockNumber = stockNumber; + } +} From fc5e1ffe6b4eae155d6e6ad3f806f04f0dfc249a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 30 May 2020 15:34:54 +0200 Subject: [PATCH 057/597] Upgrade to latest 1.6.2 asciidoc and fix incorrect closing documentation tag --- documentation/pom.xml | 8 ++++---- .../ap/test/nestedbeans/exclusions/custom/Source.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/documentation/pom.xml b/documentation/pom.xml index 90c3e0b64e..ceb3cb1db6 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -21,9 +21,9 @@ MapStruct Documentation - 1.5.0-alpha.11 - 1.5.4 - 1.7.21 + 1.5.3 + 1.6.2 + 9.2.6.0 @@ -33,7 +33,7 @@ org.asciidoctor asciidoctor-maven-plugin - 1.5.3 + 1.6.0 org.asciidoctor diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/Source.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/Source.java index 6cdc3e2752..cf5581c726 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/Source.java @@ -39,4 +39,4 @@ public void setNested(NestedSource nested) { } // tag::documentation[] } -// tag::documentation[] +// end::documentation[] From 72ce5f3bd201d542278f586bc725c6bcc493ae61 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 1 Jun 2020 13:42:32 +0200 Subject: [PATCH 058/597] [maven-release-plugin] prepare release 1.4.0.Beta1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 6c908377c8..0ee850799b 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 63311b5950..54bd288072 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index c74e0b8bb1..241ea0ce4e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index b404ed3cee..4abb86aa0d 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index ceb3cb1db6..07b8c23969 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index a643bab4b8..c1444a63c1 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index b26cf8683d..e151af0d8a 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta1 pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.Beta1 diff --git a/pom.xml b/pom.xml index f9c649c168..f511147688 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta1 parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.Beta1 diff --git a/processor/pom.xml b/processor/pom.xml index c047301268..afe315d258 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta1 ../parent/pom.xml From d87d75a7a88f1f7fa45312ad54a66772ad4cdec1 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 1 Jun 2020 13:42:32 +0200 Subject: [PATCH 059/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0ee850799b..6c908377c8 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 54bd288072..63311b5950 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 241ea0ce4e..c74e0b8bb1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 4abb86aa0d..b404ed3cee 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 07b8c23969..ceb3cb1db6 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index c1444a63c1..a643bab4b8 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index e151af0d8a..b26cf8683d 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta1 + 1.4.0-SNAPSHOT pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.Beta1 + HEAD diff --git a/pom.xml b/pom.xml index f511147688..f9c649c168 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta1 + 1.4.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.Beta1 + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index afe315d258..c047301268 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta1 + 1.4.0-SNAPSHOT ../parent/pom.xml From da37d401520ee32889f706cf06308a9ac95338e2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 7 Jun 2020 16:45:32 +0200 Subject: [PATCH 060/597] #2109 Generate correct array assignment for constructor parameter mapping --- .../ap/internal/model/BeanMappingMethod.ftl | 16 +----- ...pperForCollectionsAndMapsWithNullCheck.ftl | 4 -- .../ap/test/bugs/_2109/Issue2109Mapper.java | 24 ++++++++ .../ap/test/bugs/_2109/Issue2109Test.java | 55 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2109/Source.java | 28 ++++++++++ .../mapstruct/ap/test/bugs/_2109/Target.java | 28 ++++++++++ .../nestedsource/ArtistToChartEntryImpl.java | 34 ++++-------- .../nestedtarget/ChartEntryToArtistImpl.java | 32 +++++------ 8 files changed, 165 insertions(+), 56 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Target.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 45825b4fd6..c58f9e7581 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -30,31 +30,26 @@ <#list sourceParametersExcludingPrimitives as sourceParam> <#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)> <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> - <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName}; + <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; if ( ${sourceParam.name} != null ) { <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> } - else { - <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> - ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; - - } <#list sourcePrimitiveParameters as sourceParam> <#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)> <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> - <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName}; + <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; <@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> <#else> <#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping> - <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName}; + <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; <#if mapNullToDefault>if ( ${sourceParameters[0].name} != null ) { <#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping> @@ -62,11 +57,6 @@ <#if mapNullToDefault> } - else { - <#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping> - ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; - - } <#list constructorConstantMappings as constantMapping> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl index 92e2e9d09d..3d6172b49d 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl @@ -18,10 +18,6 @@ <@lib.handleLocalVarNullCheck needs_explicit_local_var=directAssignment> <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if directAssignment><@wrapLocalVarInCollectionInitializer/><#else><@lib.handleWithAssignmentOrNullCheckVar/>; - <#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && !ext.targetBeanName?has_content>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleLocalVarNullCheck --> - ${ext.targetWriteAccessorName}<@lib.handleWrite>null; - } - <#-- wraps the local variable in a collection initializer (new collection, or EnumSet.copyOf) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Mapper.java new file mode 100644 index 0000000000..7542c2c0cf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Mapper.java @@ -0,0 +1,24 @@ +/* + * 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._2109; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2109Mapper { + + Issue2109Mapper INSTANCE = Mappers.getMapper( Issue2109Mapper.class ); + + Target map(Source source); + + @Mapping(target = "data", defaultExpression = "java(new byte[0])") + Target mapWithEmptyData(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Test.java new file mode 100644 index 0000000000..6ee44acb97 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Test.java @@ -0,0 +1,55 @@ +/* + * 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._2109; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2109") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2109Mapper.class, + Source.class, + Target.class, +}) +public class Issue2109Test { + + @Test + public void shouldCorrectlyMapArrayInConstructorMapping() { + Target target = Issue2109Mapper.INSTANCE.map( new Source( 100L, new byte[] { 100, 120, 40, 40 } ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getId() ).isEqualTo( 100L ); + assertThat( target.getData() ).containsExactly( 100, 120, 40, 40 ); + + target = Issue2109Mapper.INSTANCE.map( new Source( 50L, null ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getId() ).isEqualTo( 50L ); + assertThat( target.getData() ).isNull(); + + target = Issue2109Mapper.INSTANCE.mapWithEmptyData( new Source( 100L, new byte[] { 100, 120, 40, 40 } ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getId() ).isEqualTo( 100L ); + assertThat( target.getData() ).containsExactly( 100, 120, 40, 40 ); + + target = Issue2109Mapper.INSTANCE.mapWithEmptyData( new Source( 50L, null ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getId() ).isEqualTo( 50L ); + assertThat( target.getData() ).isEmpty(); + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Source.java new file mode 100644 index 0000000000..a0aba8ce8b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Source.java @@ -0,0 +1,28 @@ +/* + * 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._2109; + +/** + * @author Filip Hrisafov + */ +public class Source { + + private final Long id; + private final byte[] data; + + public Source(Long id, byte[] data) { + this.id = id; + this.data = data; + } + + public Long getId() { + return id; + } + + public byte[] getData() { + return data; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Target.java new file mode 100644 index 0000000000..2e44bc21b6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Target.java @@ -0,0 +1,28 @@ +/* + * 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._2109; + +/** + * @author Filip Hrisafov + */ +public class Target { + + private final Long id; + private final byte[] data; + + public Target(Long id, byte[] data) { + this.id = id; + this.data = data; + } + + public Long getId() { + return id; + } + + public byte[] getData() { + return data; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java index bd8c81a5db..09f96b4b3a 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java @@ -26,36 +26,24 @@ public ChartEntry map(Chart chart, Song song, Integer position) { return null; } - String chartName; + String chartName = null; if ( chart != null ) { chartName = chart.getName(); } - else { - chartName = null; - } - String songTitle; - String artistName; - String recordedAt; - String city; + String songTitle = null; + String artistName = null; + String recordedAt = null; + String city = null; if ( song != null ) { songTitle = song.getTitle(); artistName = songArtistName( song ); recordedAt = songArtistLabelStudioName( song ); city = songArtistLabelStudioCity( song ); } - else { - songTitle = null; - artistName = null; - recordedAt = null; - city = null; - } - int position1; + int position1 = 0; if ( position != null ) { position1 = position; } - else { - position1 = 0; - } ChartEntry chartEntry = new ChartEntry( chartName, songTitle, artistName, recordedAt, city, position1 ); @@ -68,10 +56,10 @@ public ChartEntry map(Song song) { return null; } - String songTitle; - String artistName; - String recordedAt; - String city; + String songTitle = null; + String artistName = null; + String recordedAt = null; + String city = null; songTitle = song.getTitle(); artistName = songArtistName( song ); @@ -92,7 +80,7 @@ public ChartEntry map(Chart name) { return null; } - String chartName; + String chartName = null; chartName = name.getName(); diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java index f5deb649be..0413ef5b79 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java @@ -27,8 +27,8 @@ public Chart map(ChartEntry chartEntry) { return null; } - Song song; - String name; + Song song = null; + String name = null; song = chartEntryToSong( chartEntry ); name = chartEntry.getChartName(); @@ -46,12 +46,12 @@ public ChartEntry map(Chart chart) { return null; } - String chartName; - String songTitle; - String artistName; - String recordedAt; - String city; - int position; + String chartName = null; + String songTitle = null; + String artistName = null; + String recordedAt = null; + String city = null; + int position = 0; chartName = chart.getName(); songTitle = chartSongTitle( chart ); @@ -70,8 +70,8 @@ protected Studio chartEntryToStudio(ChartEntry chartEntry) { return null; } - String name; - String city; + String name = null; + String city = null; name = chartEntry.getRecordedAt(); city = chartEntry.getCity(); @@ -86,7 +86,7 @@ protected Label chartEntryToLabel(ChartEntry chartEntry) { return null; } - Studio studio; + Studio studio = null; studio = chartEntryToStudio( chartEntry ); @@ -102,8 +102,8 @@ protected Artist chartEntryToArtist(ChartEntry chartEntry) { return null; } - Label label; - String name; + Label label = null; + String name = null; label = chartEntryToLabel( chartEntry ); name = chartEntry.getArtistName(); @@ -118,9 +118,9 @@ protected Song chartEntryToSong(ChartEntry chartEntry) { return null; } - Artist artist; - String title; - List positions; + Artist artist = null; + String title = null; + List positions = null; artist = chartEntryToArtist( chartEntry ); title = chartEntry.getSongTitle(); From 29b82e772ce3cfe5dd330359d2b2a034def46a76 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sun, 14 Jun 2020 21:04:31 +0200 Subject: [PATCH 061/597] #2111 extra tests (#2119) --- .../ap/test/bugs/_2111/Issue2111Mapper.java | 45 +++++++++++++++++++ .../ap/test/bugs/_2111/Issue2111Test.java | 26 +++++++++++ 2 files changed, 71 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Test.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Mapper.java new file mode 100644 index 0000000000..9bf63385fe --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Mapper.java @@ -0,0 +1,45 @@ +/* + * 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._2111; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +import static java.util.Collections.singletonList; + +@Mapper +public interface Issue2111Mapper { + + @Mapping(target = "strs", source = "ex", qualifiedByName = "wrap") + DTO map(UseExample from); + + @Named("wrap") + default String mapExample(Example ex) { + return ex.name; + } + + @Named("wrap") + default List wrapInList(T t) { + return singletonList( t ); + } + + //CHECKSTYLE:OFF + class Example { + public String name; + } + + class UseExample { + public Example ex; + } + + class DTO { + public List strs; + } + //CHECKSTYLE:ON +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Test.java new file mode 100644 index 0000000000..0d6b9ef771 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Test.java @@ -0,0 +1,26 @@ +/* + * 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._2111; + +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; + +/** + * @author Sjaak Derksen + */ +@IssueKey("2111") +@RunWith( AnnotationProcessorTestRunner.class ) +@WithClasses( Issue2111Mapper.class ) +public class Issue2111Test { + + @Test + public void shouldCompile() { + + } +} From ef3cbc1b36fa0ae0b9518a0dc525b93fe1484797 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Jun 2020 23:12:02 +0200 Subject: [PATCH 062/597] #2121 Add extra test case --- .../ap/test/bugs/_2121/Issue2121Mapper.java | 51 +++++++++++++++++++ .../ap/test/bugs/_2121/Issue2121Test.java | 36 +++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Test.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Mapper.java new file mode 100644 index 0000000000..f61300c0ee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Mapper.java @@ -0,0 +1,51 @@ +/* + * 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._2121; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2121Mapper { + + Issue2121Mapper INSTANCE = Mappers.getMapper( Issue2121Mapper.class ); + + Target map(Source source); + + class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Source { + + private final SourceEnum value; + + public Source(SourceEnum value) { + this.value = value; + } + + public SourceEnum getValue() { + return value; + } + } + + enum SourceEnum { + VALUE1, + VALUE2 + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Test.java new file mode 100644 index 0000000000..3d6f6dfca1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Test.java @@ -0,0 +1,36 @@ +/* + * 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._2121; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2121") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses(Issue2121Mapper.class) +public class Issue2121Test { + + @Test + public void shouldCompile() { + Issue2121Mapper mapper = Issue2121Mapper.INSTANCE; + + Issue2121Mapper.Target target = mapper.map( new Issue2121Mapper.Source( Issue2121Mapper.SourceEnum.VALUE1 ) ); + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "VALUE1" ); + + target = mapper.map( new Issue2121Mapper.Source( null ) ); + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isNull(); + } +} From bdc58b96025cc5d1e89278ee2b12c3a841c5d156 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Jun 2020 23:24:56 +0200 Subject: [PATCH 063/597] #2124 Add extra test case --- .../ap/test/bugs/_2124/CommitComment.java | 22 +++++++++++ .../ap/test/bugs/_2124/Issue2124Mapper.java | 28 ++++++++++++++ .../ap/test/bugs/_2124/Issue2124Test.java | 38 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/CommitComment.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Test.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/CommitComment.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/CommitComment.java new file mode 100644 index 0000000000..9067f82c3d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/CommitComment.java @@ -0,0 +1,22 @@ +/* + * 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._2124; + +/** + * @author Filip Hrisafov + */ +public class CommitComment { + + private final Integer issueId; + + public CommitComment(Integer issueId) { + this.issueId = issueId; + } + + public Integer getIssueId() { + return issueId; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Mapper.java new file mode 100644 index 0000000000..af6250c7f5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Mapper.java @@ -0,0 +1,28 @@ +/* + * 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._2124; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2124Mapper { + + Issue2124Mapper INSTANCE = Mappers.getMapper( Issue2124Mapper.class ); + + @Mapping(target = "issueId", source = "comment.issueId", qualifiedByName = "mapped") + CommitComment clone(CommitComment comment, String otherSource); + + @Named("mapped") + default Integer mapIssueNumber(int issueNumber) { + return issueNumber * 2; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Test.java new file mode 100644 index 0000000000..5671c78877 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Test.java @@ -0,0 +1,38 @@ +/* + * 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._2124; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2124") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + CommitComment.class, + Issue2124Mapper.class +}) +public class Issue2124Test { + + @Test + public void shouldCompile() { + + CommitComment clone = Issue2124Mapper.INSTANCE.clone( new CommitComment( 100 ), null ); + assertThat( clone ).isNotNull(); + assertThat( clone.getIssueId() ).isEqualTo( 200 ); + + clone = Issue2124Mapper.INSTANCE.clone( new CommitComment( null ), null ); + assertThat( clone ).isNotNull(); + assertThat( clone.getIssueId() ).isNull(); + } +} From 92b4316abd483528c7282dc34a08e04015d13ed8 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Jun 2020 23:47:02 +0200 Subject: [PATCH 064/597] #2117 Add extra test case --- .../ap/test/bugs/_2117/Issue2117Mapper.java | 30 ++++++++++++++++ .../ap/test/bugs/_2117/Issue2117Test.java | 36 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Test.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Mapper.java new file mode 100644 index 0000000000..06dd72fafd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Mapper.java @@ -0,0 +1,30 @@ +/* + * 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._2117; + +import java.nio.file.AccessMode; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2117Mapper { + + Issue2117Mapper INSTANCE = Mappers.getMapper( Issue2117Mapper.class ); + + @Mapping(target = "accessMode", source = "accessMode") + Target toTarget(AccessMode accessMode, String otherSource); + + class Target { + // CHECKSTYLE:OFF + public AccessMode accessMode; + // CHECKSTYLE ON + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Test.java new file mode 100644 index 0000000000..fd51fd9384 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Test.java @@ -0,0 +1,36 @@ +/* + * 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._2117; + +import java.nio.file.AccessMode; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2117") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2117Mapper.class +}) +public class Issue2117Test { + + @Test + public void shouldCompile() { + + Issue2117Mapper.Target target = Issue2117Mapper.INSTANCE.toTarget( AccessMode.READ, null ); + + assertThat( target ).isNotNull(); + assertThat( target.accessMode ).isEqualTo( AccessMode.READ ); + } +} From 13df6a21bc9931347460201d13d77f6f6df5a30d Mon Sep 17 00:00:00 2001 From: SahinSarkar Date: Sat, 20 Jun 2020 17:20:33 +0530 Subject: [PATCH 065/597] Fixed typo in documentation --- .../main/asciidoc/chapter-10-advanced-mapping-options.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 13205caa9b..28d6e9d1e0 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -195,7 +195,7 @@ null check, regardless of the value of the `NullValuePropertyMappingStrategy`, t [TIP] ==== -`NullValuePropertyMappingStrategy` also applies when the presense checker returns `not present`. +`NullValuePropertyMappingStrategy` also applies when the presence checker returns `not present`. ==== [[checking-source-property-for-null-arguments]] From 082704cc551c0b533c77e3a88681b086ec831a0f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 4 Jul 2020 17:27:32 +0200 Subject: [PATCH 066/597] #2131 Add extra test case --- .../ap/test/bugs/_2131/Issue2131Mapper.java | 45 +++++++++++++++++++ .../ap/test/bugs/_2131/Issue2131Test.java | 37 +++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Test.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Mapper.java new file mode 100644 index 0000000000..a0e0b1df7f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Mapper.java @@ -0,0 +1,45 @@ +/* + * 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._2131; + +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +public interface Issue2131Mapper { + + Issue2131Mapper INSTANCE = Mappers.getMapper( Issue2131Mapper.class ); + + TestDto map(TestModel source); + + class TestModel { + private final String name; + + public TestModel(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + class TestDto { + private String name; + + public TestDto(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Test.java new file mode 100644 index 0000000000..80cde5c5bf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Test.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.bugs._2131; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2131") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses(Issue2131Mapper.class) +public class Issue2131Test { + + @Test + public void shouldCompile() { + Issue2131Mapper mapper = Issue2131Mapper.INSTANCE; + + Issue2131Mapper.TestDto target = mapper.map( new Issue2131Mapper.TestModel( "test" ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getName() ).isEqualTo( "test" ); + + target = mapper.map( new Issue2131Mapper.TestModel( null ) ); + assertThat( target ).isNotNull(); + assertThat( target.getName() ).isNull(); + } +} From 81a88bdb6cc10d0f4c2977ed9f48d3d28aac7fc4 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 4 Jul 2020 18:20:09 +0200 Subject: [PATCH 067/597] #2133 @BeanMapping#resultType should not be applied to forged methods (#2134) * #2133 reproducer * #2133 solution --- .../ap/internal/model/BeanMappingMethod.java | 5 +- .../ap/test/bugs/_2133/Issue2133Mapper.java | 75 +++++++++++++++++++ .../ap/test/bugs/_2133/Issue2133Test.java | 23 ++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index ea4f3fbdee..1be610842a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -472,7 +472,10 @@ private void sortPropertyMappingsByDependencies() { } private Type getReturnTypeToConstructFromSelectionParameters(SelectionParameters selectionParams) { - if ( selectionParams != null && selectionParams.getResultType() != null ) { + // resultType only applies to method that actually has @BeanMapping annotation, never to forged methods + if ( !( method instanceof ForgedMethod ) + && selectionParams != null + && selectionParams.getResultType() != null ) { return ctx.getTypeFactory().getType( selectionParams.getResultType() ); } return null; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Mapper.java new file mode 100644 index 0000000000..484620c0c4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Mapper.java @@ -0,0 +1,75 @@ +/* + * 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._2133; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2133Mapper { + + Issue2133Mapper INSTANCE = Mappers.getMapper( Issue2133Mapper.class ); + + @BeanMapping(resultType = Target.class) + AbstractTarget map(Source source); + + class Source { + + private EmbeddedDto embedded; + + public EmbeddedDto getEmbedded() { + return embedded; + } + + public void setEmbedded(EmbeddedDto embedded) { + this.embedded = embedded; + } + } + + class Target extends AbstractTarget { + } + + abstract class AbstractTarget { + + private EmbeddedEntity embedded; + + public EmbeddedEntity getEmbedded() { + return embedded; + } + + public void setEmbedded(EmbeddedEntity embedded) { + this.embedded = embedded; + } + } + + class EmbeddedDto { + + private String s1; + + public String getS1() { + return s1; + } + + public void setS1(String s1) { + this.s1 = s1; + } + } + + class EmbeddedEntity { + + private String s1; + + public String getS1() { + return s1; + } + + public void setS1(String s1) { + this.s1 = s1; + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Test.java new file mode 100644 index 0000000000..8a8e8eebbe --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Test.java @@ -0,0 +1,23 @@ +/* + * 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._2133; + +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; + +@IssueKey("2133") +@WithClasses( Issue2133Mapper.class ) +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2133Test { + + @Test + public void shouldCompile() { + } + +} From 971abc48c7ed176a0977be8e41903da2b21d6e9c Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 4 Jul 2020 18:49:33 +0200 Subject: [PATCH 068/597] #2122 fix for 2 step mapping and generics (#2129) --- .../ap/internal/model/common/Type.java | 53 ++++++++ .../creation/MappingResolverImpl.java | 118 +++++++++--------- .../_2122/Issue2122Method2MethodMapper.java | 103 +++++++++++++++ .../Issue2122Method2TypeConversionMapper.java | 54 ++++++++ .../ap/test/bugs/_2122/Issue2122Test.java | 80 ++++++++++++ .../Issue2122TypeConversion2MethodMapper.java | 54 ++++++++ 6 files changed, 406 insertions(+), 56 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Method2MethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Method2TypeConversionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122TypeConversion2MethodMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 0974fe3477..ea27153cca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -24,9 +24,11 @@ import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; +import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; @@ -1073,6 +1075,57 @@ public boolean isLiteral() { return isLiteral; } + /** + * Steps through the declaredType in order to find a match for this typevar Type. It allignes with + * the provided parameterized type where this typeVar type is used. + * + * @param declaredType the type + * @param parameterizedType the parameterized type + * + * @return the matching declared type. + */ + public Type resolveTypeVarToType(Type declaredType, Type parameterizedType) { + if ( isTypeVar() ) { + TypeVarMatcher typeVarMatcher = new TypeVarMatcher( typeUtils, this ); + return typeVarMatcher.visit( parameterizedType.getTypeMirror(), declaredType ); + } + return this; + } + + private static class TypeVarMatcher extends SimpleTypeVisitor8 { + + private TypeVariable typeVarToMatch; + private Types types; + + TypeVarMatcher( Types types, Type typeVarToMatch ) { + super( null ); + this.typeVarToMatch = (TypeVariable) typeVarToMatch.getTypeMirror(); + this.types = types; + } + + @Override + public Type visitTypeVariable(TypeVariable t, Type parameterized) { + if ( types.isSameType( t, typeVarToMatch ) ) { + return parameterized; + } + return super.visitTypeVariable( t, parameterized ); + } + + @Override + public Type visitDeclared(DeclaredType t, Type parameterized) { + if ( types.isAssignable( types.erasure( t ), types.erasure( parameterized.getTypeMirror() ) ) ) { + // if same type, we can cast en assume number of type args are also the same + for ( int i = 0; i < t.getTypeArguments().size(); i++ ) { + Type result = visit( t.getTypeArguments().get( i ), parameterized.getTypeParameters().get( i ) ); + if ( result != null ) { + return result; + } + } + } + return super.visitDeclared( t, parameterized ); + } + } + /** * It strips all the {@code []} from the {@code className}. * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 2875cef67a..a42ea3826b 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -23,6 +23,7 @@ import org.mapstruct.ap.internal.conversion.ConversionProvider; import org.mapstruct.ap.internal.conversion.Conversions; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.model.Field; import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.MapperReference; @@ -43,7 +44,6 @@ import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; -import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; @@ -426,29 +426,31 @@ private Assignment resolveViaMethodAndMethod(Type sourceType, Type targetType) { // sourceMethod or builtIn that fits the signature B to C. Only then there is a match. If we have a match // a nested method call can be called. so C = methodY( methodX (A) ) for ( Method methodYCandidate : methodYCandidates ) { - if ( Object.class.getName() - .equals( methodYCandidate.getSourceParameters().get( 0 ).getType().getName() ) ) { + Type ySourceType = methodYCandidate.getSourceParameters().get( 0 ).getType(); + if ( Object.class.getName().equals( ySourceType.getName() ) ) { // java.lang.Object as intermediate result continue; } - methodRefY = - resolveViaMethod( methodYCandidate.getSourceParameters().get( 0 ).getType(), targetType, true ); - - if ( methodRefY != null ) { - selectionCriteria.setPreferUpdateMapping( false ); - Assignment methodRefX = - resolveViaMethod( sourceType, methodYCandidate.getSourceParameters().get( 0 ).getType(), true ); - selectionCriteria.setPreferUpdateMapping( savedPreferUpdateMapping ); - if ( methodRefX != null ) { - methodRefY.setAssignment( methodRefX ); - methodRefX.setAssignment( sourceRHS ); - break; - } - else { - // both should match; - supportingMethodCandidates.clear(); - methodRefY = null; + ySourceType = ySourceType.resolveTypeVarToType( targetType, methodYCandidate.getResultType() ); + + if ( ySourceType != null ) { + methodRefY = resolveViaMethod( ySourceType, targetType, true ); + if ( methodRefY != null ) { + + selectionCriteria.setPreferUpdateMapping( false ); + Assignment methodRefX = resolveViaMethod( sourceType, ySourceType, true ); + selectionCriteria.setPreferUpdateMapping( savedPreferUpdateMapping ); + if ( methodRefX != null ) { + methodRefY.setAssignment( methodRefX ); + methodRefX.setAssignment( sourceRHS ); + break; + } + else { + // both should match; + supportingMethodCandidates.clear(); + methodRefY = null; + } } } } @@ -475,28 +477,30 @@ private Assignment resolveViaConversionAndMethod(Type sourceType, Type targetTyp Assignment methodRefY = null; for ( Method methodYCandidate : methodYCandidates ) { - if ( Object.class.getName() - .equals( methodYCandidate.getSourceParameters().get( 0 ).getType().getName() ) ) { + Type ySourceType = methodYCandidate.getSourceParameters().get( 0 ).getType(); + if ( Object.class.getName().equals( ySourceType.getName() ) ) { // java.lang.Object as intermediate result continue; } - methodRefY = - resolveViaMethod( methodYCandidate.getSourceParameters().get( 0 ).getType(), targetType, true ); - - if ( methodRefY != null ) { - Type targetTypeX = methodYCandidate.getSourceParameters().get( 0 ).getType(); - ConversionAssignment conversionXRef = resolveViaConversion( sourceType, targetTypeX ); - if ( conversionXRef != null ) { - methodRefY.setAssignment( conversionXRef.getAssignment() ); - conversionXRef.getAssignment().setAssignment( sourceRHS ); - conversionXRef.reportMessageWhenNarrowing( messager, this ); - break; - } - else { - // both should match - supportingMethodCandidates.clear(); - methodRefY = null; + ySourceType = ySourceType.resolveTypeVarToType( targetType, methodYCandidate.getResultType() ); + + if ( ySourceType != null ) { + methodRefY = resolveViaMethod( ySourceType, targetType, true ); + if ( methodRefY != null ) { + Type targetTypeX = methodYCandidate.getSourceParameters().get( 0 ).getType(); + ConversionAssignment conversionXRef = resolveViaConversion( sourceType, targetTypeX ); + if ( conversionXRef != null ) { + methodRefY.setAssignment( conversionXRef.getAssignment() ); + conversionXRef.getAssignment().setAssignment( sourceRHS ); + conversionXRef.reportMessageWhenNarrowing( messager, this ); + break; + } + else { + // both should match + supportingMethodCandidates.clear(); + methodRefY = null; + } } } } @@ -524,29 +528,31 @@ private ConversionAssignment resolveViaMethodAndConversion(Type sourceType, Type // search the other way around for ( Method methodXCandidate : methodXCandidates ) { + Type xTargetType = methodXCandidate.getReturnType(); if ( methodXCandidate.isUpdateMethod() || - Object.class.getName().equals( methodXCandidate.getReturnType().getFullyQualifiedName() ) ) { + Object.class.getName().equals( xTargetType.getFullyQualifiedName() ) ) { // skip update methods || java.lang.Object as intermediate result continue; } - Assignment methodRefX = resolveViaMethod( - sourceType, - methodXCandidate.getReturnType(), - true - ); - if ( methodRefX != null ) { - conversionYRef = resolveViaConversion( methodXCandidate.getReturnType(), targetType ); - if ( conversionYRef != null ) { - conversionYRef.getAssignment().setAssignment( methodRefX ); - methodRefX.setAssignment( sourceRHS ); - conversionYRef.reportMessageWhenNarrowing( messager, this ); - break; - } - else { - // both should match; - supportingMethodCandidates.clear(); - conversionYRef = null; + xTargetType = + xTargetType.resolveTypeVarToType( sourceType, methodXCandidate.getParameters().get( 0 ).getType() ); + + if ( xTargetType != null ) { + Assignment methodRefX = resolveViaMethod( sourceType, xTargetType, true ); + if ( methodRefX != null ) { + conversionYRef = resolveViaConversion( methodXCandidate.getReturnType(), targetType ); + if ( conversionYRef != null ) { + conversionYRef.getAssignment().setAssignment( methodRefX ); + methodRefX.setAssignment( sourceRHS ); + conversionYRef.reportMessageWhenNarrowing( messager, this ); + break; + } + else { + // both should match; + supportingMethodCandidates.clear(); + conversionYRef = null; + } } } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Method2MethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Method2MethodMapper.java new file mode 100644 index 0000000000..033a314af2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Method2MethodMapper.java @@ -0,0 +1,103 @@ +/* + * 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._2122; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Bandowski + */ +@Mapper +public interface Issue2122Method2MethodMapper { + + Issue2122Method2MethodMapper INSTANCE = Mappers.getMapper( Issue2122Method2MethodMapper.class ); + + @Mapping(target = "embeddedTarget", source = "value") + @Mapping(target = "embeddedMapTarget", source = "value") + @Mapping(target = "embeddedListListTarget", source = "value") + Target toTarget(Source source); + + EmbeddedTarget toEmbeddedTarget(String value); + + default List singleEntry(T entry) { + return Collections.singletonList( entry ); + } + + default List> singleNestedListEntry(T entry) { + return Collections.singletonList( Collections.singletonList( entry ) ); + } + + default HashMap singleEntryMap(T entry) { + HashMap result = new HashMap<>( ); + result.put( "test", entry ); + return result; + } + + class Source { + String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class Target { + List embeddedTarget; + + Map embeddedMapTarget; + + List> embeddedListListTarget; + + public List getEmbeddedTarget() { + return embeddedTarget; + } + + public void setEmbeddedTarget(List embeddedTarget) { + this.embeddedTarget = embeddedTarget; + } + + public Map getEmbeddedMapTarget() { + return embeddedMapTarget; + } + + public void setEmbeddedMapTarget( Map embeddedMapTarget) { + this.embeddedMapTarget = embeddedMapTarget; + } + + public List> getEmbeddedListListTarget() { + return embeddedListListTarget; + } + + public void setEmbeddedListListTarget( + List> embeddedListListTarget) { + this.embeddedListListTarget = embeddedListListTarget; + } + } + + class EmbeddedTarget { + String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Method2TypeConversionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Method2TypeConversionMapper.java new file mode 100644 index 0000000000..43d40aa772 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Method2TypeConversionMapper.java @@ -0,0 +1,54 @@ +/* + * 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._2122; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface Issue2122Method2TypeConversionMapper { + + Issue2122Method2TypeConversionMapper INSTANCE = Mappers.getMapper( Issue2122Method2TypeConversionMapper.class ); + + @Mapping(target = "value", source = "strings") + Target toTarget(Source source); + + default T toFirstElement(List entry) { + return entry.get( 0 ); + } + + class Source { + List strings; + + public List getStrings() { + return strings; + } + + public void setStrings(List strings) { + this.strings = strings; + } + } + + class Target { + Integer value; + + public Integer getValue() { + return value; + } + + public void setValue(Integer value) { + this.value = value; + } + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Test.java new file mode 100644 index 0000000000..0d42a8f125 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Test.java @@ -0,0 +1,80 @@ +/* + * 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._2122; + +import java.util.Collections; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Christian Bandowski + */ +@IssueKey("2122") +@RunWith(AnnotationProcessorTestRunner.class) + +public class Issue2122Test { + + @Test + @WithClasses( Issue2122Method2MethodMapper.class ) + public void shouldMapMethod2Method() { + Issue2122Method2MethodMapper.Source source = new Issue2122Method2MethodMapper.Source(); + source.setValue( "value" ); + + Issue2122Method2MethodMapper.Target target = Issue2122Method2MethodMapper.INSTANCE.toTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getEmbeddedTarget() ).isNotNull(); + assertThat( target.getEmbeddedTarget() ).hasSize( 1 ) + .element( 0 ) + .extracting( Issue2122Method2MethodMapper.EmbeddedTarget::getValue ).isEqualTo( "value" ); + assertThat( target.getEmbeddedMapTarget() ).isNotNull(); + assertThat( target.getEmbeddedMapTarget() ).hasSize( 1 ); + assertThat( target.getEmbeddedMapTarget().get( "test" ) ) + .extracting( Issue2122Method2MethodMapper.EmbeddedTarget::getValue ).isEqualTo( "value" ); + assertThat( target.getEmbeddedListListTarget() ).isNotNull(); + assertThat( target.getEmbeddedListListTarget() ).hasSize( 1 ); + assertThat( target.getEmbeddedListListTarget().get( 0 ) ).hasSize( 1 ) + .element( 0 ) + .extracting( Issue2122Method2MethodMapper.EmbeddedTarget::getValue ).isEqualTo( "value" ); + } + + @Test + @WithClasses( Issue2122TypeConversion2MethodMapper.class ) + public void shouldMapTypeConversion2Method() { + Issue2122TypeConversion2MethodMapper.Source source = new Issue2122TypeConversion2MethodMapper.Source(); + source.setValue( 5 ); + + Issue2122TypeConversion2MethodMapper.Target target = + Issue2122TypeConversion2MethodMapper.INSTANCE.toTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getStrings() ).isNotNull(); + assertThat( target.getStrings() ).hasSize( 1 ) + .element( 0 ) + .isEqualTo( "5" ); + } + + @Test + @WithClasses( Issue2122Method2TypeConversionMapper.class ) + public void shouldMapMethod2TypeConversion() { + Issue2122Method2TypeConversionMapper.Source source = new Issue2122Method2TypeConversionMapper.Source(); + source.setStrings( Collections.singletonList( "5" ) ); + + Issue2122Method2TypeConversionMapper.Target target = + Issue2122Method2TypeConversionMapper.INSTANCE.toTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( 5 ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122TypeConversion2MethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122TypeConversion2MethodMapper.java new file mode 100644 index 0000000000..2555f53dee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122TypeConversion2MethodMapper.java @@ -0,0 +1,54 @@ +/* + * 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._2122; + +import java.util.Collections; +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface Issue2122TypeConversion2MethodMapper { + + Issue2122TypeConversion2MethodMapper INSTANCE = Mappers.getMapper( Issue2122TypeConversion2MethodMapper.class ); + + @Mapping(target = "strings", source = "value") + Target toTarget(Source source); + + default List singleEntry(T entry) { + return Collections.singletonList( entry ); + } + + class Source { + Integer value; + + public Integer getValue() { + return value; + } + + public void setValue(Integer value) { + this.value = value; + } + } + + class Target { + List strings; + + public List getStrings() { + return strings; + } + + public void setStrings(List strings) { + this.strings = strings; + } + } + +} From 1ce282362cba827d985bcb18a720708b5336c2e3 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 4 Jul 2020 21:50:20 +0200 Subject: [PATCH 069/597] #2101 inherited properties need to be analysed against redefined properties when inheriting mappings (#2103) --- .../model/source/MappingMethodOptions.java | 51 ++++++++++++-- .../processor/MapperCreationProcessor.java | 16 ++--- .../bugs/_2101/Issue2101AdditionalMapper.java | 45 +++++++++++++ .../ap/test/bugs/_2101/Issue2101Mapper.java | 49 ++++++++++++++ .../ap/test/bugs/_2101/Issue2101Test.java | 67 +++++++++++++++++++ 5 files changed, 212 insertions(+), 16 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101AdditionalMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 6c5cc1d04b..4a06acdd4b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -6,15 +6,16 @@ package org.mapstruct.ap.internal.model.source; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.util.accessor.Accessor; import static org.mapstruct.ap.internal.model.source.MappingOptions.getMappingTargetNamesBy; @@ -136,9 +137,8 @@ public void markAsFullyInitialized() { * * @param templateMethod the template method with the options to inherit, may be {@code null} * @param isInverse if {@code true}, the specified options are from an inverse method - * @param method the source method */ - public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse, SourceMethod method ) { + public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse) { MappingMethodOptions templateOptions = templateMethod.getOptions(); if ( null != templateOptions ) { if ( !getIterableMapping().hasAnnotation() && templateOptions.getIterableMapping().hasAnnotation() ) { @@ -200,13 +200,56 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } // now add all (does not override duplicates and leaves original mappings) - mappings.addAll( newMappings ); + addAllNonRedefined( newMappings ); // filter new mappings filterNestedTargetIgnores( mappings ); } } + private void addAllNonRedefined(Set inheritedMappings) { + Set redefinedSources = new HashSet<>(); + Set redefinedTargets = new HashSet<>(); + for ( MappingOptions redefinedMappings : mappings ) { + if ( redefinedMappings.getSourceName() != null ) { + redefinedSources.add( redefinedMappings.getSourceName() ); + } + if ( redefinedMappings.getTargetName() != null ) { + redefinedTargets.add( redefinedMappings.getTargetName() ); + } + } + for ( MappingOptions inheritedMapping : inheritedMappings ) { + if ( inheritedMapping.isIgnored() + || ( !isRedefined( redefinedSources, inheritedMapping.getSourceName() ) + && !isRedefined( redefinedTargets, inheritedMapping.getTargetName() ) ) + ) { + mappings.add( inheritedMapping ); + } + } + } + + private boolean isRedefined(Set redefinedNames, String inheritedName ) { + for ( String redefinedName : redefinedNames ) { + if ( elementsAreContainedIn( redefinedName, inheritedName ) ) { + return true; + } + } + return false; + } + + private boolean elementsAreContainedIn( String redefinedName, String inheritedName ) { + if ( redefinedName.startsWith( inheritedName ) ) { + // it is possible to redefine an exact matching source name, because the same source can be mapped to + // multiple targets. It is not possible for target, but caught by the Set and equals methoded in + // MappingOptions + if ( redefinedName.length() > inheritedName.length() ) { + // redefined.lenght() > inherited.length(), first following character should be separator + return '.' == redefinedName.charAt( inheritedName.length() ); + } + } + return false; + } + public void applyIgnoreAll(SourceMethod method, TypeFactory typeFactory ) { CollectionMappingStrategyGem cms = method.getOptions().getMapper().getCollectionMappingStrategy(); Type writeType = method.getResultType(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index c22377f984..e480eaf7a1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -447,10 +447,10 @@ private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConf // apply defined (@InheritConfiguration, @InheritInverseConfiguration) mappings if ( forwardTemplateMethod != null ) { - mappingOptions.applyInheritedOptions( forwardTemplateMethod, false, method ); + mappingOptions.applyInheritedOptions( forwardTemplateMethod, false ); } if ( inverseTemplateMethod != null ) { - mappingOptions.applyInheritedOptions( inverseTemplateMethod, true, method ); + mappingOptions.applyInheritedOptions( inverseTemplateMethod, true ); } // apply auto inherited options @@ -460,11 +460,7 @@ private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConf // but.. there should not be an @InheritedConfiguration if ( forwardTemplateMethod == null && inheritanceStrategy.isApplyForward() ) { if ( applicablePrototypeMethods.size() == 1 ) { - mappingOptions.applyInheritedOptions( - first( applicablePrototypeMethods ), - false, - method - ); + mappingOptions.applyInheritedOptions( first( applicablePrototypeMethods ), false ); } else if ( applicablePrototypeMethods.size() > 1 ) { messager.printMessage( @@ -477,11 +473,7 @@ else if ( applicablePrototypeMethods.size() > 1 ) { // or no @InheritInverseConfiguration if ( inverseTemplateMethod == null && inheritanceStrategy.isApplyReverse() ) { if ( applicableReversePrototypeMethods.size() == 1 ) { - mappingOptions.applyInheritedOptions( - first( applicableReversePrototypeMethods ), - true, - method - ); + mappingOptions.applyInheritedOptions( first( applicableReversePrototypeMethods ), true ); } else if ( applicableReversePrototypeMethods.size() > 1 ) { messager.printMessage( diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101AdditionalMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101AdditionalMapper.java new file mode 100644 index 0000000000..ea745d6bc1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101AdditionalMapper.java @@ -0,0 +1,45 @@ +/* + * 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._2101; + +import org.mapstruct.InheritConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2101AdditionalMapper { + + Issue2101AdditionalMapper INSTANCE = Mappers.getMapper( Issue2101AdditionalMapper.class ); + + @Mapping(target = "value1", source = "value.nestedValue1") + @Mapping(target = "value2", source = "value.nestedValue2") + @Mapping(target = "value3", source = "valueThrowOffPath") + Target map1(Source source); + + @InheritConfiguration + @Mapping(target = "value2", source = "value.nestedValue1") + @Mapping(target = "value3", constant = "test") + Target map2(Source source); + + //CHECKSTYLE:OFF + class Source { + public String valueThrowOffPath; + public NestedSource value; + } + + class Target { + public String value1; + public String value2; + public String value3; + } + + class NestedSource { + public String nestedValue1; + public String nestedValue2; + } + //CHECKSTYLE:ON +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Mapper.java new file mode 100644 index 0000000000..5a1f01fa72 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Mapper.java @@ -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._2101; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2101Mapper { + + Issue2101Mapper INSTANCE = Mappers.getMapper( Issue2101Mapper.class ); + + @Mapping(target = "value1", source = "codeValue1") + @Mapping(target = "value2", source = "codeValue2") + Source map(Target target); + + @InheritInverseConfiguration + @Mapping(target = "codeValue1.code", constant = "c1") + @Mapping(target = "codeValue1.value", source = "value1") + @Mapping(target = "codeValue2.code", constant = "c2") + @Mapping(target = "codeValue2.value", source = "value2") + Target map(Source source); + + default String mapFrom( CodeValuePair cv ) { + return cv.code; + } + + //CHECKSTYLE:OFF + class Source { + public String value1; + public String value2; + } + + class Target { + public CodeValuePair codeValue1; + public CodeValuePair codeValue2; + } + + class CodeValuePair { + public String code; + public String value; + } + //CHECKSTYLE:ON +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java new file mode 100644 index 0000000000..36c2c32134 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java @@ -0,0 +1,67 @@ +/* + * 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._2101; + +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 static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("2101") +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2101Test { + + @Test + @WithClasses(Issue2101Mapper.class) + public void shouldMap() { + + Issue2101Mapper.Source source = new Issue2101Mapper.Source(); + source.value1 = "v1"; + source.value2 = "v2"; + + Issue2101Mapper.Target target = Issue2101Mapper.INSTANCE.map( source ); + + assertThat( target.codeValue1.code ).isEqualTo( "c1" ); + assertThat( target.codeValue1.value ).isEqualTo( "v1" ); + assertThat( target.codeValue2.code ).isEqualTo( "c2" ); + assertThat( target.codeValue2.value ).isEqualTo( "v2" ); + + } + + @Test + @WithClasses(Issue2101AdditionalMapper.class) + public void shouldMapSomeAdditionalTests1() { + Issue2101AdditionalMapper.Source source = new Issue2101AdditionalMapper.Source(); + source.value = new Issue2101AdditionalMapper.NestedSource(); + source.value.nestedValue1 = "value1"; + source.value.nestedValue2 = "value2"; + source.valueThrowOffPath = "value3"; + + Issue2101AdditionalMapper.Target target = Issue2101AdditionalMapper.INSTANCE.map1( source ); + assertThat( target.value1 ).isEqualTo( "value1" ); + assertThat( target.value2 ).isEqualTo( "value2" ); + assertThat( target.value3 ).isEqualTo( "value3" ); + } + + @Test + @WithClasses(Issue2101AdditionalMapper.class) + public void shouldMapSomeAdditionalTests2() { + Issue2101AdditionalMapper.Source source = new Issue2101AdditionalMapper.Source(); + source.value = new Issue2101AdditionalMapper.NestedSource(); + source.value.nestedValue1 = "value1"; + source.value.nestedValue2 = "value2"; + source.valueThrowOffPath = "value3"; + + Issue2101AdditionalMapper.Target target = Issue2101AdditionalMapper.INSTANCE.map2( source ); + assertThat( target.value1 ).isEqualTo( "value1" ); + assertThat( target.value2 ).isEqualTo( "value1" ); + assertThat( target.value3 ).isEqualTo( "test" ); + + } +} From 12ac3486096bf8b4e7a1e14137f560f03c8083ed Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 4 Jul 2020 22:06:30 +0200 Subject: [PATCH 070/597] #2136 performance improvement 2 step mappings (#2138) --- .../creation/MappingResolverImpl.java | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index a42ea3826b..67eebbb30b 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -39,6 +39,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.Method; +import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMappingMethods; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod; import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; @@ -376,32 +377,56 @@ private Assignment resolveViaMethod(Type sourceType, Type targetType, boolean co return null; } + /** + * Applies matching to given method only + * + * @param sourceType the source type + * @param targetType the target type + * @param method the method to match + * @return an assignment if a match, given the criteria could be found. When the method is a + * buildIn method, all the bookkeeping is applied. + */ + private Assignment applyMatching(Type sourceType, Type targetType, Method method) { + + if ( method instanceof SourceMethod ) { + SelectedMethod selectedMethod = + getBestMatch( java.util.Collections.singletonList( method ), sourceType, targetType ); + return selectedMethod != null ? getMappingMethodReference( selectedMethod ) : null; + } + else if ( method instanceof BuiltInMethod ) { + return resolveViaBuiltInMethod( sourceType, targetType ); + } + return null; + } + private Assignment resolveViaBuiltInMethod(Type sourceType, Type targetType) { SelectedMethod matchingBuiltInMethod = getBestMatch( builtInMethods.getBuiltInMethods(), sourceType, targetType ); if ( matchingBuiltInMethod != null ) { - - Set allUsedFields = new HashSet<>( mapperReferences ); - SupportingField.addAllFieldsIn( supportingMethodCandidates, allUsedFields ); - SupportingMappingMethod supportingMappingMethod = - new SupportingMappingMethod( matchingBuiltInMethod.getMethod(), allUsedFields ); - supportingMethodCandidates.add( supportingMappingMethod ); - ConversionContext ctx = new DefaultConversionContext( - typeFactory, - messager, - sourceType, - targetType, - formattingParameters - ); - Assignment methodReference = MethodReference.forBuiltInMethod( matchingBuiltInMethod.getMethod(), ctx ); - methodReference.setAssignment( sourceRHS ); - return methodReference; + return createFromBuiltInMethod( matchingBuiltInMethod.getMethod() ); } return null; } + private Assignment createFromBuiltInMethod(BuiltInMethod method) { + Set allUsedFields = new HashSet<>( mapperReferences ); + SupportingField.addAllFieldsIn( supportingMethodCandidates, allUsedFields ); + SupportingMappingMethod supportingMappingMethod = new SupportingMappingMethod( method, allUsedFields ); + supportingMethodCandidates.add( supportingMappingMethod ); + ConversionContext ctx = new DefaultConversionContext( + typeFactory, + messager, + method.getSourceParameters().get( 0 ).getType(), + method.getResultType(), + formattingParameters + ); + Assignment methodReference = MethodReference.forBuiltInMethod( method, ctx ); + methodReference.setAssignment( sourceRHS ); + return methodReference; + } + /** * Suppose mapping required from A to C and: *

      @@ -435,7 +460,7 @@ private Assignment resolveViaMethodAndMethod(Type sourceType, Type targetType) { ySourceType = ySourceType.resolveTypeVarToType( targetType, methodYCandidate.getResultType() ); if ( ySourceType != null ) { - methodRefY = resolveViaMethod( ySourceType, targetType, true ); + methodRefY = applyMatching( ySourceType, targetType, methodYCandidate ); if ( methodRefY != null ) { selectionCriteria.setPreferUpdateMapping( false ); @@ -486,10 +511,9 @@ private Assignment resolveViaConversionAndMethod(Type sourceType, Type targetTyp ySourceType = ySourceType.resolveTypeVarToType( targetType, methodYCandidate.getResultType() ); if ( ySourceType != null ) { - methodRefY = resolveViaMethod( ySourceType, targetType, true ); + methodRefY = applyMatching( ySourceType, targetType, methodYCandidate ); if ( methodRefY != null ) { - Type targetTypeX = methodYCandidate.getSourceParameters().get( 0 ).getType(); - ConversionAssignment conversionXRef = resolveViaConversion( sourceType, targetTypeX ); + ConversionAssignment conversionXRef = resolveViaConversion( sourceType, ySourceType ); if ( conversionXRef != null ) { methodRefY.setAssignment( conversionXRef.getAssignment() ); conversionXRef.getAssignment().setAssignment( sourceRHS ); @@ -539,9 +563,9 @@ private ConversionAssignment resolveViaMethodAndConversion(Type sourceType, Type xTargetType.resolveTypeVarToType( sourceType, methodXCandidate.getParameters().get( 0 ).getType() ); if ( xTargetType != null ) { - Assignment methodRefX = resolveViaMethod( sourceType, xTargetType, true ); + Assignment methodRefX = applyMatching( sourceType, xTargetType, methodXCandidate ); if ( methodRefX != null ) { - conversionYRef = resolveViaConversion( methodXCandidate.getReturnType(), targetType ); + conversionYRef = resolveViaConversion( xTargetType, targetType ); if ( conversionYRef != null ) { conversionYRef.getAssignment().setAssignment( methodRefX ); methodRefX.setAssignment( sourceRHS ); From fc4f65ddb67ccdfdfc112f969e6507b8bd700895 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Jul 2020 23:11:41 +0200 Subject: [PATCH 071/597] [maven-release-plugin] prepare release 1.4.0.Beta2 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 6c908377c8..b2289c2213 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta2 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 63311b5950..762d94f36f 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta2 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index c74e0b8bb1..5060618d5a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta2 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index b404ed3cee..87d025ac2c 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta2 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index ceb3cb1db6..80ff15c786 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta2 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index a643bab4b8..c33feee073 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta2 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index b26cf8683d..978d3f5476 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta2 pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.Beta2 diff --git a/pom.xml b/pom.xml index f9c649c168..14893a3d9d 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta2 parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.Beta2 diff --git a/processor/pom.xml b/processor/pom.xml index c047301268..680f2564cb 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta2 ../parent/pom.xml From 2fede3583d109cb2f6827ebbcb8c9cfaf0463f51 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Jul 2020 23:11:41 +0200 Subject: [PATCH 072/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index b2289c2213..6c908377c8 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta2 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 762d94f36f..63311b5950 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta2 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 5060618d5a..c74e0b8bb1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta2 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 87d025ac2c..b404ed3cee 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta2 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 80ff15c786..ceb3cb1db6 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta2 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index c33feee073..a643bab4b8 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta2 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 978d3f5476..b26cf8683d 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta2 + 1.4.0-SNAPSHOT pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.Beta2 + HEAD diff --git a/pom.xml b/pom.xml index 14893a3d9d..f9c649c168 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta2 + 1.4.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.Beta2 + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 680f2564cb..c047301268 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta2 + 1.4.0-SNAPSHOT ../parent/pom.xml From c0d88f86bf4d3de7f2f13a1c8e013d4473039142 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sun, 5 Jul 2020 14:41:08 +0200 Subject: [PATCH 073/597] #2139 reproducer (#2140) * #2139 reproducer * #2139 solution * #2139 license --- .../model/source/MappingMethodOptions.java | 4 +- .../ap/test/bugs/_2101/Issue2101Test.java | 16 ++++++++ .../_2101/Issue2102IgnoreAllButMapper.java | 38 +++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2102IgnoreAllButMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 4a06acdd4b..e0007949a9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -238,10 +238,10 @@ private boolean isRedefined(Set redefinedNames, String inheritedName ) { } private boolean elementsAreContainedIn( String redefinedName, String inheritedName ) { - if ( redefinedName.startsWith( inheritedName ) ) { + if ( inheritedName != null && redefinedName.startsWith( inheritedName ) ) { // it is possible to redefine an exact matching source name, because the same source can be mapped to // multiple targets. It is not possible for target, but caught by the Set and equals methoded in - // MappingOptions + // MappingOptions. SourceName == null also could hint at redefinition if ( redefinedName.length() > inheritedName.length() ) { // redefined.lenght() > inherited.length(), first following character should be separator return '.' == redefinedName.charAt( inheritedName.length() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java index 36c2c32134..9148184b25 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java @@ -64,4 +64,20 @@ public void shouldMapSomeAdditionalTests2() { assertThat( target.value3 ).isEqualTo( "test" ); } + + @Test + @WithClasses(Issue2102IgnoreAllButMapper.class) + public void shouldApplyIgnoreAllButTemplateOfMethod1() { + + Issue2102IgnoreAllButMapper.Source source = new Issue2102IgnoreAllButMapper.Source(); + source.value1 = "value1"; + source.value2 = "value2"; + + Issue2102IgnoreAllButMapper.Target target = Issue2102IgnoreAllButMapper.INSTANCE.map1( source ); + assertThat( target.value1 ).isEqualTo( "value1" ); + + target = Issue2102IgnoreAllButMapper.INSTANCE.map2( source ); + assertThat( target.value1 ).isEqualTo( "value2" ); + + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2102IgnoreAllButMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2102IgnoreAllButMapper.java new file mode 100644 index 0000000000..9634ed310b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2102IgnoreAllButMapper.java @@ -0,0 +1,38 @@ +/* + * 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._2101; + +import org.mapstruct.BeanMapping; +import org.mapstruct.InheritConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2102IgnoreAllButMapper { + + Issue2102IgnoreAllButMapper INSTANCE = Mappers.getMapper( Issue2102IgnoreAllButMapper.class ); + + @BeanMapping( ignoreByDefault = true ) + @Mapping(target = "value1") // but do map value1 + Target map1(Source source); + + @InheritConfiguration + @Mapping(target = "value1", source = "value2" ) + Target map2(Source source); + + //CHECKSTYLE:OFF + class Source { + public String value1; + public String value2; + } + + class Target { + public String value1; + } + //CHECKSTYLE:ON + +} From 74f281fa3e7359bcac4f86c3221ae5c392013a67 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sun, 5 Jul 2020 22:22:01 +0200 Subject: [PATCH 074/597] #2135 improved messages for not able to select qualified method (#2141) * #2135 improved messages for not able to select qualified method --- .../creation/MappingResolverImpl.java | 52 +++++++++-- .../mapstruct/ap/internal/util/Message.java | 7 +- .../ap/internal/util/MessageConstants.java | 15 ++++ .../selection/qualifier/QualifierTest.java | 4 +- ...eousMessageByAnnotationAndNamedMapper.java | 54 +++++++++++ .../ErroneousMessageByAnnotationMapper.java | 45 ++++++++++ .../errors/ErroneousMessageByNamedMapper.java | 45 ++++++++++ .../qualifier/errors/QualfierMessageTest.java | 90 +++++++++++++++++++ 8 files changed, 302 insertions(+), 10 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/MessageConstants.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByAnnotationAndNamedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByAnnotationMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByNamedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 67eebbb30b..5427ddefa9 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -10,8 +10,11 @@ import java.util.List; import java.util.Set; import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; @@ -48,6 +51,7 @@ import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.MessageConstants; import org.mapstruct.ap.internal.util.NativeTypes; import org.mapstruct.ap.internal.util.Strings; @@ -260,13 +264,7 @@ && allowDirect( sourceType, targetType ) ) { } if ( hasQualfiers() ) { - messager.printMessage( - mappingMethod.getExecutable(), - positionHint, - Message.GENERAL_NO_QUALIFYING_METHOD, - Strings.join( selectionCriteria.getQualifiers(), ", " ), - Strings.join( selectionCriteria.getQualifiedByNames(), ", " ) - ); + printQualifierMessage( selectionCriteria ); } else if ( allowMappingMethod() ) { // only forge if we would allow mapping method @@ -281,6 +279,46 @@ private boolean hasQualfiers() { return selectionCriteria != null && selectionCriteria.hasQualfiers(); } + private void printQualifierMessage(SelectionCriteria selectionCriteria ) { + + List annotations = selectionCriteria.getQualifiers().stream() + .filter( DeclaredType.class::isInstance ) + .map( DeclaredType.class::cast ) + .map( DeclaredType::asElement ) + .map( Element::getSimpleName ) + .map( Name::toString ) + .map( a -> "@" + a ) + .collect( Collectors.toList() ); + List names = selectionCriteria.getQualifiedByNames(); + + if ( !annotations.isEmpty() && !names.isEmpty() ) { + messager.printMessage( + mappingMethod.getExecutable(), + positionHint, + Message.GENERAL_NO_QUALIFYING_METHOD_COMBINED, + Strings.join( names, MessageConstants.AND ), + Strings.join( annotations, MessageConstants.AND ) + ); + } + else if ( !annotations.isEmpty() ) { + messager.printMessage( + mappingMethod.getExecutable(), + positionHint, + Message.GENERAL_NO_QUALIFYING_METHOD_ANNOTATION, + Strings.join( annotations, MessageConstants.AND ) + ); + } + else { + messager.printMessage( + mappingMethod.getExecutable(), + positionHint, + Message.GENERAL_NO_QUALIFYING_METHOD_NAMED, + Strings.join( names, MessageConstants.AND ) + ); + } + + } + private boolean allowDirect( Type sourceType, Type targetType ) { if ( selectionCriteria != null && selectionCriteria.isAllowDirect() ) { return true; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 4c46c09917..ae66d38776 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -7,6 +7,8 @@ import javax.tools.Diagnostic; +import static org.mapstruct.ap.internal.util.MessageConstants.FAQ_QUALIFIER_URL; + /** * A message used in warnings/errors raised by the annotation processor. * @@ -119,7 +121,9 @@ public enum Message { GENERAL_JODA_NOT_ON_CLASSPATH( "Cannot validate Joda dateformat, no Joda on classpath. Consider adding Joda to the annotation processorpath.", Diagnostic.Kind.WARNING ), GENERAL_NOT_ALL_FORGED_CREATED( "Internal Error in creation of Forged Methods, it was expected all Forged Methods to finished with creation, but %s did not" ), GENERAL_NO_SUITABLE_CONSTRUCTOR( "%s does not have an accessible constructor." ), - GENERAL_NO_QUALIFYING_METHOD( "No qualifying method found for qualifiers: %s and / or qualifying names: %s" ), + GENERAL_NO_QUALIFYING_METHOD_ANNOTATION( "Qualifier error. No method found annotated with: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ), + GENERAL_NO_QUALIFYING_METHOD_NAMED( "Qualifier error. No method found annotated with @Named#value: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ), + GENERAL_NO_QUALIFYING_METHOD_COMBINED( "Qualifier error. No method found annotated with @Named#value: [ %s ], annotated with [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ), BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD( "More than one builder creation method for \"%s\". Found methods: \"%s\". Builder will not be used. Consider implementing a custom BuilderProvider SPI.", Diagnostic.Kind.WARNING ), BUILDER_NO_BUILD_METHOD_FOUND("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\".", Diagnostic.Kind.ERROR ), @@ -162,6 +166,7 @@ public enum Message { VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." ); // CHECKSTYLE:ON + private final String description; private final Diagnostic.Kind kind; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/MessageConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/MessageConstants.java new file mode 100644 index 0000000000..d3dabdb2d8 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/MessageConstants.java @@ -0,0 +1,15 @@ +/* + * 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.internal.util; + +public final class MessageConstants { + + public static final String AND = " and "; + public static final String FAQ_QUALIFIER_URL = "https://mapstruct.org/faq/#qualifier"; + + private MessageConstants() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java index 912affa1a3..37ecfb1be6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java @@ -103,8 +103,8 @@ public void shouldMatchClassAndMethod() { kind = Kind.ERROR, line = 28, message = - "No qualifying method found for qualifiers: org.mapstruct.ap.test.selection.qualifier.annotation" + - ".NonQualifierAnnotated and / or qualifying names: "), + "Qualifier error. No method found annotated with: [ @NonQualifierAnnotated ]. " + + "See https://mapstruct.org/faq/#qualifier for more info."), @Diagnostic( type = ErroneousMapper.class, kind = Kind.ERROR, diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByAnnotationAndNamedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByAnnotationAndNamedMapper.java new file mode 100644 index 0000000000..acf9674fe2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByAnnotationAndNamedMapper.java @@ -0,0 +1,54 @@ +/* + * 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.selection.qualifier.errors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Qualifier; + +@Mapper +public interface ErroneousMessageByAnnotationAndNamedMapper { + + @Mapping(target = "nested", source = "value", qualifiedBy = { + SelectMe1.class, + SelectMe2.class + }, qualifiedByName = { "selectMe1", "selectMe2" }) + Target map(Source source); + + default Nested map(String in) { + return null; + } + + // CHECKSTYLE:OFF + class Source { + public String value; + } + + class Target { + public Nested nested; + } + + class Nested { + public String value; + } + // CHECKSTYLE ON + + @Qualifier + @java.lang.annotation.Target(ElementType.METHOD) + @Retention(RetentionPolicy.CLASS) + public @interface SelectMe1 { + } + + @Qualifier + @java.lang.annotation.Target(ElementType.METHOD) + @Retention(RetentionPolicy.CLASS) + public @interface SelectMe2 { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByAnnotationMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByAnnotationMapper.java new file mode 100644 index 0000000000..d5aa391f15 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByAnnotationMapper.java @@ -0,0 +1,45 @@ +/* + * 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.selection.qualifier.errors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Qualifier; + +@Mapper +public interface ErroneousMessageByAnnotationMapper { + + @Mapping(target = "nested", source = "value", qualifiedBy = SelectMe.class) + Target map(Source source); + + default Nested map(String in) { + return null; + } + + // CHECKSTYLE:OFF + class Source { + public String value; + } + + class Target { + public Nested nested; + } + + class Nested { + public String value; + } + // CHECKSTYLE ON + + @Qualifier + @java.lang.annotation.Target(ElementType.METHOD) + @Retention(RetentionPolicy.CLASS) + public @interface SelectMe { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByNamedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByNamedMapper.java new file mode 100644 index 0000000000..44f0a57760 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByNamedMapper.java @@ -0,0 +1,45 @@ +/* + * 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.selection.qualifier.errors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Qualifier; + +@Mapper +public interface ErroneousMessageByNamedMapper { + + @Mapping(target = "nested", source = "value", qualifiedByName = "SelectMe") + Target map(Source source); + + default Nested map(String in) { + return null; + } + + // CHECKSTYLE:OFF + class Source { + public String value; + } + + class Target { + public Nested nested; + } + + class Nested { + public String value; + } + // CHECKSTYLE ON + + @Qualifier + @java.lang.annotation.Target(ElementType.METHOD) + @Retention(RetentionPolicy.CLASS) + public @interface SelectMe { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java new file mode 100644 index 0000000000..4c8edc64dd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java @@ -0,0 +1,90 @@ +/* + * 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.selection.qualifier.errors; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static javax.tools.Diagnostic.Kind.ERROR; + +@IssueKey("2135") +@RunWith(AnnotationProcessorTestRunner.class) +public class QualfierMessageTest { + + @Test + @WithClasses(ErroneousMessageByAnnotationMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = ErroneousMessageByAnnotationMapper.class, + kind = ERROR, + line = 19, + message = "Qualifier error. No method found annotated with: [ @SelectMe ]. " + + "See https://mapstruct.org/faq/#qualifier for more info."), + @Diagnostic( + type = ErroneousMessageByAnnotationMapper.class, + kind = ERROR, + line = 19, + messageRegExp = "Can't map property.*") + + } + ) + public void testNoQualifyingMethodByAnnotationFound() { + } + + @Test + @WithClasses(ErroneousMessageByNamedMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = ErroneousMessageByNamedMapper.class, + kind = ERROR, + line = 19, + message = "Qualifier error. No method found annotated with @Named#value: [ SelectMe ]. " + + "See https://mapstruct.org/faq/#qualifier for more info."), + @Diagnostic( + type = ErroneousMessageByNamedMapper.class, + kind = ERROR, + line = 19, + messageRegExp = "Can't map property.*") + + } + ) + public void testNoQualifyingMethodByNamedFound() { + } + + @Test + @WithClasses(ErroneousMessageByAnnotationAndNamedMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = ErroneousMessageByAnnotationAndNamedMapper.class, + kind = ERROR, + line = 19, + message = + "Qualifier error. No method found annotated with @Named#value: [ selectMe1 and selectMe2 ], " + + "annotated with [ @SelectMe1 and @SelectMe2 ]. " + + "See https://mapstruct.org/faq/#qualifier for more info."), + @Diagnostic( + type = ErroneousMessageByAnnotationAndNamedMapper.class, + kind = ERROR, + line = 19, + messageRegExp = "Can't map property.*") + + } + ) + public void testNoQualifyingMethodByAnnotationAndNamedFound() { + } +} From 2a849dca12c6547a56de6cd68d49bad1ca53390a Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Thu, 16 Jul 2020 13:49:49 +0200 Subject: [PATCH 075/597] #2145 fixing 2 step mapping methods (refactoring) (#2146) --- .../ap/internal/model/common/Type.java | 18 + .../ap/internal/model/source/Method.java | 14 + .../internal/model/source/SourceMethod.java | 12 +- .../model/source/builtin/BuiltInMethod.java | 20 +- .../model/source/builtin/JaxbElemToValue.java | 10 +- .../model/source/selector/SelectedMethod.java | 18 + .../selector/XmlElementDeclSelector.java | 2 +- .../creation/MappingResolverImpl.java | 749 +++++++++++------- .../ap/internal/util/Collections.java | 13 + .../mapstruct/ap/internal/util/Message.java | 4 + .../ap/test/bugs/_2145/Issue2145Mapper.java | 85 ++ .../ap/test/bugs/_2145/Issue2145Test.java | 34 + .../twosteperror/ErroneousMapperCM.java | 56 ++ .../twosteperror/ErroneousMapperMC.java | 52 ++ .../twosteperror/ErroneousMapperMM.java | 74 ++ .../twosteperror/TwoStepMappingTest.java | 79 ++ 16 files changed, 946 insertions(+), 294 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperCM.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMC.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMM.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index ea27153cca..a829fa61a2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -482,6 +482,24 @@ public boolean isAssignableTo(Type other) { return typeUtils.isAssignable( typeMirrorToMatch, other.typeMirror ); } + /** + * Whether this type is raw assignable to the given other type. We can't make a verdict on typevars, + * they need to be resolved first. + * + * @param other The other type. + * + * @return {@code true} if and only if this type is assignable to the given other type. + */ + public boolean isRawAssignableTo(Type other) { + if ( isTypeVar() || other.isTypeVar() ) { + return true; + } + if ( equals( other ) ) { + return true; + } + return typeUtils.isAssignable( typeUtils.erasure( typeMirror ), typeUtils.erasure( other.typeMirror ) ); + } + /** * getPropertyReadAccessors * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index 3930dc76b1..39d827d975 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -187,4 +187,18 @@ public interface Method { default boolean isMappingTargetAssignableToReturnType() { return isUpdateMethod() && getResultType().isAssignableTo( getReturnType() ); } + + /** + * @return the first source type, intended for mapping methods from single source to target + */ + default Type getMappingSourceType() { + return getSourceParameters().get( 0 ).getType(); + } + + /** + * @return the short name for error messages + */ + default String shortName() { + return getResultType().getName() + ":" + getName() + "(" + getMappingSourceType().getName() + ")"; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 3551db8c51..9ed432d3ec 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -294,7 +294,7 @@ public boolean inverses(SourceMethod method) { return method.getDeclaringMapper() == null && method.isAbstract() && getSourceParameters().size() == 1 && method.getSourceParameters().size() == 1 - && first( getSourceParameters() ).getType().isAssignableTo( method.getResultType() ) + && getMappingSourceType().isAssignableTo( method.getResultType() ) && getResultType().isAssignableTo( first( method.getSourceParameters() ).getType() ); } @@ -326,7 +326,7 @@ public Parameter getTargetTypeParameter() { public boolean isIterableMapping() { if ( isIterableMapping == null ) { isIterableMapping = getSourceParameters().size() == 1 - && first( getSourceParameters() ).getType().isIterableType() + && getMappingSourceType().isIterableType() && getResultType().isIterableType(); } return isIterableMapping; @@ -335,9 +335,9 @@ && first( getSourceParameters() ).getType().isIterableType() public boolean isStreamMapping() { if ( isStreamMapping == null ) { isStreamMapping = getSourceParameters().size() == 1 - && ( first( getSourceParameters() ).getType().isIterableType() && getResultType().isStreamType() - || first( getSourceParameters() ).getType().isStreamType() && getResultType().isIterableType() - || first( getSourceParameters() ).getType().isStreamType() && getResultType().isStreamType() ); + && ( getMappingSourceType().isIterableType() && getResultType().isStreamType() + || getMappingSourceType().isStreamType() && getResultType().isIterableType() + || getMappingSourceType().isStreamType() && getResultType().isStreamType() ); } return isStreamMapping; } @@ -345,7 +345,7 @@ public boolean isStreamMapping() { public boolean isMapMapping() { if ( isMapMapping == null ) { isMapMapping = getSourceParameters().size() == 1 - && first( getSourceParameters() ).getType().isMapType() + && getMappingSourceType().isMapType() && getResultType().isMapType(); } return isMapMapping; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java index 0621a68db7..360eb56bac 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java @@ -67,21 +67,13 @@ public boolean matches(List sourceTypes, Type targetType) { Type sourceType = first( sourceTypes ); - if ( getReturnType().isAssignableTo( targetType.erasure() ) - && sourceType.erasure().isAssignableTo( getParameter().getType() ) ) { - return doTypeVarsMatch( sourceType, targetType ); - } - if ( getReturnType().getFullyQualifiedName().equals( "java.lang.Object" ) - && sourceType.erasure().isAssignableTo( getParameter().getType() ) ) { - // return type could be a type parameter T - return doTypeVarsMatch( sourceType, targetType ); - } - if ( getReturnType().isAssignableTo( targetType.erasure() ) - && getParameter().getType().getFullyQualifiedName().equals( "java.lang.Object" ) ) { - // parameter type could be a type parameter T - return doTypeVarsMatch( sourceType, targetType ); + Type returnType = getReturnType().resolveTypeVarToType( sourceType, getParameter().getType() ); + if ( returnType == null ) { + return false; } - return false; + + return returnType.isAssignableTo( targetType ) + && sourceType.erasure().isAssignableTo( getParameter().getType() ); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java index 1ae3efe3c9..4d116266fd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java @@ -5,15 +5,16 @@ */ package org.mapstruct.ap.internal.model.source.builtin; +import static org.mapstruct.ap.internal.util.Collections.asSet; + import java.util.Set; + import javax.xml.bind.JAXBElement; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import static org.mapstruct.ap.internal.util.Collections.asSet; - /** * @author Sjaak Derksen */ @@ -24,8 +25,9 @@ public class JaxbElemToValue extends BuiltInMethod { private final Set importTypes; public JaxbElemToValue(TypeFactory typeFactory) { - this.parameter = new Parameter( "element", typeFactory.getType( JAXBElement.class ) ); - this.returnType = typeFactory.getType( Object.class ); + Type type = typeFactory.getType( JAXBElement.class ); + this.parameter = new Parameter( "element", type ); + this.returnType = type.getTypeParameters().get( 0 ); this.importTypes = asSet( parameter.getType() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectedMethod.java index bc0696d2b2..7acb1af210 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectedMethod.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.model.source.selector; import java.util.List; +import java.util.Objects; import org.mapstruct.ap.internal.model.common.ParameterBinding; import org.mapstruct.ap.internal.model.source.Method; @@ -39,4 +40,21 @@ public void setParameterBindings(List parameterBindings) { public String toString() { return method.toString(); } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + SelectedMethod that = (SelectedMethod) o; + return method.equals( that.method ); + } + + @Override + public int hashCode() { + return Objects.hash( method ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java index cb9d309171..c3c394aefd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java @@ -69,7 +69,7 @@ public List> getMatchingMethods(Method mapp } String name = xmlElementDecl.name().get(); - TypeMirror scope = xmlElementDecl.scope().get(); + TypeMirror scope = xmlElementDecl.scope().getValue(); boolean nameIsSetAndMatches = name != null && name.equals( xmlElementRefInfo.nameValue() ); boolean scopeIsSetAndMatches = diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 5427ddefa9..5edd792944 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -5,12 +5,23 @@ */ package org.mapstruct.ap.internal.processor.creation; +import static java.util.Collections.singletonList; +import static org.mapstruct.ap.internal.util.Collections.first; +import static org.mapstruct.ap.internal.util.Collections.firstKey; +import static org.mapstruct.ap.internal.util.Collections.firstValue; + import java.util.ArrayList; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; + import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -42,7 +53,6 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.Method; -import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMappingMethods; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod; import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; @@ -55,9 +65,6 @@ import org.mapstruct.ap.internal.util.NativeTypes; import org.mapstruct.ap.internal.util.Strings; -import static java.util.Collections.singletonList; -import static org.mapstruct.ap.internal.util.Collections.first; - /** * The one and only implementation of {@link MappingResolver}. The class has been split into an interface an * implementation for the sake of avoiding package dependencies. Specifically, this implementation refers to classes @@ -78,6 +85,8 @@ public class MappingResolverImpl implements MappingResolver { private final BuiltInMappingMethods builtInMethods; private final MethodSelectors methodSelectors; + private static final String JL_OBJECT_NAME = Object.class.getName(); + /** * Private methods which are not present in the original mapper interface and are added to map certain property * types. @@ -113,7 +122,9 @@ public Assignment getTargetAssignment(Method mappingMethod, Type targetType, sourceRHS, criteria, positionHint, - forger + forger, + builtInMethods.getBuiltInMethods(), + messager ); return attempt.getTargetAssignment( sourceRHS.getSourceTypeForMatching(), targetType ); @@ -141,10 +152,11 @@ private class ResolvingAttempt { private final List methods; private final SelectionCriteria selectionCriteria; private final SourceRHS sourceRHS; - private final boolean savedPreferUpdateMapping; private final FormattingParameters formattingParameters; private final AnnotationMirror positionHint; private final Supplier forger; + private final List builtIns; + private final FormattingMessager messager; // resolving via 2 steps creates the possibility of wrong matches, first builtin method matches, // second doesn't. In that case, the first builtin method should not lead to a supported method @@ -155,7 +167,9 @@ private ResolvingAttempt(List sourceModel, Method mappingMethod, FormattingParameters formattingParameters, SourceRHS sourceRHS, SelectionCriteria criteria, AnnotationMirror positionHint, - Supplier forger) { + Supplier forger, + List builtIns, + FormattingMessager messager) { this.mappingMethod = mappingMethod; this.methods = filterPossibleCandidateMethods( sourceModel ); @@ -164,9 +178,10 @@ private ResolvingAttempt(List sourceModel, Method mappingMethod, this.sourceRHS = sourceRHS; this.supportingMethodCandidates = new HashSet<>(); this.selectionCriteria = criteria; - this.savedPreferUpdateMapping = criteria.isPreferUpdateMapping(); this.positionHint = positionHint; this.forger = forger; + this.builtIns = builtIns; + this.messager = messager; } private List filterPossibleCandidateMethods(List candidateMethods) { @@ -182,14 +197,16 @@ private List filterPossibleCandidateMethods(List candid private Assignment getTargetAssignment(Type sourceType, Type targetType) { - Assignment referencedMethod; + Assignment assignment; // first simple mapping method if ( allowMappingMethod() ) { - referencedMethod = resolveViaMethod( sourceType, targetType, false ); - if ( referencedMethod != null ) { - referencedMethod.setAssignment( sourceRHS ); - return referencedMethod; + List> matches = getBestMatch( methods, sourceType, targetType ); + reportErrorWhenAmbigious( matches, targetType ); + if ( !matches.isEmpty() ) { + assignment = toMethodRef( first( matches ) ); + assignment.setAssignment( sourceRHS ); + return assignment; } } @@ -228,38 +245,40 @@ && allowDirect( sourceType, targetType ) ) { // check for a built-in method if ( !hasQualfiers() ) { - Assignment builtInMethod = resolveViaBuiltInMethod( sourceType, targetType ); - if ( builtInMethod != null ) { - builtInMethod.setAssignment( sourceRHS ); + List> matches = getBestMatch( builtIns, sourceType, targetType ); + reportErrorWhenAmbigious( matches, targetType ); + if ( !matches.isEmpty() ) { + assignment = toBuildInRef( first( matches ) ); + assignment.setAssignment( sourceRHS ); usedSupportedMappings.addAll( supportingMethodCandidates ); - return builtInMethod; + return assignment; } } } if ( allow2Steps() ) { // 2 step method, first: method(method(source)) - referencedMethod = resolveViaMethodAndMethod( sourceType, targetType ); - if ( referencedMethod != null ) { + assignment = MethodMethod.getBestMatch( this, sourceType, targetType ); + if ( assignment != null ) { usedSupportedMappings.addAll( supportingMethodCandidates ); - return referencedMethod; + return assignment; } // 2 step method, then: method(conversion(source)) - referencedMethod = resolveViaConversionAndMethod( sourceType, targetType ); - if ( referencedMethod != null ) { + assignment = ConversionMethod.getBestMatch( this, sourceType, targetType ); + if ( assignment != null ) { usedSupportedMappings.addAll( supportingMethodCandidates ); - return referencedMethod; + return assignment; } // stop here when looking for update methods. selectionCriteria.setPreferUpdateMapping( false ); // 2 step method, finally: conversion(method(source)) - ConversionAssignment conversion = resolveViaMethodAndConversion( sourceType, targetType ); - if ( conversion != null ) { + assignment = MethodConversion.getBestMatch( this, sourceType, targetType ); + if ( assignment != null ) { usedSupportedMappings.addAll( supportingMethodCandidates ); - return conversion.getAssignment(); + return assignment; } } @@ -395,232 +414,6 @@ private ConversionAssignment resolveViaConversion(Type sourceType, Type targetTy return null; } - /** - * Returns a reference to a method mapping the given source type to the given target type, if such a method - * exists. - */ - private Assignment resolveViaMethod(Type sourceType, Type targetType, boolean considerBuiltInMethods) { - - // first try to find a matching source method - SelectedMethod matchingSourceMethod = getBestMatch( methods, sourceType, targetType ); - - if ( matchingSourceMethod != null ) { - return getMappingMethodReference( matchingSourceMethod ); - } - - if ( considerBuiltInMethods ) { - return resolveViaBuiltInMethod( sourceType, targetType ); - } - - return null; - } - - /** - * Applies matching to given method only - * - * @param sourceType the source type - * @param targetType the target type - * @param method the method to match - * @return an assignment if a match, given the criteria could be found. When the method is a - * buildIn method, all the bookkeeping is applied. - */ - private Assignment applyMatching(Type sourceType, Type targetType, Method method) { - - if ( method instanceof SourceMethod ) { - SelectedMethod selectedMethod = - getBestMatch( java.util.Collections.singletonList( method ), sourceType, targetType ); - return selectedMethod != null ? getMappingMethodReference( selectedMethod ) : null; - } - else if ( method instanceof BuiltInMethod ) { - return resolveViaBuiltInMethod( sourceType, targetType ); - } - return null; - } - - private Assignment resolveViaBuiltInMethod(Type sourceType, Type targetType) { - SelectedMethod matchingBuiltInMethod = - getBestMatch( builtInMethods.getBuiltInMethods(), sourceType, targetType ); - - if ( matchingBuiltInMethod != null ) { - return createFromBuiltInMethod( matchingBuiltInMethod.getMethod() ); - } - - return null; - } - - private Assignment createFromBuiltInMethod(BuiltInMethod method) { - Set allUsedFields = new HashSet<>( mapperReferences ); - SupportingField.addAllFieldsIn( supportingMethodCandidates, allUsedFields ); - SupportingMappingMethod supportingMappingMethod = new SupportingMappingMethod( method, allUsedFields ); - supportingMethodCandidates.add( supportingMappingMethod ); - ConversionContext ctx = new DefaultConversionContext( - typeFactory, - messager, - method.getSourceParameters().get( 0 ).getType(), - method.getResultType(), - formattingParameters - ); - Assignment methodReference = MethodReference.forBuiltInMethod( method, ctx ); - methodReference.setAssignment( sourceRHS ); - return methodReference; - } - - /** - * Suppose mapping required from A to C and: - *
        - *
      • no direct referenced mapping method either built-in or referenced is available from A to C
      • - *
      • no conversion is available
      • - *
      • there is a method from A to B, methodX
      • - *
      • there is a method from B to C, methodY
      • - *
      - * then this method tries to resolve this combination and make a mapping methodY( methodX ( parameter ) ) - */ - private Assignment resolveViaMethodAndMethod(Type sourceType, Type targetType) { - - List methodYCandidates = new ArrayList<>( methods ); - methodYCandidates.addAll( builtInMethods.getBuiltInMethods() ); - - Assignment methodRefY = null; - - // Iterate over all source methods. Check if the return type matches with the parameter that we need. - // so assume we need a method from A to C we look for a methodX from A to B (all methods in the - // list form such a candidate). - // For each of the candidates, we need to look if there's a methodY, either - // sourceMethod or builtIn that fits the signature B to C. Only then there is a match. If we have a match - // a nested method call can be called. so C = methodY( methodX (A) ) - for ( Method methodYCandidate : methodYCandidates ) { - Type ySourceType = methodYCandidate.getSourceParameters().get( 0 ).getType(); - if ( Object.class.getName().equals( ySourceType.getName() ) ) { - // java.lang.Object as intermediate result - continue; - } - - ySourceType = ySourceType.resolveTypeVarToType( targetType, methodYCandidate.getResultType() ); - - if ( ySourceType != null ) { - methodRefY = applyMatching( ySourceType, targetType, methodYCandidate ); - if ( methodRefY != null ) { - - selectionCriteria.setPreferUpdateMapping( false ); - Assignment methodRefX = resolveViaMethod( sourceType, ySourceType, true ); - selectionCriteria.setPreferUpdateMapping( savedPreferUpdateMapping ); - if ( methodRefX != null ) { - methodRefY.setAssignment( methodRefX ); - methodRefX.setAssignment( sourceRHS ); - break; - } - else { - // both should match; - supportingMethodCandidates.clear(); - methodRefY = null; - } - } - } - } - return methodRefY; - } - - /** - * Suppose mapping required from A to C and: - *
        - *
      • there is a conversion from A to B, conversionX
      • - *
      • there is a method from B to C, methodY
      • - *
      - * then this method tries to resolve this combination and make a mapping methodY( conversionX ( parameter ) ) - * - * Instead of directly using a built in method candidate, all the return types as 'B' of all available built-in - * methods are used to resolve a mapping (assignment) from result type to 'B'. If a match is found, an attempt - * is done to find a matching type conversion. - */ - private Assignment resolveViaConversionAndMethod(Type sourceType, Type targetType) { - - List methodYCandidates = new ArrayList<>( methods ); - methodYCandidates.addAll( builtInMethods.getBuiltInMethods() ); - - Assignment methodRefY = null; - - for ( Method methodYCandidate : methodYCandidates ) { - Type ySourceType = methodYCandidate.getSourceParameters().get( 0 ).getType(); - if ( Object.class.getName().equals( ySourceType.getName() ) ) { - // java.lang.Object as intermediate result - continue; - } - - ySourceType = ySourceType.resolveTypeVarToType( targetType, methodYCandidate.getResultType() ); - - if ( ySourceType != null ) { - methodRefY = applyMatching( ySourceType, targetType, methodYCandidate ); - if ( methodRefY != null ) { - ConversionAssignment conversionXRef = resolveViaConversion( sourceType, ySourceType ); - if ( conversionXRef != null ) { - methodRefY.setAssignment( conversionXRef.getAssignment() ); - conversionXRef.getAssignment().setAssignment( sourceRHS ); - conversionXRef.reportMessageWhenNarrowing( messager, this ); - break; - } - else { - // both should match - supportingMethodCandidates.clear(); - methodRefY = null; - } - } - } - } - return methodRefY; - } - - /** - * Suppose mapping required from A to C and: - *
        - *
      • there is a method from A to B, methodX
      • - *
      • there is a conversion from B to C, conversionY
      • - *
      - * then this method tries to resolve this combination and make a mapping conversionY( methodX ( parameter ) ) - * - * Instead of directly using a built in method candidate, all the return types as 'B' of all available built-in - * methods are used to resolve a mapping (assignment) from source type to 'B'. If a match is found, an attempt - * is done to find a matching type conversion. - */ - private ConversionAssignment resolveViaMethodAndConversion(Type sourceType, Type targetType) { - - List methodXCandidates = new ArrayList<>( methods ); - methodXCandidates.addAll( builtInMethods.getBuiltInMethods() ); - - ConversionAssignment conversionYRef = null; - - // search the other way around - for ( Method methodXCandidate : methodXCandidates ) { - Type xTargetType = methodXCandidate.getReturnType(); - if ( methodXCandidate.isUpdateMethod() || - Object.class.getName().equals( xTargetType.getFullyQualifiedName() ) ) { - // skip update methods || java.lang.Object as intermediate result - continue; - } - - xTargetType = - xTargetType.resolveTypeVarToType( sourceType, methodXCandidate.getParameters().get( 0 ).getType() ); - - if ( xTargetType != null ) { - Assignment methodRefX = applyMatching( sourceType, xTargetType, methodXCandidate ); - if ( methodRefX != null ) { - conversionYRef = resolveViaConversion( xTargetType, targetType ); - if ( conversionYRef != null ) { - conversionYRef.getAssignment().setAssignment( methodRefX ); - methodRefX.setAssignment( sourceRHS ); - conversionYRef.reportMessageWhenNarrowing( messager, this ); - break; - } - else { - // both should match; - supportingMethodCandidates.clear(); - conversionYRef = null; - } - } - } - } - return conversionYRef; - } - private boolean isCandidateForMapping(Method methodCandidate) { return isCreateMethodForMapping( methodCandidate ) || isUpdateMethodForMapping( methodCandidate ); } @@ -640,15 +433,17 @@ private boolean isUpdateMethodForMapping(Method methodCandidate) { && !methodCandidate.isLifecycleCallbackMethod(); } - private SelectedMethod getBestMatch(List methods, Type sourceType, Type returnType) { - - List> candidates = methodSelectors.getMatchingMethods( + private List> getBestMatch(List methods, Type source, Type target) { + return methodSelectors.getMatchingMethods( mappingMethod, methods, - singletonList( sourceType ), - returnType, + singletonList( source ), + target, selectionCriteria ); + } + + private void reportErrorWhenAmbigious(List> candidates, Type target) { // raise an error if more than one mapping method is suitable to map the given source type // into the target type @@ -660,7 +455,7 @@ private SelectedMethod getBestMatch(List methods, Type positionHint, Message.GENERAL_AMBIGIOUS_MAPPING_METHOD, sourceRHS.getSourceErrorMessagePart(), - returnType, + target, Strings.join( candidates, ", " ) ); } @@ -669,29 +464,41 @@ private SelectedMethod getBestMatch(List methods, Type mappingMethod.getExecutable(), positionHint, Message.GENERAL_AMBIGIOUS_FACTORY_METHOD, - returnType, + target, Strings.join( candidates, ", " ) ); } } - - if ( !candidates.isEmpty() ) { - return first( candidates ); - } - - return null; } - private Assignment getMappingMethodReference(SelectedMethod method) { - MapperReference mapperReference = findMapperReference( method.getMethod() ); + private Assignment toMethodRef(SelectedMethod selectedMethod) { + MapperReference mapperReference = findMapperReference( selectedMethod.getMethod() ); return MethodReference.forMapperReference( - method.getMethod(), + selectedMethod.getMethod(), mapperReference, - method.getParameterBindings() + selectedMethod.getParameterBindings() ); } + private Assignment toBuildInRef(SelectedMethod selectedMethod) { + BuiltInMethod method = selectedMethod.getMethod(); + Set allUsedFields = new HashSet<>( mapperReferences ); + SupportingField.addAllFieldsIn( supportingMethodCandidates, allUsedFields ); + SupportingMappingMethod supportingMappingMethod = new SupportingMappingMethod( method, allUsedFields ); + supportingMethodCandidates.add( supportingMappingMethod ); + ConversionContext ctx = new DefaultConversionContext( + typeFactory, + messager, + method.getMappingSourceType(), + method.getResultType(), + formattingParameters + ); + Assignment methodReference = MethodReference.forBuiltInMethod( method, ctx ); + methodReference.setAssignment( sourceRHS ); + return methodReference; + } + /** * Whether the given source and target type are both a collection type or both a map type and the source value * can be propagated via a copy constructor. @@ -770,6 +577,7 @@ private boolean hasCompatibleCopyConstructor(Type sourceType, Type targetType) { return false; } + } private static class ConversionAssignment { @@ -813,5 +621,408 @@ private void report(FormattingMessager messager, ResolvingAttempt attempt, Messa ); } + String shortName() { + return sourceType.getName() + "-->" + targetType.getName(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + ConversionAssignment that = (ConversionAssignment) o; + return sourceType.equals( that.sourceType ) && targetType.equals( that.targetType ); + } + + @Override + public int hashCode() { + return Objects.hash( sourceType, targetType ); + } + } + + /** + * Suppose mapping required from A to C and: + *
        + *
      • no direct referenced mapping method either built-in or referenced is available from A to C
      • + *
      • no conversion is available
      • + *
      • there is a method from A to B, methodX
      • + *
      • there is a method from B to C, methodY
      • + *
      + * then this method tries to resolve this combination and make a mapping methodY( methodX ( parameter ) ) + * + * NOTE method X cannot be an update method + */ + private static class MethodMethod { + + private final ResolvingAttempt attempt; + private final List xMethods; + private final List yMethods; + private final Function, Assignment> xCreate; + private final Function, Assignment> yCreate; + + // results + private boolean hasResult = false; + private Assignment result = null; + + static Assignment getBestMatch(ResolvingAttempt att, Type sourceType, Type targetType) { + MethodMethod mmAttempt = + new MethodMethod<>( att, att.methods, att.methods, att::toMethodRef, att::toMethodRef ) + .getBestMatch( sourceType, targetType ); + if ( mmAttempt.hasResult ) { + return mmAttempt.result; + } + MethodMethod mbAttempt = + new MethodMethod<>( att, att.methods, att.builtIns, att::toMethodRef, att::toBuildInRef ) + .getBestMatch( sourceType, targetType ); + if ( mbAttempt.hasResult ) { + return mbAttempt.result; + } + MethodMethod bmAttempt = + new MethodMethod<>( att, att.builtIns, att.methods, att::toBuildInRef, att::toMethodRef ) + .getBestMatch( sourceType, targetType ); + if ( bmAttempt.hasResult ) { + return bmAttempt.result; + } + MethodMethod bbAttempt = + new MethodMethod<>( att, att.builtIns, att.builtIns, att::toBuildInRef, att::toBuildInRef ) + .getBestMatch( sourceType, targetType ); + return bbAttempt.result; + } + + MethodMethod(ResolvingAttempt attempt, List xMethods, List yMethods, + Function, Assignment> xCreate, + Function, Assignment> yCreate) { + this.attempt = attempt; + this.xMethods = xMethods; + this.yMethods = yMethods; + this.xCreate = xCreate; + this.yCreate = yCreate; + } + + private MethodMethod getBestMatch(Type sourceType, Type targetType) { + + Set yCandidates = new HashSet<>(); + Map, List>> xCandidates = new LinkedHashMap<>(); + Map, Type> typesInTheMiddle = new LinkedHashMap<>(); + + // Iterate over all source methods. Check if the return type matches with the parameter that we need. + // so assume we need a method from A to C we look for a methodX from A to B (all methods in the + // list form such a candidate). + // For each of the candidates, we need to look if there's a methodY, either + // sourceMethod or builtIn that fits the signature B to C. Only then there is a match. If we have a match + // a nested method call can be called. so C = methodY( methodX (A) ) + attempt.selectionCriteria.setPreferUpdateMapping( false ); + for ( T2 yCandidate : yMethods ) { + Type ySourceType = yCandidate.getMappingSourceType(); + ySourceType = ySourceType.resolveTypeVarToType( targetType, yCandidate.getResultType() ); + Type yTargetType = yCandidate.getResultType(); + if ( ySourceType == null + || !yTargetType.isRawAssignableTo( targetType ) + || JL_OBJECT_NAME.equals( ySourceType.getFullyQualifiedName() ) ) { + // java.lang.Object as intermediate result + continue; + } + List> xMatches = attempt.getBestMatch( xMethods, sourceType, ySourceType ); + if ( !xMatches.isEmpty() ) { + xMatches.stream().forEach( x -> xCandidates.put( x, new ArrayList<>() ) ); + final Type typeInTheMiddle = ySourceType; + xMatches.stream().forEach( x -> typesInTheMiddle.put( x, typeInTheMiddle ) ); + yCandidates.add( yCandidate ); + } + } + attempt.selectionCriteria.setPreferUpdateMapping( true ); + + // collect all results + List yCandidatesList = new ArrayList<>( yCandidates ); + Iterator, List>>> i = xCandidates.entrySet().iterator(); + while ( i.hasNext() ) { + Map.Entry, List>> entry = i.next(); + Type typeInTheMiddle = typesInTheMiddle.get( entry.getKey() ); + entry.getValue().addAll( attempt.getBestMatch( yCandidatesList, typeInTheMiddle, targetType ) ); + if ( entry.getValue().isEmpty() ) { + i.remove(); + } + } + + // no results left + if ( xCandidates.isEmpty() ) { + return this; + } + hasResult = true; + + // get result, there should be one entry left with only one value + if ( xCandidates.size() == 1 && firstValue( xCandidates ).size() == 1 ) { + Assignment methodRefY = yCreate.apply( first( firstValue( xCandidates ) ) ); + Assignment methodRefX = xCreate.apply( firstKey( xCandidates ) ); + methodRefY.setAssignment( methodRefX ); + methodRefX.setAssignment( attempt.sourceRHS ); + result = methodRefY; + } + else { + reportAmbigiousError( xCandidates, targetType ); + } + return this; + + } + + void reportAmbigiousError(Map, List>> xCandidates, Type target) { + StringBuilder result = new StringBuilder(); + xCandidates.entrySet() + .stream() + .forEach( e -> result.append( "method(s)Y: " ) + .append( e.getValue() + .stream() + .map( v -> v.getMethod().shortName() ) + .collect( Collectors.joining( ", " ) ) ) + .append( ", methodX: " ) + .append( e.getKey().getMethod().shortName() ) + .append( "; " ) ); + attempt.messager.printMessage( + attempt.mappingMethod.getExecutable(), + attempt.positionHint, + Message.GENERAL_AMBIGIOUS_MAPPING_METHODY_METHODX, + attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(), + target.getName(), + result.toString() ); + } + } + + /** + * Suppose mapping required from A to C and: + *
        + *
      • there is a conversion from A to B, conversionX
      • + *
      • there is a method from B to C, methodY
      • + *
      + * then this method tries to resolve this combination and make a mapping methodY( conversionX ( parameter ) ) + * + * Instead of directly using a built in method candidate, all the return types as 'B' of all available built-in + * methods are used to resolve a mapping (assignment) from result type to 'B'. If a match is found, an attempt + * is done to find a matching type conversion. + */ + private static class ConversionMethod { + + private final ResolvingAttempt attempt; + private final List methods; + private final Function, Assignment> create; + + // results + private boolean hasResult = false; + private Assignment result = null; + + static Assignment getBestMatch(ResolvingAttempt att, Type sourceType, Type targetType) { + ConversionMethod mAttempt = new ConversionMethod<>( att, att.methods, att::toMethodRef ) + .getBestMatch( sourceType, targetType ); + if ( mAttempt.hasResult ) { + return mAttempt.result; + } + ConversionMethod bAttempt = + new ConversionMethod<>( att, att.builtIns, att::toBuildInRef ) + .getBestMatch( sourceType, targetType ); + return bAttempt.result; + } + + ConversionMethod(ResolvingAttempt attempt, List methods, Function, Assignment> create) { + this.attempt = attempt; + this.methods = methods; + this.create = create; + } + + private ConversionMethod getBestMatch(Type sourceType, Type targetType) { + + List yCandidates = new ArrayList<>(); + Map>> xRefCandidates = new LinkedHashMap<>(); + + for ( T yCandidate : methods ) { + Type ySourceType = yCandidate.getMappingSourceType(); + ySourceType = ySourceType.resolveTypeVarToType( targetType, yCandidate.getResultType() ); + Type yTargetType = yCandidate.getResultType(); + if ( ySourceType == null + || !yTargetType.isRawAssignableTo( targetType ) + || JL_OBJECT_NAME.equals( ySourceType.getFullyQualifiedName() ) ) { + // java.lang.Object as intermediate result + continue; + } + ConversionAssignment xRefCandidate = attempt.resolveViaConversion( sourceType, ySourceType ); + if ( xRefCandidate != null ) { + xRefCandidates.put( xRefCandidate, new ArrayList<>() ); + yCandidates.add( yCandidate ); + } + } + + // collect all results + Iterator>>> i = xRefCandidates.entrySet().iterator(); + while ( i.hasNext() ) { + Map.Entry>> entry = i.next(); + entry.getValue().addAll( attempt.getBestMatch( yCandidates, entry.getKey().targetType, targetType ) ); + if ( entry.getValue().isEmpty() ) { + i.remove(); + } + } + + // no results left + if ( xRefCandidates.isEmpty() ) { + return this; + } + hasResult = true; + + // get result, there should be one entry left with only one value + if ( xRefCandidates.size() == 1 && firstValue( xRefCandidates ).size() == 1 ) { + Assignment methodRefY = create.apply( first( firstValue( xRefCandidates ) ) ); + ConversionAssignment conversionRefX = firstKey( xRefCandidates ); + conversionRefX.reportMessageWhenNarrowing( attempt.messager, attempt ); + methodRefY.setAssignment( conversionRefX.assignment ); + conversionRefX.assignment.setAssignment( attempt.sourceRHS ); + result = methodRefY; + } + else { + reportAmbigiousError( xRefCandidates, targetType ); + } + return this; + + } + + void reportAmbigiousError(Map>> xRefCandidates, Type target) { + StringBuilder result = new StringBuilder(); + xRefCandidates.entrySet() + .stream() + .forEach( e -> result.append( "method(s)Y: " ) + .append( e.getValue() + .stream() + .map( v -> v.getMethod().shortName() ) + .collect( Collectors.joining( ", " ) ) ) + .append( ", conversionX: " ) + .append( e.getKey().shortName() ) + .append( "; " ) ); + attempt.messager.printMessage( + attempt.mappingMethod.getExecutable(), + attempt.positionHint, + Message.GENERAL_AMBIGIOUS_MAPPING_METHODY_CONVERSIONX, + attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(), + target.getName(), + result.toString() ); + } } + + /** + * Suppose mapping required from A to C and: + *
        + *
      • there is a method from A to B, methodX
      • + *
      • there is a conversion from B to C, conversionY
      • + *
      + * then this method tries to resolve this combination and make a mapping conversionY( methodX ( parameter ) ) + * + * Instead of directly using a built in method candidate, all the return types as 'B' of all available built-in + * methods are used to resolve a mapping (assignment) from source type to 'B'. If a match is found, an attempt + * is done to find a matching type conversion. + * + * NOTE methodX cannot be an update method + */ + private static class MethodConversion { + + private final ResolvingAttempt attempt; + private final List methods; + private final Function, Assignment> create; + + // results + private boolean hasResult = false; + private Assignment result = null; + + static Assignment getBestMatch(ResolvingAttempt att, Type sourceType, Type targetType) { + MethodConversion mAttempt = new MethodConversion<>( att, att.methods, att::toMethodRef ) + .getBestMatch( sourceType, targetType ); + if ( mAttempt.hasResult ) { + return mAttempt.result; + } + MethodConversion bAttempt = new MethodConversion<>( att, att.builtIns, att::toBuildInRef ) + .getBestMatch( sourceType, targetType ); + return bAttempt.result; + } + + MethodConversion(ResolvingAttempt attempt, List methods, Function, Assignment> create) { + this.attempt = attempt; + this.methods = methods; + this.create = create; + } + + private MethodConversion getBestMatch(Type sourceType, Type targetType) { + + List xCandidates = new ArrayList<>(); + Map>> yRefCandidates = new LinkedHashMap<>(); + + // search through methods, and select egible candidates + for ( T xCandidate : methods ) { + Type xTargetType = xCandidate.getReturnType(); + Type xSourceType = xCandidate.getMappingSourceType(); + xTargetType = xTargetType.resolveTypeVarToType( sourceType, xSourceType ); + if ( xTargetType == null + || xCandidate.isUpdateMethod() + || !sourceType.isRawAssignableTo( xSourceType ) + || JL_OBJECT_NAME.equals( xTargetType.getFullyQualifiedName() ) ) { + // skip update methods || java.lang.Object as intermediate result + continue; + } + ConversionAssignment yRefCandidate = attempt.resolveViaConversion( xTargetType, targetType ); + if ( yRefCandidate != null ) { + yRefCandidates.put( yRefCandidate, new ArrayList<>() ); + xCandidates.add( xCandidate ); + } + } + + // collect all results + Iterator>>> i = yRefCandidates.entrySet().iterator(); + while ( i.hasNext() ) { + Map.Entry>> entry = i.next(); + entry.getValue().addAll( attempt.getBestMatch( xCandidates, sourceType, entry.getKey().sourceType ) ); + if ( entry.getValue().isEmpty() ) { + i.remove(); + } + } + + // no results left + if ( yRefCandidates.isEmpty() ) { + return this; + } + hasResult = true; + + // get result, there should be one entry left with only one value + if ( yRefCandidates.size() == 1 && firstValue( yRefCandidates ).size() == 1 ) { + Assignment methodRefX = create.apply( first( firstValue( yRefCandidates ) ) ); + ConversionAssignment conversionRefY = firstKey( yRefCandidates ); + conversionRefY.reportMessageWhenNarrowing( attempt.messager, attempt ); + methodRefX.setAssignment( attempt.sourceRHS ); + conversionRefY.assignment.setAssignment( methodRefX ); + result = conversionRefY.assignment; + } + else { + reportAmbigiousError( yRefCandidates, targetType ); + } + return this; + + } + + void reportAmbigiousError(Map>> yRefCandidates, Type target) { + StringBuilder result = new StringBuilder(); + yRefCandidates.entrySet() + .stream() + .forEach( e -> result.append( "conversionY: " ) + .append( e.getKey().shortName() ) + .append( ", method(s)X: " ) + .append( e.getValue() + .stream() + .map( v -> v.getMethod().shortName() ) + .collect( Collectors.joining( ", " ) ) ) + .append( "; " ) ); + attempt.messager.printMessage( + attempt.mappingMethod.getExecutable(), + attempt.positionHint, + Message.GENERAL_AMBIGIOUS_MAPPING_CONVERSIONY_METHODX, + attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(), + target.getName(), + result.toString() ); + } + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Collections.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Collections.java index 7f2d5cd8db..365efc56ce 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Collections.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Collections.java @@ -9,6 +9,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -63,4 +64,16 @@ public static List join(List a, List b) { return result; } + public static Map.Entry first(Map map) { + return map.entrySet().iterator().next(); + } + + public static V firstValue(Map map) { + return first( map ).getValue(); + } + + public static K firstKey(Map map) { + return first( map ).getKey(); + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index ae66d38776..d10faf64b9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -125,6 +125,10 @@ public enum Message { GENERAL_NO_QUALIFYING_METHOD_NAMED( "Qualifier error. No method found annotated with @Named#value: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ), GENERAL_NO_QUALIFYING_METHOD_COMBINED( "Qualifier error. No method found annotated with @Named#value: [ %s ], annotated with [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ), + GENERAL_AMBIGIOUS_MAPPING_METHODY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( methodX ( parameter ) ): %s." ), + GENERAL_AMBIGIOUS_MAPPING_CONVERSIONY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found conversionY( methodX ( parameter ) ): %s." ), + GENERAL_AMBIGIOUS_MAPPING_METHODY_CONVERSIONX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( conversionX ( parameter ) ): %s." ), + BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD( "More than one builder creation method for \"%s\". Found methods: \"%s\". Builder will not be used. Consider implementing a custom BuilderProvider SPI.", Diagnostic.Kind.WARNING ), BUILDER_NO_BUILD_METHOD_FOUND("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\".", Diagnostic.Kind.ERROR ), BUILDER_NO_BUILD_METHOD_FOUND_DEFAULT("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\". Consider to add @Builder in order to select the correct build method.", Diagnostic.Kind.ERROR ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Mapper.java new file mode 100644 index 0000000000..1b8969664d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Mapper.java @@ -0,0 +1,85 @@ +/* + * 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._2145; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; +import javax.xml.namespace.QName; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper( uses = Issue2145Mapper.ObjectFactory.class ) +public interface Issue2145Mapper { + + Issue2145Mapper INSTANCE = Mappers.getMapper( Issue2145Mapper.class ); + + @Mapping(target = "nested", source = "value") + Target map(Source source); + + default Nested map(String in) { + Nested nested = new Nested(); + nested.setValue( in ); + return nested; + } + + class Target { + + private JAXBElement nested; + + public JAXBElement getNested() { + return nested; + } + + public void setNested(JAXBElement nested) { + this.nested = nested; + } + } + + class Nested { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class Source { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class ObjectFactory { + + private static final QName Q_NAME = new QName( "http://www.test.com/test", "" ); + private static final QName Q_NAME_NESTED = new QName( "http://www.test.com/test", "nested" ); + + @XmlElementDecl(namespace = "http://www.test.com/test", name = "Nested") + public JAXBElement createNested(Nested value) { + return new JAXBElement( Q_NAME, Nested.class, null, value ); + } + + @XmlElementDecl(namespace = "http://www.test.com/test", name = "nested", scope = Nested.class) + public JAXBElement createNestedInNestedTarget(Nested value) { + return new JAXBElement( Q_NAME_NESTED, Nested.class, Target.class, value ); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java new file mode 100644 index 0000000000..02ad2ba901 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java @@ -0,0 +1,34 @@ +/* + * 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._2145; + +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 static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("2145") +@WithClasses(Issue2145Mapper.class) +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2145Test { + + @Test + public void test() { + Issue2145Mapper.Source source = new Issue2145Mapper.Source(); + source.setValue( "test" ); + + Issue2145Mapper.Target target = Issue2145Mapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getNested() ).isNotNull(); + assertThat( target.getNested().getScope() ).isEqualTo( Issue2145Mapper.Target.class ); + assertThat( target.getNested().getValue() ).isNotNull(); + assertThat( target.getNested().getValue().getValue() ).isEqualTo( "test" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperCM.java b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperCM.java new file mode 100644 index 0000000000..6910057682 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperCM.java @@ -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.selection.twosteperror; + +import java.math.BigDecimal; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousMapperCM { + + ErroneousMapperCM INSTANCE = Mappers.getMapper( ErroneousMapperCM.class ); + + Target map(Source s); + + default TargetType methodY1(String s) { + return new TargetType( s ); + } + + default TargetType methodY2(Double d) { + return new TargetType( d.toString() ); + } + + // CHECKSTYLE:OFF + class Target { + public TargetType t1; + } + + class Source { + public BigDecimal t1; + } + + class TargetType { + public String t1; + + public TargetType(String test) { + this.t1 = test; + } + } + + class TypeInTheMiddleA { + + TypeInTheMiddleA(String t1) { + this.test = t1; + } + + public String test; + } + + // CHECKSTYLE:ON + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMC.java b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMC.java new file mode 100644 index 0000000000..4b7dcf90a3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMC.java @@ -0,0 +1,52 @@ +/* + * 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.selection.twosteperror; + +import java.math.BigDecimal; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousMapperMC { + + ErroneousMapperMC INSTANCE = Mappers.getMapper( ErroneousMapperMC.class ); + + Target map(Source s); + + default BigDecimal methodX1(SourceType s) { + return new BigDecimal( s.t1 ); + } + + default Double methodX2(SourceType s) { + return new Double( s.t1 ); + } + + // CHECKSTYLE:OFF + class Target { + public String t1; + } + + class Source { + public SourceType t1; + } + + class SourceType { + public String t1; + } + + class TargetType { + public String t1; + + public TargetType(String test) { + this.t1 = test; + } + } + + + // CHECKSTYLE:ON + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMM.java b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMM.java new file mode 100644 index 0000000000..2f0e25c647 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMM.java @@ -0,0 +1,74 @@ +/* + * 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.selection.twosteperror; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousMapperMM { + + ErroneousMapperMM INSTANCE = Mappers.getMapper( ErroneousMapperMM.class ); + + Target map( Source s ); + + default TargetType methodY1(TypeInTheMiddleA s) { + return new TargetType( s.test ); + } + + default TypeInTheMiddleA methodX1(SourceType s) { + return new TypeInTheMiddleA( s.t1 ); + } + + default TargetType methodY2(TypeInTheMiddleB s) { + return new TargetType( s.test ); + } + + default TypeInTheMiddleB methodX2(SourceType s) { + return new TypeInTheMiddleB( s.t1 ); + } + + // CHECKSTYLE:OFF + class Target { + public TargetType t1; + } + + class Source { + public SourceType t1; + } + + class SourceType { + public String t1; + } + + class TargetType { + public String t1; + + public TargetType(String test) { + this.t1 = test; + } + } + + class TypeInTheMiddleA { + + TypeInTheMiddleA(String t1) { + this.test = t1; + } + + public String test; + } + + class TypeInTheMiddleB { + + TypeInTheMiddleB(String t1) { + this.test = t1; + } + + public String test; + } + // CHECKSTYLE:ON + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java new file mode 100644 index 0000000000..e22150fe36 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java @@ -0,0 +1,79 @@ +/* + * 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.selection.twosteperror; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +@RunWith( AnnotationProcessorTestRunner.class ) +public class TwoStepMappingTest { + + @Test + @WithClasses( ErroneousMapperMM.class ) + @ExpectedCompilationOutcome( value = CompilationResult.FAILED, + diagnostics = @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperMM.class, + line = 16, + message = "Ambiguous 2step methods found, mapping SourceType s to TargetType. " + + "Found methodY( methodX ( parameter ) ): " + + "method(s)Y: TargetType:methodY1(TypeInTheMiddleA), methodX: TypeInTheMiddleA:methodX1(SourceType); " + + "method(s)Y: TargetType:methodY2(TypeInTheMiddleB), methodX: TypeInTheMiddleB:methodX2(SourceType); ." + ) ) + public void methodAndMethodTest() { + } + + @Test + @WithClasses( ErroneousMapperCM.class ) + @ExpectedCompilationOutcome( value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperCM.class, + line = 18, + message = "Ambiguous 2step methods found, mapping BigDecimal s to TargetType. " + + "Found methodY( conversionX ( parameter ) ): " + + "method(s)Y: TargetType:methodY1(String), conversionX: BigDecimal-->String; " + + "method(s)Y: TargetType:methodY2(Double), conversionX: BigDecimal-->Double; ." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperCM.class, + line = 18, + messageRegExp = "Can't map property.*" + ) + } ) + public void conversionAndMethodTest() { + } + + @Test + @WithClasses( ErroneousMapperMC.class ) + @ExpectedCompilationOutcome( value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperMC.class, + line = 18, + message = "Ambiguous 2step methods found, mapping SourceType s to String. " + + "Found conversionY( methodX ( parameter ) ): " + + "conversionY: BigDecimal-->String, method(s)X: BigDecimal:methodX1(SourceType); " + + "conversionY: Double-->String, method(s)X: Double:methodX2(SourceType); ." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperMC.class, + line = 18, + messageRegExp = "Can't map property.*" + ) + } ) + public void methodAndConversionTest() { + } +} From 6aa39ff4283b8fff5373bd292d69e1cb1da5cf0f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 17 Jul 2020 22:10:26 +0200 Subject: [PATCH 076/597] #2142: Strip leading underscore when sanitizing identifier name --- .../ap/internal/model/ForgedMethod.java | 3 +- .../mapstruct/ap/internal/util/Strings.java | 19 +++++++- .../ap/internal/util/StringsTest.java | 10 ++++ .../ap/test/bugs/_2142/Issue2142Mapper.java | 46 +++++++++++++++++++ .../ap/test/bugs/_2142/Issue2142Test.java | 34 ++++++++++++++ .../test/bugs/_2142/Issue2142MapperImpl.java | 31 +++++++++++++ 6 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Test.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2142/Issue2142MapperImpl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java index e7a64e9138..ce1d8c024d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java @@ -125,8 +125,7 @@ private ForgedMethod(String name, Type sourceType, Type returnType, List( 1 + additionalParameters.size() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java index 71acee3cf4..07049c17ad 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java @@ -73,6 +73,8 @@ public class Strings { "while" ); + private static final char UNDERSCORE = '_'; + private Strings() { } @@ -166,7 +168,22 @@ public static String getSafeVariableName(String name, Collection existin * @return the identifier without any characters that are not allowed as part of a Java identifier. */ public static String sanitizeIdentifierName(String identifier) { - return identifier.replace( "[]", "Array" ); + if ( identifier != null && identifier.length() > 0 ) { + + int firstNonUnderScoreIndex = 0; + while ( firstNonUnderScoreIndex < identifier.length() && + identifier.charAt( firstNonUnderScoreIndex ) == UNDERSCORE ) { + firstNonUnderScoreIndex++; + } + + if ( firstNonUnderScoreIndex < identifier.length()) { + // If it is not consisted of only underscores + return identifier.substring( firstNonUnderScoreIndex ).replace( "[]", "Array" ); + } + + return identifier.replace( "[]", "Array" ); + } + return identifier; } /** diff --git a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java index ced598330d..e0eb961d10 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java @@ -83,6 +83,8 @@ public void testGetSaveVariableNameWithArrayExistingVariables() { assertThat( Strings.getSafeVariableName( "Case" ) ).isEqualTo( "case1" ); assertThat( Strings.getSafeVariableName( "Synchronized" ) ).isEqualTo( "synchronized1" ); assertThat( Strings.getSafeVariableName( "prop", "prop", "prop_" ) ).isEqualTo( "prop1" ); + assertThat( Strings.getSafeVariableName( "_Test" ) ).isEqualTo( "test" ); + assertThat( Strings.getSafeVariableName( "__Test" ) ).isEqualTo( "test" ); } @Test @@ -98,12 +100,20 @@ public void testGetSaveVariableNameWithCollection() { assertThat( Strings.getSafeVariableName( "prop", Arrays.asList( "prop", "prop1" ) ) ).isEqualTo( "prop2" ); assertThat( Strings.getSafeVariableName( "prop.font", Arrays.asList( "propFont", "propFont_" ) ) ) .isEqualTo( "propFont1" ); + assertThat( Strings.getSafeVariableName( "_Test", new ArrayList<>() ) ).isEqualTo( "test" ); + assertThat( Strings.getSafeVariableName( "__Test", Arrays.asList( "test" ) ) ).isEqualTo( "test1" ); + assertThat( Strings.getSafeVariableName( "___", new ArrayList<>() ) ).isEqualTo( "___" ); } @Test public void testSanitizeIdentifierName() { assertThat( Strings.sanitizeIdentifierName( "test" ) ).isEqualTo( "test" ); assertThat( Strings.sanitizeIdentifierName( "int[]" ) ).isEqualTo( "intArray" ); + assertThat( Strings.sanitizeIdentifierName( "_Test" ) ).isEqualTo( "Test" ); + assertThat( Strings.sanitizeIdentifierName( "_int[]" ) ).isEqualTo( "intArray" ); + assertThat( Strings.sanitizeIdentifierName( "__int[]" ) ).isEqualTo( "intArray" ); + assertThat( Strings.sanitizeIdentifierName( "test_" ) ).isEqualTo( "test_" ); + assertThat( Strings.sanitizeIdentifierName( "___" ) ).isEqualTo( "___" ); } @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Mapper.java new file mode 100644 index 0000000000..198976b37e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Mapper.java @@ -0,0 +1,46 @@ +/* + * 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._2142; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2142Mapper { + + Issue2142Mapper INSTANCE = Mappers.getMapper( Issue2142Mapper.class ); + + _Target map(Source source); + + // CHECKSTYLE:OFF + class _Target { + // CHECKSTYLE:ON + private final String value; + + public _Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Test.java new file mode 100644 index 0000000000..8cfff232e5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Test.java @@ -0,0 +1,34 @@ +/* + * 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._2142; + +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; + +@IssueKey("2142") +@WithClasses(Issue2142Mapper.class) +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2142Test { + + @Rule + public final GeneratedSource generatedSource = new GeneratedSource() + .addComparisonToFixtureFor( Issue2142Mapper.class ); + + @Test + public void underscorePrefixShouldBeStrippedFromGeneratedLocalVariables() { + Issue2142Mapper._Target target = Issue2142Mapper.INSTANCE.map( new Issue2142Mapper.Source( "value1" ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "value1" ); + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2142/Issue2142MapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2142/Issue2142MapperImpl.java new file mode 100644 index 0000000000..8a6d7d1b70 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2142/Issue2142MapperImpl.java @@ -0,0 +1,31 @@ +/* + * 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._2142; + +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2019-02-10T09:58:11+0100", + comments = "version: , compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)" +) +public class Issue2142MapperImpl implements Issue2142Mapper { + + @Override + public _Target map(Source source) { + if ( source == null ) { + return null; + } + + String value = null; + + value = source.getValue(); + + _Target target = new _Target( value ); + + return target; + } +} From cb432fa61b76a8f81bac05ab120b342a007e8536 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 18 Jul 2020 18:53:32 +0200 Subject: [PATCH 077/597] #2150 Change the rules for how a constructor for mapping is picked New rules: 1. Constructor annotated with @Default (from any package) has highest precedence 2. If there is a single public constructor then it would be used to construct the object 3. If a parameterless constructor exists then it would be used to construct the object, and the other constructors will be ignored --- .../chapter-3-defining-a-mapper.asciidoc | 48 +++++++++- .../ap/internal/model/BeanMappingMethod.java | 88 +++++++++++++------ .../visibility/ConstructorVisibilityTest.java | 79 +++++++++++++++++ .../SimpleWithPublicConstructorMapper.java | 48 ++++++++++ ...sConstructorAndDefaultAnnotatedMapper.java | 51 +++++++++++ ...hPublicParameterlessConstructorMapper.java | 49 +++++++++++ 6 files changed, 334 insertions(+), 29 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicParameterlessConstructorMapper.java diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index ad5d8111f7..7d2dd052f6 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -537,8 +537,52 @@ When doing a mapping MapStruct checks if there is a builder for the type being m If there is no builder, then MapStruct looks for a single accessible constructor. When there are multiple constructors then the following is done to pick the one which should be used: -* If a parameterless constructor exists then it would be used to construct the object, and the other constructors will be ignored -* If there are multiple constructors then the one annotated with annotation named `@Default` (from any package) will be used +* If a constructor is annotated with an annotation named `@Default` (from any package) it will be used. +* If a single public constructor exists then it will be used to construct the object, and the other non public constructors will be ignored. +* If a parameterless constructor exists then it will be used to construct the object, and the other constructors will be ignored. +* If there are multiple eligible constructors then there will be a compilation error due to ambigious constructors. In order to break the ambiquity an annotation named `@Default` (from any package) can used. + +.Deciding which constructor to use +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public class Vehicle { + + protected Vehicle() { } + + // MapStruct will use this constructor, because it is a single public constructor + public Vehicle(String color) { } +} + +public class Car { + + // MapStruct will use this constructor, because it is a parameterless empty constructor + public Car() { } + + public Car(String make, String color) { } +} + +public class Truck { + + public Truck() { } + + // MapStruct will use this constructor, because it is annotated with @Default + @Default + public Truck(String make, String color) { } +} + +public class Van { + + // There will be a compilation error when using this class because MapStruct cannot pick a constructor + + public Van(String make) { } + + public Van(String make, String color) { } + +} +---- +==== When using a constructor then the names of the parameters of the constructor will be used and matched to the target properties. When the constructor has an annotation named `@ConstructorProperties` (from any package) then this annotation will be used to get the names of the parameters. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 1be610842a..dc5d6ac41e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -603,23 +603,57 @@ private ConstructorAccessor getConstructorAccessor(Type type) { List constructors = ElementFilter.constructorsIn( type.getTypeElement() .getEnclosedElements() ); - ExecutableElement defaultConstructor = null; + // The rules for picking a constructor are the following: + // 1. Constructor annotated with @Default (from any package) has highest precedence + // 2. If there is a single public constructor then it would be used to construct the object + // 3. If a parameterless constructor exists then it would be used to construct the object, and the other + // constructors will be ignored + ExecutableElement defaultAnnotatedConstructor = null; + ExecutableElement parameterLessConstructor = null; List accessibleConstructors = new ArrayList<>( constructors.size() ); + List publicConstructors = new ArrayList<>( ); for ( ExecutableElement constructor : constructors ) { if ( constructor.getModifiers().contains( Modifier.PRIVATE ) ) { continue; } + if ( hasDefaultAnnotationFromAnyPackage( constructor ) ) { + // We found a constructor annotated with @Default everything else is irrelevant + defaultAnnotatedConstructor = constructor; + break; + } + if ( constructor.getParameters().isEmpty() ) { - defaultConstructor = constructor; + parameterLessConstructor = constructor; } else { accessibleConstructors.add( constructor ); } + + if ( constructor.getModifiers().contains( Modifier.PUBLIC ) ) { + publicConstructors.add( constructor ); + } + } + + if ( defaultAnnotatedConstructor != null ) { + // If a default annotated constructor exists it will be used, it has highest precedence + return getConstructorAccessor( type, defaultAnnotatedConstructor ); + } + + if ( publicConstructors.size() == 1 ) { + // If there is a single public constructor then use that one + ExecutableElement publicConstructor = publicConstructors.get( 0 ); + if ( publicConstructor.getParameters().isEmpty() ) { + // The public parameterless constructor + return null; + } + + return getConstructorAccessor( type, publicConstructor ); } - if ( defaultConstructor != null ) { + if ( parameterLessConstructor != null ) { + // If there is a constructor without parameters use it return null; } @@ -627,39 +661,26 @@ private ConstructorAccessor getConstructorAccessor(Type type) { return null; } - ExecutableElement constructor = null; if ( accessibleConstructors.size() > 1 ) { - for ( ExecutableElement accessibleConstructor : accessibleConstructors ) { - for ( AnnotationMirror annotationMirror : accessibleConstructor.getAnnotationMirrors() ) { - if ( annotationMirror.getAnnotationType() - .asElement() - .getSimpleName() - .contentEquals( "Default" ) ) { - constructor = accessibleConstructor; - break; - } - } - } - - if ( constructor == null ) { - ctx.getMessager().printMessage( - method.getExecutable(), - GENERAL_AMBIGIOUS_CONSTRUCTORS, - type, - Strings.join( constructors, ", " ) - ); - return null; - } + ctx.getMessager().printMessage( + method.getExecutable(), + GENERAL_AMBIGIOUS_CONSTRUCTORS, + type, + Strings.join( constructors, ", " ) + ); + return null; } else { - constructor = accessibleConstructors.get( 0 ); + return getConstructorAccessor( type, accessibleConstructors.get( 0 ) ); } + } + + private ConstructorAccessor getConstructorAccessor(Type type, ExecutableElement constructor) { List constructorParameters = ctx.getTypeFactory() .getParameters( (DeclaredType) type.getTypeMirror(), constructor ); - List constructorProperties = null; for ( AnnotationMirror annotationMirror : constructor.getAnnotationMirrors() ) { if ( annotationMirror.getAnnotationType() @@ -736,6 +757,19 @@ private Accessor createConstructorAccessor(Element element, String parameterName return new ParameterElementAccessor( element, safeParameterName ); } + private boolean hasDefaultAnnotationFromAnyPackage(Element element) { + for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { + if ( annotationMirror.getAnnotationType() + .asElement() + .getSimpleName() + .contentEquals( "Default" ) ) { + return true; + } + } + + return false; + } + private List getArrayValues(AnnotationValue av) { if ( av.getValue() instanceof List ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java new file mode 100644 index 0000000000..b40dcbf17c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java @@ -0,0 +1,79 @@ +/* + * 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.constructor.visibility; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.Default; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2150") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + PersonDto.class, + Default.class, +}) +public class ConstructorVisibilityTest { + + @Test + @WithClasses({ + SimpleWithPublicConstructorMapper.class + }) + public void shouldUseSinglePublicConstructorAlways() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + + SimpleWithPublicConstructorMapper.Person target = + SimpleWithPublicConstructorMapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 30 ); + } + + @Test + @WithClasses({ + SimpleWithPublicParameterlessConstructorMapper.class + }) + public void shouldUsePublicParameterConstructorIfPresent() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + + SimpleWithPublicParameterlessConstructorMapper.Person target = + SimpleWithPublicParameterlessConstructorMapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getName() ).isEqualTo( "From Constructor" ); + assertThat( target.getAge() ).isEqualTo( -1 ); + } + + @Test + @WithClasses({ + SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper.class + }) + public void shouldUseDefaultAnnotatedConstructorAlways() { + PersonDto source = new PersonDto(); + source.setName( "Bob" ); + source.setAge( 30 ); + + SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper.Person target = + SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getName() ).isEqualTo( "Bob" ); + assertThat( target.getAge() ).isEqualTo( 30 ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicConstructorMapper.java new file mode 100644 index 0000000000..ce0a98aa34 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicConstructorMapper.java @@ -0,0 +1,48 @@ +/* + * 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.constructor.visibility; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SimpleWithPublicConstructorMapper { + + SimpleWithPublicConstructorMapper INSTANCE = Mappers.getMapper( SimpleWithPublicConstructorMapper.class ); + + Person map(PersonDto dto); + + class Person { + + private final String name; + private final int age; + + protected Person() { + this( "From Constructor", -1 ); + } + + protected Person(String name) { + this( name, -1 ); + } + + public Person(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper.java new file mode 100644 index 0000000000..923821681a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper.java @@ -0,0 +1,51 @@ +/* + * 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.constructor.visibility; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.constructor.Default; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper { + + SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper INSTANCE = Mappers.getMapper( + SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper.class ); + + Person map(PersonDto dto); + + class Person { + + private final String name; + private final int age; + + protected Person() { + this( "From Constructor", -1 ); + } + + protected Person(String name) { + this( name, -1 ); + } + + @Default + public Person(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicParameterlessConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicParameterlessConstructorMapper.java new file mode 100644 index 0000000000..c0ba14bc42 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/SimpleWithPublicParameterlessConstructorMapper.java @@ -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.constructor.visibility; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SimpleWithPublicParameterlessConstructorMapper { + + SimpleWithPublicParameterlessConstructorMapper INSTANCE = Mappers.getMapper( + SimpleWithPublicParameterlessConstructorMapper.class ); + + Person map(PersonDto dto); + + class Person { + + private final String name; + private final int age; + + public Person() { + this( "From Constructor", -1 ); + } + + protected Person(String name) { + this( name, -1 ); + } + + public Person(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + } +} From 36349c49e9e545c2d9f55bfc02e457fe1a596433 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sun, 19 Jul 2020 15:40:30 +0200 Subject: [PATCH 078/597] #2156 ambiguous mapping message: location and limit # candidates (#2162) --- .../model/AbstractMappingMethodBuilder.java | 11 ++- .../ap/internal/model/BeanMappingMethod.java | 6 +- .../model/ContainerMappingMethodBuilder.java | 1 + .../ap/internal/model/MapMappingMethod.java | 2 + .../internal/model/MappingBuilderContext.java | 3 +- .../model/ObjectFactoryMethodResolver.java | 2 +- .../ap/internal/model/PropertyMapping.java | 2 + .../creation/MappingResolverImpl.java | 82 ++++++++++++------- .../mapstruct/ap/internal/util/Message.java | 13 +-- .../ap/internal/util/MessageConstants.java | 1 + .../ap/test/bugs/_1242/Issue1242Test.java | 3 +- .../AmbiguousAnnotatedFactoryTest.java | 3 +- .../ambiguousfactorymethod/FactoryTest.java | 4 +- .../ambiguousmapping/AmbigiousMapperTest.java | 67 +++++++++++++++ .../ErroneousWithAmbiguousMethodsMapper.java | 52 ++++++++++++ ...ithMoreThanFiveAmbiguousMethodsMapper.java | 74 +++++++++++++++++ .../complex/ComplexInheritanceTest.java | 13 ++- .../resulttype/InheritanceSelectionTest.java | 4 +- 18 files changed, 287 insertions(+), 56 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithAmbiguousMethodsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithMoreThanFiveAmbiguousMethodsMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index 019f941741..91b8427630 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -27,6 +27,8 @@ public AbstractMappingMethodBuilder(Class selfType) { public abstract M build(); + private ForgedMethodHistory description; + /** * @return {@code true} if property names should be used for the creation of the {@link ForgedMethodHistory}. */ @@ -44,7 +46,7 @@ Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) { history = ( (ForgedMethod) method ).getHistory(); } - ForgedMethodHistory forgedHistory = new ForgedMethodHistory( + description = new ForgedMethodHistory( history, Strings.stubPropertyName( sourceRHS.getSourceType().getName() ), Strings.stubPropertyName( targetType.getName() ), @@ -53,7 +55,7 @@ Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) { shouldUsePropertyNamesInHistory(), sourceRHS.getSourceErrorMessagePart() ); - ForgedMethod forgedMethod = forElementMapping( name, sourceType, targetType, method, forgedHistory, true ); + ForgedMethod forgedMethod = forElementMapping( name, sourceType, targetType, method, description, true ); BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); return createForgedAssignment( @@ -77,4 +79,9 @@ private String getName(Type type) { builder.append( type.getIdentification() ); return builder.toString(); } + + public ForgedMethodHistory getDescription() { + return description; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index dc5d6ac41e..a82ee1a091 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -62,7 +62,7 @@ import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_ABSTRACT; import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_NOT_ASSIGNABLE; import static org.mapstruct.ap.internal.util.Message.GENERAL_ABSTRACT_RETURN_TYPE; -import static org.mapstruct.ap.internal.util.Message.GENERAL_AMBIGIOUS_CONSTRUCTORS; +import static org.mapstruct.ap.internal.util.Message.GENERAL_AMBIGUOUS_CONSTRUCTORS; import static org.mapstruct.ap.internal.util.Message.GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS; /** @@ -569,7 +569,7 @@ else if ( matchingFactoryMethods.size() == 1 ) { else { ctx.getMessager().printMessage( method.getExecutable(), - Message.GENERAL_AMBIGIOUS_FACTORY_METHOD, + Message.GENERAL_AMBIGUOUS_FACTORY_METHOD, returnTypeImpl, Strings.join( matchingFactoryMethods, ", " ) ); @@ -665,7 +665,7 @@ private ConstructorAccessor getConstructorAccessor(Type type) { ctx.getMessager().printMessage( method.getExecutable(), - GENERAL_AMBIGIOUS_CONSTRUCTORS, + GENERAL_AMBIGUOUS_CONSTRUCTORS, type, Strings.join( constructors, ", " ) ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java index 67c03fd3de..79113ce61a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java @@ -83,6 +83,7 @@ public final M build() { ); Assignment assignment = ctx.getMappingResolver().getTargetAssignment( method, + getDescription(), targetElementType, formattingParameters, criteria, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java index 0fbe6f9c00..415f9b6c33 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java @@ -96,6 +96,7 @@ public MapMappingMethod build() { Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment( method, + getDescription(), keyTargetType, keyFormattingParameters, keyCriteria, @@ -142,6 +143,7 @@ public MapMappingMethod build() { Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment( method, + getDescription(), valueTargetType, valueFormattingParameters, valueCriteria, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index 53317aefa0..f5c5887d54 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -78,6 +78,7 @@ public interface MappingResolver { * returns a parameter assignment * * @param mappingMethod target mapping method + * @param description * @param targetType return type to match * @param formattingParameters used for formatting dates and numbers * @param criteria parameters criteria in the selection process @@ -92,7 +93,7 @@ public interface MappingResolver { *
    • null, no assignment found
    • * */ - Assignment getTargetAssignment(Method mappingMethod, Type targetType, + Assignment getTargetAssignment(Method mappingMethod, ForgedMethodHistory description, Type targetType, FormattingParameters formattingParameters, SelectionCriteria criteria, SourceRHS sourceRHS, AnnotationMirror positionHint, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index 34d2944276..34fba406ef 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -81,7 +81,7 @@ public static MethodReference getFactoryMethod( Method method, if ( matchingFactoryMethods.size() > 1 ) { ctx.getMessager().printMessage( method.getExecutable(), - Message.GENERAL_AMBIGIOUS_FACTORY_METHOD, + Message.GENERAL_AMBIGUOUS_FACTORY_METHOD, alternativeTarget, Strings.join( matchingFactoryMethods, ", " ) ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 571c3530f1..4e0050e561 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -242,6 +242,7 @@ public PropertyMapping build() { if ( forgeMethodWithMappingReferences == null ) { assignment = ctx.getMappingResolver().getTargetAssignment( method, + getForgedMethodHistory( rightHandSide ), targetType, formattingParameters, criteria, @@ -791,6 +792,7 @@ public PropertyMapping build() { if ( !targetType.isEnumType() ) { assignment = ctx.getMappingResolver().getTargetAssignment( method, + null, // TODO description for constant targetType, formattingParameters, criteria, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 5edd792944..8fa20f079d 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -39,6 +39,7 @@ import org.mapstruct.ap.internal.conversion.Conversions; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.model.Field; +import org.mapstruct.ap.internal.model.ForgedMethodHistory; import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.MapperReference; import org.mapstruct.ap.internal.model.MappingBuilderContext.MappingResolver; @@ -74,6 +75,8 @@ */ public class MappingResolverImpl implements MappingResolver { + private static final int MAX_REPORTING_AMBIGUOUS = 5; + private final FormattingMessager messager; private final Types typeUtils; private final TypeFactory typeFactory; @@ -109,7 +112,7 @@ public MappingResolverImpl(FormattingMessager messager, Elements elementUtils, T } @Override - public Assignment getTargetAssignment(Method mappingMethod, Type targetType, + public Assignment getTargetAssignment(Method mappingMethod, ForgedMethodHistory description, Type targetType, FormattingParameters formattingParameters, SelectionCriteria criteria, SourceRHS sourceRHS, AnnotationMirror positionHint, @@ -118,6 +121,7 @@ public Assignment getTargetAssignment(Method mappingMethod, Type targetType, ResolvingAttempt attempt = new ResolvingAttempt( sourceModel, mappingMethod, + description, formattingParameters, sourceRHS, criteria, @@ -149,6 +153,7 @@ private MapperReference findMapperReference(Method method) { private class ResolvingAttempt { private final Method mappingMethod; + private final ForgedMethodHistory description; private final List methods; private final SelectionCriteria selectionCriteria; private final SourceRHS sourceRHS; @@ -163,7 +168,7 @@ private class ResolvingAttempt { // so this set must be cleared. private final Set supportingMethodCandidates; - private ResolvingAttempt(List sourceModel, Method mappingMethod, + private ResolvingAttempt(List sourceModel, Method mappingMethod, ForgedMethodHistory description, FormattingParameters formattingParameters, SourceRHS sourceRHS, SelectionCriteria criteria, AnnotationMirror positionHint, @@ -172,6 +177,7 @@ private ResolvingAttempt(List sourceModel, Method mappingMethod, FormattingMessager messager) { this.mappingMethod = mappingMethod; + this.description = description; this.methods = filterPossibleCandidateMethods( sourceModel ); this.formattingParameters = formattingParameters == null ? FormattingParameters.EMPTY : formattingParameters; @@ -202,7 +208,7 @@ private Assignment getTargetAssignment(Type sourceType, Type targetType) { // first simple mapping method if ( allowMappingMethod() ) { List> matches = getBestMatch( methods, sourceType, targetType ); - reportErrorWhenAmbigious( matches, targetType ); + reportErrorWhenAmbiguous( matches, targetType ); if ( !matches.isEmpty() ) { assignment = toMethodRef( first( matches ) ); assignment.setAssignment( sourceRHS ); @@ -246,7 +252,7 @@ && allowDirect( sourceType, targetType ) ) { // check for a built-in method if ( !hasQualfiers() ) { List> matches = getBestMatch( builtIns, sourceType, targetType ); - reportErrorWhenAmbigious( matches, targetType ); + reportErrorWhenAmbiguous( matches, targetType ); if ( !matches.isEmpty() ) { assignment = toBuildInRef( first( matches ) ); assignment.setAssignment( sourceRHS ); @@ -443,29 +449,37 @@ private List> getBestMatch(List methods, ); } - private void reportErrorWhenAmbigious(List> candidates, Type target) { + private void reportErrorWhenAmbiguous(List> candidates, Type target) { // raise an error if more than one mapping method is suitable to map the given source type // into the target type if ( candidates.size() > 1 ) { + String descriptionStr = ""; + if ( description != null ) { + descriptionStr = description.createSourcePropertyErrorMessage(); + } + else { + descriptionStr = sourceRHS.getSourceErrorMessagePart(); + } + if ( sourceRHS.getSourceErrorMessagePart() != null ) { messager.printMessage( mappingMethod.getExecutable(), positionHint, - Message.GENERAL_AMBIGIOUS_MAPPING_METHOD, - sourceRHS.getSourceErrorMessagePart(), + Message.GENERAL_AMBIGUOUS_MAPPING_METHOD, + descriptionStr, target, - Strings.join( candidates, ", " ) + join( candidates ) ); } else { messager.printMessage( mappingMethod.getExecutable(), positionHint, - Message.GENERAL_AMBIGIOUS_FACTORY_METHOD, + Message.GENERAL_AMBIGUOUS_FACTORY_METHOD, target, - Strings.join( candidates, ", " ) + join( candidates ) ); } } @@ -578,6 +592,18 @@ private boolean hasCompatibleCopyConstructor(Type sourceType, Type targetType) { return false; } + private String join( List> candidates ) { + + String candidateStr = candidates.stream() + .limit( MAX_REPORTING_AMBIGUOUS ) + .map( m -> m.getMethod().shortName() ) + .collect( Collectors.joining( ", " ) ); + + if ( candidates.size() > MAX_REPORTING_AMBIGUOUS ) { + candidateStr += String.format( "... and %s more", candidates.size() - MAX_REPORTING_AMBIGUOUS ); + } + return candidateStr; + } } private static class ConversionAssignment { @@ -762,28 +788,26 @@ private MethodMethod getBestMatch(Type sourceType, Type targetType) { result = methodRefY; } else { - reportAmbigiousError( xCandidates, targetType ); + reportAmbiguousError( xCandidates, targetType ); } return this; } - void reportAmbigiousError(Map, List>> xCandidates, Type target) { + void reportAmbiguousError(Map, List>> xCandidates, Type target) { StringBuilder result = new StringBuilder(); xCandidates.entrySet() .stream() + .limit( MAX_REPORTING_AMBIGUOUS ) .forEach( e -> result.append( "method(s)Y: " ) - .append( e.getValue() - .stream() - .map( v -> v.getMethod().shortName() ) - .collect( Collectors.joining( ", " ) ) ) + .append( attempt.join( e.getValue() ) ) .append( ", methodX: " ) .append( e.getKey().getMethod().shortName() ) .append( "; " ) ); attempt.messager.printMessage( attempt.mappingMethod.getExecutable(), attempt.positionHint, - Message.GENERAL_AMBIGIOUS_MAPPING_METHODY_METHODX, + Message.GENERAL_AMBIGUOUS_MAPPING_METHODY_METHODX, attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(), target.getName(), result.toString() ); @@ -878,28 +902,26 @@ private ConversionMethod getBestMatch(Type sourceType, Type targetType) { result = methodRefY; } else { - reportAmbigiousError( xRefCandidates, targetType ); + reportAmbiguousError( xRefCandidates, targetType ); } return this; } - void reportAmbigiousError(Map>> xRefCandidates, Type target) { + void reportAmbiguousError(Map>> xRefCandidates, Type target) { StringBuilder result = new StringBuilder(); xRefCandidates.entrySet() .stream() + .limit( MAX_REPORTING_AMBIGUOUS ) .forEach( e -> result.append( "method(s)Y: " ) - .append( e.getValue() - .stream() - .map( v -> v.getMethod().shortName() ) - .collect( Collectors.joining( ", " ) ) ) + .append( attempt.join( e.getValue() ) ) .append( ", conversionX: " ) .append( e.getKey().shortName() ) .append( "; " ) ); attempt.messager.printMessage( attempt.mappingMethod.getExecutable(), attempt.positionHint, - Message.GENERAL_AMBIGIOUS_MAPPING_METHODY_CONVERSIONX, + Message.GENERAL_AMBIGUOUS_MAPPING_METHODY_CONVERSIONX, attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(), target.getName(), result.toString() ); @@ -997,28 +1019,26 @@ private MethodConversion getBestMatch(Type sourceType, Type targetType) { result = conversionRefY.assignment; } else { - reportAmbigiousError( yRefCandidates, targetType ); + reportAmbiguousError( yRefCandidates, targetType ); } return this; } - void reportAmbigiousError(Map>> yRefCandidates, Type target) { + void reportAmbiguousError(Map>> yRefCandidates, Type target) { StringBuilder result = new StringBuilder(); yRefCandidates.entrySet() .stream() + .limit( MAX_REPORTING_AMBIGUOUS ) .forEach( e -> result.append( "conversionY: " ) .append( e.getKey().shortName() ) .append( ", method(s)X: " ) - .append( e.getValue() - .stream() - .map( v -> v.getMethod().shortName() ) - .collect( Collectors.joining( ", " ) ) ) + .append( attempt.join( e.getValue() ) ) .append( "; " ) ); attempt.messager.printMessage( attempt.mappingMethod.getExecutable(), attempt.positionHint, - Message.GENERAL_AMBIGIOUS_MAPPING_CONVERSIONY_METHODX, + Message.GENERAL_AMBIGUOUS_MAPPING_CONVERSIONY_METHODX, attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(), target.getName(), result.toString() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index d10faf64b9..e2a476ec9a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -7,6 +7,7 @@ import javax.tools.Diagnostic; +import static org.mapstruct.ap.internal.util.MessageConstants.FAQ_AMBIGUOUS_URL; import static org.mapstruct.ap.internal.util.MessageConstants.FAQ_QUALIFIER_URL; /** @@ -111,9 +112,9 @@ public enum Message { GENERAL_NO_IMPLEMENTATION( "No implementation type is registered for return type %s." ), GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ), - GENERAL_AMBIGIOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s." ), - GENERAL_AMBIGIOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s." ), - GENERAL_AMBIGIOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ), + GENERAL_AMBIGUOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), + GENERAL_AMBIGUOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), + GENERAL_AMBIGUOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ), GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ), GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ), GENERAL_VALID_DATE( "Given date format \"%s\" is valid.", Diagnostic.Kind.NOTE ), @@ -125,9 +126,9 @@ public enum Message { GENERAL_NO_QUALIFYING_METHOD_NAMED( "Qualifier error. No method found annotated with @Named#value: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ), GENERAL_NO_QUALIFYING_METHOD_COMBINED( "Qualifier error. No method found annotated with @Named#value: [ %s ], annotated with [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ), - GENERAL_AMBIGIOUS_MAPPING_METHODY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( methodX ( parameter ) ): %s." ), - GENERAL_AMBIGIOUS_MAPPING_CONVERSIONY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found conversionY( methodX ( parameter ) ): %s." ), - GENERAL_AMBIGIOUS_MAPPING_METHODY_CONVERSIONX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( conversionX ( parameter ) ): %s." ), + GENERAL_AMBIGUOUS_MAPPING_METHODY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( methodX ( parameter ) ): %s." ), + GENERAL_AMBIGUOUS_MAPPING_CONVERSIONY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found conversionY( methodX ( parameter ) ): %s." ), + GENERAL_AMBIGUOUS_MAPPING_METHODY_CONVERSIONX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( conversionX ( parameter ) ): %s." ), BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD( "More than one builder creation method for \"%s\". Found methods: \"%s\". Builder will not be used. Consider implementing a custom BuilderProvider SPI.", Diagnostic.Kind.WARNING ), BUILDER_NO_BUILD_METHOD_FOUND("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\".", Diagnostic.Kind.ERROR ), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/MessageConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/MessageConstants.java index d3dabdb2d8..6b4603af19 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/MessageConstants.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/MessageConstants.java @@ -9,6 +9,7 @@ public final class MessageConstants { public static final String AND = " and "; public static final String FAQ_QUALIFIER_URL = "https://mapstruct.org/faq/#qualifier"; + public static final String FAQ_AMBIGUOUS_URL = "https://mapstruct.org/faq/#ambiguous"; private MessageConstants() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java index 0cf59e7267..80e1038127 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java @@ -65,7 +65,8 @@ public void factoryMethodWithSourceParamIsChosen() { ".lang.Class clazz), org.mapstruct.ap.test.bugs._1242" + ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB(@TargetType java.lang" + ".Class clazz), org.mapstruct.ap.test.bugs._1242" + - ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB().") + ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB()." + + " See https://mapstruct.org/faq/#ambiguous for more info." ) }) public void ambiguousMethodErrorForTwoFactoryMethodsWithSourceParam() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java index 94993e4a66..24cdbe3a53 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java @@ -36,7 +36,8 @@ public class AmbiguousAnnotatedFactoryTest { ".ambiguousannotatedfactorymethod.Foo foo), org.mapstruct.ap.test.erroneous" + ".ambiguousannotatedfactorymethod.Bar org.mapstruct.ap.test.erroneous" + ".ambiguousannotatedfactorymethod.AmbiguousBarFactory.createBar(org.mapstruct.ap.test.erroneous" + - ".ambiguousannotatedfactorymethod.Foo foo).") + ".ambiguousannotatedfactorymethod.Foo foo)." + + " See https://mapstruct.org/faq/#ambiguous for more info.") } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java index e4e9ff81cb..682c46d036 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java @@ -37,8 +37,8 @@ public class FactoryTest { message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.erroneous" + ".ambiguousfactorymethod.Bar: org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar " + "createBar(), org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar org.mapstruct.ap.test" + - ".erroneous.ambiguousfactorymethod.a.BarFactory.createBar().") - + ".erroneous.ambiguousfactorymethod.a.BarFactory.createBar()." + + " See https://mapstruct.org/faq/#ambiguous for more info.") } ) public void shouldUseTwoFactoryMethods() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java new file mode 100644 index 0000000000..a04373b13f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java @@ -0,0 +1,67 @@ +/* + * 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.erroneous.ambiguousmapping; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +@IssueKey("2156") +@RunWith(AnnotationProcessorTestRunner.class) +public class AmbigiousMapperTest { + + @Test + @WithClasses( ErroneousWithAmbiguousMethodsMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousWithAmbiguousMethodsMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 16, + message = "Ambiguous mapping methods found for mapping property " + + "\"org.mapstruct.ap.test.erroneous.ambiguousmapping." + + "ErroneousWithAmbiguousMethodsMapper.LeafDTO branch.leaf\" to " + + "org.mapstruct.ap.test.erroneous.ambiguousmapping." + + "ErroneousWithAmbiguousMethodsMapper.LeafEntity: " + + "LeafEntity:map1(LeafDTO), LeafEntity:map2(LeafDTO). " + + "See https://mapstruct.org/faq/#ambiguous for more info.") + } + ) + + public void testErrorMessageForAmbiguous() { + } + + @Test + @WithClasses( ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + message = "Ambiguous mapping methods found for mapping property " + + "\"org.mapstruct.ap.test.erroneous.ambiguousmapping." + + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO branch.leaf\" to " + + "org.mapstruct.ap.test.erroneous.ambiguousmapping." + + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity: " + + "LeafEntity:map1(LeafDTO), " + + "LeafEntity:map2(LeafDTO), " + + "LeafEntity:map3(LeafDTO), " + + "LeafEntity:map4(LeafDTO), " + + "LeafEntity:map5(LeafDTO)" + + "... and 1 more. " + + "See https://mapstruct.org/faq/#ambiguous for more info.") + } + ) + public void testErrorMessageForManyAmbiguous() { + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithAmbiguousMethodsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithAmbiguousMethodsMapper.java new file mode 100644 index 0000000000..7ebce015d1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithAmbiguousMethodsMapper.java @@ -0,0 +1,52 @@ +/* + * 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.erroneous.ambiguousmapping; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousWithAmbiguousMethodsMapper { + + ErroneousWithAmbiguousMethodsMapper INSTANCE = Mappers.getMapper( ErroneousWithAmbiguousMethodsMapper.class ); + + TrunkEntity map(TrunkDTO dto); + + default LeafEntity map1(LeafDTO dto) { + return new LeafEntity(); + } + + // duplicated method, triggering ambigious mapping method + default LeafEntity map2(LeafDTO dto) { + return new LeafEntity(); + } + + // CHECKSTYLE:OFF + class TrunkDTO { + public BranchDTO branch; + } + + class BranchDTO { + public LeafDTO leaf; + } + + class LeafDTO { + public int numberOfVeigns; + } + + class TrunkEntity { + public BranchEntity branch; + } + + class BranchEntity { + public LeafEntity leaf; + } + + class LeafEntity { + public int numberOfVeigns; + } + // CHECKSTYLE ON +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithMoreThanFiveAmbiguousMethodsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithMoreThanFiveAmbiguousMethodsMapper.java new file mode 100644 index 0000000000..1e050cea71 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithMoreThanFiveAmbiguousMethodsMapper.java @@ -0,0 +1,74 @@ +/* + * 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.erroneous.ambiguousmapping; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousWithMoreThanFiveAmbiguousMethodsMapper { + + ErroneousWithMoreThanFiveAmbiguousMethodsMapper + INSTANCE = Mappers.getMapper( ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class ); + + TrunkEntity map(TrunkDTO dto); + + default LeafEntity map1(LeafDTO dto) { + return new LeafEntity(); + } + + // duplicated method, triggering ambigious mapping method + default LeafEntity map2(LeafDTO dto) { + return new LeafEntity(); + } + + // duplicated method, triggering ambigious mapping method + default LeafEntity map3(LeafDTO dto) { + return new LeafEntity(); + } + + // duplicated method, triggering ambigious mapping method + default LeafEntity map4(LeafDTO dto) { + return new LeafEntity(); + } + + // duplicated method, triggering ambigious mapping method + + default LeafEntity map5(LeafDTO dto) { + return new LeafEntity(); + } + + // duplicated method, triggering ambigious mapping method + default LeafEntity map6(LeafDTO dto) { + return new LeafEntity(); + } + + // CHECKSTYLE:OFF + class TrunkDTO { + public BranchDTO branch; + } + + class BranchDTO { + public LeafDTO leaf; + } + + class LeafDTO { + public int numberOfVeigns; + } + + class TrunkEntity { + public BranchEntity branch; + } + + class BranchEntity { + public LeafEntity leaf; + } + + class LeafEntity { + public int numberOfVeigns; + } + // CHECKSTYLE ON +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java index dc37b5f79c..7c2459b118 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java @@ -69,13 +69,12 @@ public void shouldMapAttributesWithSuperTypeUsingOtherMapper() { kind = Kind.ERROR, type = ErroneousSourceCompositeTargetCompositeMapper.class, line = 19, - message = "Ambiguous mapping methods found for mapping property \"org.mapstruct.ap.test.inheritance" + - ".complex.SourceExt prop1\" to org.mapstruct.ap.test.inheritance.complex.Reference: org.mapstruct.ap" + - ".test.inheritance.complex.Reference org.mapstruct.ap.test.inheritance.complex" + - ".AdditionalMappingHelper.asReference(org.mapstruct.ap.test.inheritance.complex.SourceBase source), " + - "org.mapstruct.ap.test.inheritance.complex.Reference org.mapstruct.ap.test.inheritance.complex" + - ".AdditionalMappingHelper.asReference(org.mapstruct.ap.test.inheritance.complex.AdditionalFooSource " + - "source).")) + message = "Ambiguous mapping methods found for mapping property " + + "\"org.mapstruct.ap.test.inheritance.complex.SourceExt prop1\" " + + "to org.mapstruct.ap.test.inheritance.complex.Reference: " + + "Reference:asReference(SourceBase), Reference:asReference(AdditionalFooSource). " + + "See https://mapstruct.org/faq/#ambiguous for more info.") + ) public void ambiguousMappingMethodsReportError() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java index 5cd415e56e..9db58ec201 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java @@ -48,7 +48,9 @@ public class InheritanceSelectionTest { message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.selection.resulttype" + ".Fruit: org.mapstruct.ap.test.selection.resulttype.Apple org.mapstruct.ap.test.selection" + ".resulttype.ConflictingFruitFactory.createApple(), org.mapstruct.ap.test.selection.resulttype" + - ".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana().") + ".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana()." + + " See https://mapstruct.org/faq/#ambiguous for more info." + ) } ) public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() { From 28017e2b0ca99793b1bf8da933ec0811b75c3635 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 19 Jul 2020 15:01:49 +0200 Subject: [PATCH 079/597] Add test case for demonstrating how the ignoreByDefault can be overridden from the base configuration --- .../ignore/inherit/IgnorePropertyTest.java | 22 ++++++++++++++++++- .../ap/test/ignore/inherit/ToolMapper.java | 11 +++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java index 7dce466b41..ef0cf6fc68 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java @@ -56,7 +56,7 @@ public void shouldIgnoreAllExeptOveriddenInherited() { @Test @IssueKey("1933") - public void shouldIgnoreBase() { + public void shouldInheritIgnoreByDefaultFromBase() { WorkBenchDto workBenchDto = new WorkBenchDto(); workBenchDto.setArticleName( "MyBench" ); @@ -74,4 +74,24 @@ public void shouldIgnoreBase() { assertThat( benchTarget.getCreationDate() ).isNull(); } + @Test + @IssueKey("1933") + public void shouldOnlyIgnoreBase() { + + WorkBenchDto workBenchDto = new WorkBenchDto(); + workBenchDto.setArticleName( "MyBench" ); + workBenchDto.setArticleDescription( "Beautiful" ); + workBenchDto.setCreationDate( new Date() ); + workBenchDto.setModificationDate( new Date() ); + + WorkBenchEntity benchTarget = ToolMapper.INSTANCE.mapBenchWithImplicit( workBenchDto ); + + assertThat( benchTarget ).isNotNull(); + assertThat( benchTarget.getArticleName() ).isEqualTo( "MyBench" ); + assertThat( benchTarget.getDescription() ).isEqualTo( "Beautiful" ); + assertThat( benchTarget.getKey() ).isNull(); + assertThat( benchTarget.getModificationDate() ).isNull(); + assertThat( benchTarget.getCreationDate() ).isNull(); + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/ToolMapper.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/ToolMapper.java index ca7da9b99c..10c3a513f5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/ToolMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/ToolMapper.java @@ -29,12 +29,21 @@ public interface ToolMapper { @InheritConfiguration( name = "mapBase" ) ToolEntity mapTool(ToolDto source); - // demonstrates that all the business stuff is mapped (implicit-by-name and defined) + // demonstrates that all the business stuff is mapped only defined because BeanMapping#ignoreByDefault @InheritConfiguration( name = "mapBase" ) @Mapping(target = "description", source = "articleDescription") WorkBenchEntity mapBench(WorkBenchDto source); + // demonstrates that all the business stuff is mapped (implicit-by-name and defined) + @BeanMapping( ignoreByDefault = false ) // needed to override the one from the BeanMapping mapBase + @InheritConfiguration( name = "mapBase" ) + @Mapping(target = "description", source = "articleDescription") + WorkBenchEntity mapBenchWithImplicit(WorkBenchDto source); + // ignore all the base properties by default @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "key", ignore = true) + @Mapping( target = "modificationDate", ignore = true) + @Mapping( target = "creationDate", ignore = true) BaseEntity mapBase(Object o); } From 0495cb7fa77271193cc4e0e774080256cb185b73 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 19 Jul 2020 15:26:48 +0200 Subject: [PATCH 080/597] #2149 Do not allow using BeanMapping(ignoreByDefault = true) in combination with Mapping(target = ".") This fixes an ArrayIndexOutOfBoundsException when they were used together --- .../model/source/MappingMethodOptions.java | 41 +++++++-- .../processor/MapperCreationProcessor.java | 2 +- .../mapstruct/ap/internal/util/Message.java | 1 + .../test/bugs/_2149/Erroneous2149Mapper.java | 84 +++++++++++++++++++ .../ap/test/bugs/_2149/Issue2149Test.java | 47 +++++++++++ 5 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Erroneous2149Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index e0007949a9..b7fd7f00d0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -11,11 +11,12 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.accessor.Accessor; import static org.mapstruct.ap.internal.model.source.MappingOptions.getMappingTargetNamesBy; @@ -250,7 +251,8 @@ private boolean elementsAreContainedIn( String redefinedName, String inheritedNa return false; } - public void applyIgnoreAll(SourceMethod method, TypeFactory typeFactory ) { + public void applyIgnoreAll(SourceMethod method, TypeFactory typeFactory, + FormattingMessager messager) { CollectionMappingStrategyGem cms = method.getOptions().getMapper().getCollectionMappingStrategy(); Type writeType = method.getResultType(); if ( !method.isUpdateMethod() ) { @@ -262,15 +264,27 @@ public void applyIgnoreAll(SourceMethod method, TypeFactory typeFactory ) { Map writeAccessors = writeType.getPropertyWriteAccessors( cms ); - Set mappedPropertyNames = mappings.stream() - .map( m -> getPropertyEntries( m )[0] ) - .collect( Collectors.toSet() ); + for ( MappingOptions mapping : mappings ) { + String mappedTargetProperty = getFirstTargetPropertyName( mapping ); + if ( !".".equals( mappedTargetProperty ) ) { + // Remove the mapped target property from the write accessors + writeAccessors.remove( mappedTargetProperty ); + } + else { + messager.printMessage( + method.getExecutable(), + getBeanMapping().getMirror(), + Message.BEANMAPPING_IGNORE_BY_DEFAULT_WITH_MAPPING_TARGET_THIS + ); + // Nothing more to do if this is reached + return; + } + } + // The writeAccessors now contains only the accessors that should be ignored for ( String targetPropertyName : writeAccessors.keySet() ) { - if ( !mappedPropertyNames.contains( targetPropertyName ) ) { - MappingOptions mapping = MappingOptions.forIgnore( targetPropertyName ); - mappings.add( mapping ); - } + MappingOptions mapping = MappingOptions.forIgnore( targetPropertyName ); + mappings.add( mapping ); } } @@ -290,4 +304,13 @@ private String[] getPropertyEntries( MappingOptions mapping ) { return mapping.getTargetName().split( "\\." ); } + private String getFirstTargetPropertyName(MappingOptions mapping) { + String targetName = mapping.getTargetName(); + if ( ".".equals( targetName ) ) { + return targetName; + } + + return getPropertyEntries( mapping )[0]; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index e480eaf7a1..25f25c3c3f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -486,7 +486,7 @@ else if ( applicableReversePrototypeMethods.size() > 1 ) { // @BeanMapping( ignoreByDefault = true ) if ( mappingOptions.getBeanMapping() != null && mappingOptions.getBeanMapping().isignoreByDefault() ) { - mappingOptions.applyIgnoreAll( method, typeFactory ); + mappingOptions.applyIgnoreAll( method, typeFactory, mappingContext.getMessager() ); } mappingOptions.markAsFullyInitialized(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index e2a476ec9a..2005127fec 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -39,6 +39,7 @@ public enum Message { BEANMAPPING_UNMAPPED_SOURCES_ERROR( "Unmapped source %s." ), BEANMAPPING_CYCLE_BETWEEN_PROPERTIES( "Cycle(s) between properties given via dependsOn(): %s." ), BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON( "\"%s\" is no property of the method return type." ), + BEANMAPPING_IGNORE_BY_DEFAULT_WITH_MAPPING_TARGET_THIS( "Using @BeanMapping( ignoreByDefault = true ) with @Mapping( target = \".\", ... ) is not allowed. You'll need to explicitly ignore the target properties that should be ignored instead." ), PROPERTYMAPPING_MAPPING_NOTE( "mapping property: %s to: %s.", Diagnostic.Kind.NOTE ), PROPERTYMAPPING_CREATE_NOTE( "creating property mapping: %s.", Diagnostic.Kind.NOTE ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Erroneous2149Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Erroneous2149Mapper.java new file mode 100644 index 0000000000..2b8d73cec2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Erroneous2149Mapper.java @@ -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._2149; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Erroneous2149Mapper { + + @BeanMapping(ignoreByDefault = true) + @Mapping(target = ".", source = "name") + Target map(Source source); + + class Target { + + private String firstName; + private String age; + private String address; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getAge() { + return age; + } + + public void setAge(String age) { + this.age = age; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + } + + class Source { + + private final String age; + private final Name name; + + public Source(String age, Name name) { + this.age = age; + this.name = name; + } + + public String getAge() { + return age; + } + + public Name getName() { + return name; + } + } + + class Name { + + private final String firstName; + + public Name(String firstName) { + this.firstName = firstName; + } + + public String getFirstName() { + return firstName; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java new file mode 100644 index 0000000000..80cfbcc7db --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java @@ -0,0 +1,47 @@ +/* + * 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._2149; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2149") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Erroneous2149Mapper.class +}) +public class Issue2149Test { + + @Test + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = Erroneous2149Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 18, + message = "Using @BeanMapping( ignoreByDefault = true ) with @Mapping( target = \".\", ... ) is not " + + "allowed. You'll need to explicitly ignore the target properties that should be ignored instead." + ), + @Diagnostic( + type = Erroneous2149Mapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 20, + message = "Unmapped target property: \"address\"." + ) + } + ) + public void shouldGiveCompileErrorWhenBeanMappingIgnoreByDefaultIsCombinedWithMappingTargetThis() { + } +} From 1d223284c24ee5d701d58623919fd77a5dbbd696 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 19 Jul 2020 18:10:12 +0200 Subject: [PATCH 081/597] [maven-release-plugin] prepare release 1.4.0.Beta3 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 6c908377c8..f6a8aee847 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta3 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 63311b5950..a8c0e2d591 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta3 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index c74e0b8bb1..c3aa7a3555 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta3 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index b404ed3cee..256024d196 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta3 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index ceb3cb1db6..8481a58e2b 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta3 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index a643bab4b8..54d72ec87a 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta3 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index b26cf8683d..d7d6634276 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta3 pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.Beta3 diff --git a/pom.xml b/pom.xml index f9c649c168..7b1f68e50e 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta3 parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.Beta3 diff --git a/processor/pom.xml b/processor/pom.xml index c047301268..4ef6a0c581 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Beta3 ../parent/pom.xml From ef4bfc9aad701558e0e31797040c4cd33c31d4d4 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 19 Jul 2020 18:10:13 +0200 Subject: [PATCH 082/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index f6a8aee847..6c908377c8 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta3 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index a8c0e2d591..63311b5950 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta3 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index c3aa7a3555..c74e0b8bb1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta3 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 256024d196..b404ed3cee 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta3 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 8481a58e2b..ceb3cb1db6 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta3 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 54d72ec87a..a643bab4b8 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta3 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index d7d6634276..b26cf8683d 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta3 + 1.4.0-SNAPSHOT pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.Beta3 + HEAD diff --git a/pom.xml b/pom.xml index 7b1f68e50e..f9c649c168 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta3 + 1.4.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.Beta3 + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 4ef6a0c581..c047301268 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Beta3 + 1.4.0-SNAPSHOT ../parent/pom.xml From 2e2c20fed7502c2cb86d20ede130dfa499813a48 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Fri, 24 Jul 2020 23:19:47 +0200 Subject: [PATCH 083/597] #2161 use short names instead of FQN's in error messages (#2166) --- .../conversion/CreateDecimalFormat.java | 5 ++ .../internal/model/AbstractBaseBuilder.java | 12 ++-- .../ap/internal/model/BeanMappingMethod.java | 55 ++++++++-------- .../model/ContainerMappingMethodBuilder.java | 3 +- .../ap/internal/model/ForgedMethod.java | 7 ++ .../internal/model/ForgedMethodHistory.java | 2 +- .../ap/internal/model/MapMappingMethod.java | 11 +--- .../NestedTargetPropertyMappingHolder.java | 2 +- .../model/ObjectFactoryMethodResolver.java | 10 ++- .../ap/internal/model/PropertyMapping.java | 8 +-- .../ap/internal/model/ValueMappingMethod.java | 2 +- .../model/beanmapping/AbstractReference.java | 6 +- .../ap/internal/model/common/Parameter.java | 10 ++- .../ap/internal/model/common/Type.java | 34 +++++++++- .../ap/internal/model/common/TypeFactory.java | 12 +++- .../ap/internal/model/source/Method.java | 6 +- .../internal/model/source/SourceMethod.java | 25 +++++++ .../model/source/builtin/BuiltInMethod.java | 6 ++ .../DefaultModelElementProcessorContext.java | 3 +- .../processor/MapperCreationProcessor.java | 4 +- .../processor/MethodRetrievalProcessor.java | 5 ++ .../creation/MappingResolverImpl.java | 41 +++++++----- .../mapstruct/ap/internal/util/Message.java | 4 +- .../DateFormatValidatorFactoryTest.java | 3 +- .../common/DefaultConversionContextTest.java | 3 +- .../ReferencedAccessibilityTest.java | 15 ++--- .../ap/test/bugs/_1005/Issue1005Test.java | 16 ++--- .../ap/test/bugs/_1029/Issue1029Test.java | 4 +- .../ap/test/bugs/_1153/Issue1153Test.java | 13 ++-- .../ap/test/bugs/_1242/Issue1242Test.java | 15 ++--- .../ap/test/bugs/_1283/Issue1283Test.java | 4 +- .../ap/test/bugs/_1698/Issue1698Test.java | 5 +- .../ap/test/bugs/_590/Issue590Test.java | 5 +- .../ErroneousCollectionMappingTest.java | 39 +++++------ .../forged/CollectionMappingTest.java | 21 +++--- .../conversion/lossy/LossyConversionTest.java | 23 ++++--- .../test/defaultvalue/DefaultValueTest.java | 10 ++- .../AmbiguousAnnotatedFactoryTest.java | 10 +-- .../ambiguousfactorymethod/FactoryTest.java | 7 +- .../ambiguousmapping/AmbigiousMapperTest.java | 66 +++++++++++++------ .../ErroneousMappingsTest.java | 3 +- .../ErroneousPropertyMappingTest.java | 19 ++---- .../ap/test/ignore/IgnorePropertyTest.java | 3 +- .../attribute/AttributeInheritanceTest.java | 4 +- .../complex/ComplexInheritanceTest.java | 9 ++- .../erroneous/ErroneousStreamMappingTest.java | 44 +++++-------- .../forged/ForgedStreamMappingTest.java | 7 +- .../mappingcontrol/MappingControlTest.java | 25 +++---- .../SuggestMostSimilarNameTest.java | 11 ++-- ...DisablingNestedSimpleBeansMappingTest.java | 10 ++- .../nestedbeans/DottedErrorMessageTest.java | 42 ++++-------- .../exclusions/ErroneousJavaInternalTest.java | 26 +++----- .../custom/ErroneousCustomExclusionTest.java | 7 +- .../NestedSourcePropertiesTest.java | 3 +- .../selection/generics/ConversionTest.java | 62 +++++++---------- .../selection/qualifier/QualifierTest.java | 10 ++- .../resulttype/InheritanceSelectionTest.java | 20 +++--- .../twosteperror/TwoStepMappingTest.java | 32 ++++----- .../test/source/constants/ConstantsTest.java | 23 +++---- .../source/constants/SourceConstantsTest.java | 9 +-- .../test/updatemethods/UpdateMethodsTest.java | 8 +-- 61 files changed, 465 insertions(+), 444 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java index 9005907d09..77c59445ab 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java @@ -55,4 +55,9 @@ public Type getReturnType() { public MappingMethodOptions getOptions() { return MappingMethodOptions.empty(); } + + @Override + public String describe() { + return null; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java index f2e657f8ef..ba13854667 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java @@ -146,10 +146,10 @@ void reportCannotCreateMapping(Method method, String sourceErrorMessagePart, Typ method.getExecutable(), Message.PROPERTYMAPPING_MAPPING_NOT_FOUND, sourceErrorMessagePart, - targetType, + targetType.describe(), targetPropertyName, - targetType, - sourceType + targetType.describe(), + sourceType.describe() ); } @@ -171,10 +171,10 @@ void reportCannotCreateMapping(Method method, AnnotationMirror posHint, String s posHint, Message.PROPERTYMAPPING_MAPPING_NOT_FOUND, sourceErrorMessagePart, - targetType, + targetType.describe(), targetPropertyName, - targetType, - sourceType + targetType.describe(), + sourceType.describe() ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index a82ee1a091..3d86a59299 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -489,8 +489,8 @@ private boolean canResultTypeFromBeanMappingBeConstructed(Type resultType) { method.getExecutable(), method.getOptions().getBeanMapping().getMirror(), BEANMAPPING_ABSTRACT, - resultType, - method.getResultType() + resultType.describe(), + method.getResultType().describe() ); error = false; } @@ -499,8 +499,8 @@ else if ( !resultType.isAssignableTo( method.getResultType() ) ) { method.getExecutable(), method.getOptions().getBeanMapping().getMirror(), BEANMAPPING_NOT_ASSIGNABLE, - resultType, - method.getResultType() + resultType.describe(), + method.getResultType().describe() ); error = false; } @@ -509,7 +509,7 @@ else if ( !resultType.hasAccessibleConstructor() ) { method.getExecutable(), method.getOptions().getBeanMapping().getMirror(), Message.GENERAL_NO_SUITABLE_CONSTRUCTOR, - resultType + resultType.describe() ); error = false; } @@ -522,7 +522,7 @@ private boolean canReturnTypeBeConstructed(Type returnType) { ctx.getMessager().printMessage( method.getExecutable(), GENERAL_ABSTRACT_RETURN_TYPE, - returnType + returnType.describe() ); error = false; } @@ -530,7 +530,7 @@ else if ( !returnType.hasAccessibleConstructor() ) { ctx.getMessager().printMessage( method.getExecutable(), Message.GENERAL_NO_SUITABLE_CONSTRUCTOR, - returnType + returnType.describe() ); error = false; } @@ -570,8 +570,11 @@ else if ( matchingFactoryMethods.size() == 1 ) { ctx.getMessager().printMessage( method.getExecutable(), Message.GENERAL_AMBIGUOUS_FACTORY_METHOD, - returnTypeImpl, - Strings.join( matchingFactoryMethods, ", " ) + returnTypeImpl.describe(), + matchingFactoryMethods.stream() + .map( SelectedMethod::getMethod ) + .map( Method::describe ) + .collect( Collectors.joining( ", " ) ) ); hasFactoryMethod = true; } @@ -911,13 +914,13 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTyp String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); Message msg; - Object[] args; + String[] args; if ( targetRef.getPathProperties().isEmpty() ) { msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE; - args = new Object[] { + args = new String[] { targetPropertyName, - resultTypeToMap, + resultTypeToMap.describe(), mostSimilarProperty }; } @@ -925,9 +928,9 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTyp List pathProperties = new ArrayList<>( targetRef.getPathProperties() ); pathProperties.add( mostSimilarProperty ); msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE; - args = new Object[] { + args = new String[] { targetPropertyName, - resultTypeToMap, + resultTypeToMap.describe(), mapping.getTargetName(), Strings.join( pathProperties, "." ) }; @@ -956,14 +959,14 @@ else if ( !mapping.isIgnored() ) { msg = Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_RESULTTYPE; args = new Object[] { mapping.getTargetName(), - resultTypeToMap + resultTypeToMap.describe() }; } else { msg = Message.BEANMAPPING_PROPERTY_HAS_NO_WRITE_ACCESSOR_IN_TYPE; args = new Object[] { targetPropertyName, - resultTypeToMap, + resultTypeToMap.describe(), mapping.getTargetName() }; } @@ -1279,10 +1282,10 @@ private void reportErrorForUnmappedTargetPropertiesIfRequired() { ctx.getMessager().printMessage( this.method.getExecutable(), Message.PROPERTYMAPPING_FORGED_MAPPING_NOT_FOUND, - sourceType, - targetType, - targetType, - sourceType + sourceType.describe(), + targetType.describe(), + targetType.describe(), + sourceType.describe() ); } else { @@ -1291,10 +1294,10 @@ private void reportErrorForUnmappedTargetPropertiesIfRequired() { this.method.getExecutable(), Message.PROPERTYMAPPING_FORGED_MAPPING_WITH_HISTORY_NOT_FOUND, history.createSourcePropertyErrorMessage(), - history.getTargetType(), + history.getTargetType().describe(), history.createTargetPropertyName(), - history.getTargetType(), - history.getSourceType() + history.getTargetType().describe(), + history.getSourceType().describe() ); } } @@ -1321,14 +1324,14 @@ else if ( !ctx.isErroneous() ) { Message msg = unmappedTargetPolicy.getDiagnosticKind() == Diagnostic.Kind.ERROR ? Message.BEANMAPPING_UNMAPPED_FORGED_TARGETS_ERROR : Message.BEANMAPPING_UNMAPPED_FORGED_TARGETS_WARNING; - String sourceErrorMessage = method.getParameters().get( 0 ).getType().toString(); - String targetErrorMessage = method.getReturnType().toString(); + String sourceErrorMessage = method.getParameters().get( 0 ).getType().describe(); + String targetErrorMessage = method.getReturnType().describe(); if ( ( (ForgedMethod) method ).getHistory() != null ) { ForgedMethodHistory history = ( (ForgedMethod) method ).getHistory(); sourceErrorMessage = history.createSourcePropertyErrorMessage(); targetErrorMessage = MessageFormat.format( "\"{0} {1}\"", - history.getTargetType(), + history.getTargetType().describe(), history.createTargetPropertyName() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java index 79113ce61a..b9bbf6d094 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java @@ -100,7 +100,8 @@ public final M build() { else { reportCannotCreateMapping( method, - String.format( "%s \"%s\"", sourceRHS.getSourceErrorMessagePart(), sourceRHS.getSourceType() ), + String.format( "%s \"%s\"", sourceRHS.getSourceErrorMessagePart(), + sourceRHS.getSourceType().describe() ), sourceRHS.getSourceType(), targetElementType, "" diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java index ce1d8c024d..e02107881b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java @@ -340,6 +340,13 @@ public MappingMethodOptions getOptions() { return basedOn.getOptions(); } + @Override + public String describe() { + // the name of the forged method is never fully qualified, so no need to distinguish + // between verbose or not. The type knows whether it should log verbose + return getResultType().describe() + ":" + getName() + "(" + getMappingSourceType().describe() + ")"; + } + public MappingReferences getMappingReferences() { return mappingReferences; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethodHistory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethodHistory.java index 9355c62fd5..3463413705 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethodHistory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethodHistory.java @@ -43,7 +43,7 @@ public Type getSourceType() { } public String createSourcePropertyErrorMessage() { - return conditionallyCapitalizedElementType() + " \"" + getSourceType() + " " + + return conditionallyCapitalizedElementType() + " \"" + getSourceType().describe() + " " + stripBrackets( getDottedSourceElement() ) + "\""; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java index 415f9b6c33..a8b68b435f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java @@ -20,7 +20,6 @@ import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; -import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; @@ -42,7 +41,6 @@ public static class Builder extends AbstractMappingMethodBuilder sourceTypeParams = @@ -116,7 +109,7 @@ public MapMappingMethod build() { String.format( "%s \"%s\"", keySourceRHS.getSourceErrorMessagePart(), - keySourceRHS.getSourceType() + keySourceRHS.getSourceType().describe() ), keySourceRHS.getSourceType(), keyTargetType, @@ -173,7 +166,7 @@ public MapMappingMethod build() { String.format( "%s \"%s\"", valueSourceRHS.getSourceErrorMessagePart(), - valueSourceRHS.getSourceType() + valueSourceRHS.getSourceType().describe() ), valueSourceRHS.getSourceType(), valueTargetType, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index d46019899f..254ba62871 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -662,7 +662,7 @@ private PropertyMapping createPropertyMappingForNestedTarget(MappingReferences m mapping.getTargetAnnotationValue(), Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE, targetPropertyName, - targetType, + targetType.describe(), mapping.getTargetName(), Strings.join( pathProperties, "." ) ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index 34fba406ef..d320bb49da 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -21,7 +22,6 @@ import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; import org.mapstruct.ap.internal.util.Message; -import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.util.Collections.first; @@ -82,8 +82,12 @@ public static MethodReference getFactoryMethod( Method method, ctx.getMessager().printMessage( method.getExecutable(), Message.GENERAL_AMBIGUOUS_FACTORY_METHOD, - alternativeTarget, - Strings.join( matchingFactoryMethods, ", " ) ); + alternativeTarget.describe(), + matchingFactoryMethods.stream() + .map( SelectedMethod::getMethod ) + .map( Method::describe ) + .collect( Collectors.joining( ", " ) ) + ); return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 4e0050e561..240343ad9b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -853,9 +853,8 @@ else if ( errorMessageDetails == null ) { method.getExecutable(), positionHint, Message.CONSTANTMAPPING_MAPPING_NOT_FOUND, - sourceType, constantExpression, - targetType, + targetType.describe(), targetPropertyName ); } @@ -864,9 +863,8 @@ else if ( errorMessageDetails == null ) { method.getExecutable(), positionHint, Message.CONSTANTMAPPING_MAPPING_NOT_FOUND_WITH_DETAILS, - sourceType, constantExpression, - targetType, + targetType.describe(), targetPropertyName, errorMessageDetails ); @@ -899,7 +897,7 @@ private Assignment getEnumAssignment() { positionHint, Message.CONSTANTMAPPING_NON_EXISTING_CONSTANT, constantExpression, - targetType, + targetType.describe(), targetPropertyName ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index adfa5779e8..72f8153c5e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -209,7 +209,7 @@ else if ( NULL.equals( targetConstant ) ) { ForgedMethodHistory history = ( (ForgedMethod) method ).getHistory(); sourceErrorMessage = history.createSourcePropertyErrorMessage(); targetErrorMessage = - "\"" + history.getTargetType().toString() + " " + history.createTargetPropertyName() + "\""; + "\"" + history.getTargetType().describe() + " " + history.createTargetPropertyName() + "\""; } // all sources should now be matched, there's no default to fall back to, so if sources remain, // we have an issue. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/AbstractReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/AbstractReference.java index dcb67c903d..4db93505e3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/AbstractReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/AbstractReference.java @@ -112,18 +112,18 @@ public String toString() { } else if ( propertyEntries.isEmpty() ) { if ( parameter != null ) { - result = String.format( "parameter \"%s %s\"", parameter.getType(), parameter.getName() ); + result = String.format( "parameter \"%s %s\"", parameter.getType().describe(), parameter.getName() ); } } else if ( propertyEntries.size() == 1 ) { PropertyEntry propertyEntry = propertyEntries.get( 0 ); - result = String.format( "property \"%s %s\"", propertyEntry.getType(), propertyEntry.getName() ); + result = String.format( "property \"%s %s\"", propertyEntry.getType().describe(), propertyEntry.getName() ); } else { PropertyEntry lastPropertyEntry = propertyEntries.get( propertyEntries.size() - 1 ); result = String.format( "property \"%s %s\"", - lastPropertyEntry.getType(), + lastPropertyEntry.getType().describe(), Strings.join( getElementNames(), "." ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java index 2fd2b69eeb..dc62018be5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java @@ -83,10 +83,18 @@ public boolean isMappingTarget() { @Override public String toString() { + return String.format( format(), type ); + } + + public String describe() { + return String.format( format(), type.describe() ); + } + + private String format() { return ( mappingTarget ? "@MappingTarget " : "" ) + ( targetType ? "@TargetType " : "" ) + ( mappingContext ? "@Context " : "" ) - + type.toString() + " " + name; + + "%s " + name; } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index a829fa61a2..b2b12b8a25 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -81,6 +82,8 @@ public class Type extends ModelElement implements Comparable { private final boolean isStream; private final boolean isLiteral; + private final boolean loggingVerbose; + private final List enumConstants; private final Map toBeImportedTypes; @@ -116,7 +119,7 @@ public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory, Map toBeImportedTypes, Map notToBeImportedTypes, Boolean isToBeImported, - boolean isLiteral ) { + boolean isLiteral, boolean loggingVerbose) { this.typeUtils = typeUtils; this.elementUtils = elementUtils; @@ -162,6 +165,8 @@ public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory, this.toBeImportedTypes = toBeImportedTypes; this.notToBeImportedTypes = notToBeImportedTypes; this.filters = new Filters( accessorNaming, typeUtils, typeMirror ); + + this.loggingVerbose = loggingVerbose; } //CHECKSTYLE:ON @@ -417,7 +422,8 @@ public Type erasure() { toBeImportedTypes, notToBeImportedTypes, isToBeImported, - isLiteral + isLiteral, + loggingVerbose ); } @@ -459,7 +465,8 @@ public Type withoutBounds() { toBeImportedTypes, notToBeImportedTypes, isToBeImported, - isLiteral + isLiteral, + loggingVerbose ); } @@ -1011,6 +1018,27 @@ public String toString() { return typeMirror.toString(); } + /** + * @return a string representation of the type for use in messages + */ + public String describe() { + if ( loggingVerbose ) { + return toString(); + } + else { + // name allows for inner classes + String name = getFullyQualifiedName().replaceFirst( "^" + getPackageName() + ".", "" ); + List typeParams = getTypeParameters(); + if ( typeParams.isEmpty() ) { + return name; + } + else { + String params = typeParams.stream().map( Type::describe ).collect( Collectors.joining( "," ) ); + return String.format( "%s<%s>", name, params ); + } + } + } + /** * * @return an identification that can be used as part in a forged method name. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index a74c02fd7d..9cfe18c176 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -95,8 +95,10 @@ public class TypeFactory { private final Map toBeImportedTypes = new HashMap<>(); private final Map notToBeImportedTypes; + private final boolean loggingVerbose; + public TypeFactory(Elements elementUtils, Types typeUtils, FormattingMessager messager, RoundContext roundContext, - Map notToBeImportedTypes) { + Map notToBeImportedTypes, boolean loggingVerbose) { this.elementUtils = elementUtils; this.typeUtils = typeUtils; this.messager = messager; @@ -129,6 +131,8 @@ public TypeFactory(Elements elementUtils, Types typeUtils, FormattingMessager me ConcurrentNavigableMap.class.getName(), withDefaultConstructor( getType( ConcurrentSkipListMap.class ) ) ); + + this.loggingVerbose = loggingVerbose; } public Type getTypeForLiteral(Class type) { @@ -298,7 +302,8 @@ else if (componentTypeMirror.getKind().isPrimitive()) { toBeImportedTypes, notToBeImportedTypes, toBeImported, - isLiteral + isLiteral, + loggingVerbose ); } @@ -523,7 +528,8 @@ private ImplementationType getImplementationType(TypeMirror mirror) { toBeImportedTypes, notToBeImportedTypes, null, - implementationType.isLiteral() + implementationType.isLiteral(), + loggingVerbose ); return implementation.createNew( replacement ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index 39d827d975..2039aa86aa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -196,9 +196,7 @@ default Type getMappingSourceType() { } /** - * @return the short name for error messages + * @return the short name for error messages when verbose, full name when not */ - default String shortName() { - return getResultType().getName() + ":" + getName() + "(" + getMappingSourceType().getName() + ")"; - } + String describe(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 9ed432d3ec..98cffb4c35 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -10,6 +10,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.util.Types; @@ -67,6 +68,8 @@ public class SourceMethod implements Method { private Boolean isStreamMapping; private final boolean hasObjectFactoryAnnotation; + private final boolean verboseLogging; + public static class Builder { private Type declaringMapper = null; @@ -86,6 +89,7 @@ public static class Builder { private List valueMappings; private EnumMappingOptions enumMappingOptions; private ParameterProvidedMethods contextProvidedMethods; + private boolean verboseLogging; public Builder setDeclaringMapper(Type declaringMapper) { this.declaringMapper = declaringMapper; @@ -172,6 +176,11 @@ public Builder setContextProvidedMethods(ParameterProvidedMethods contextProvide return this; } + public Builder setVerboseLogging(boolean verboseLogging) { + this.verboseLogging = verboseLogging; + return this; + } + public SourceMethod build() { if ( mappings == null ) { @@ -215,6 +224,8 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) this.typeFactory = builder.typeFactory; this.prototypeMethods = builder.prototypeMethods; this.mapperToImplement = builder.definingType; + + this.verboseLogging = builder.verboseLogging; } private boolean determineIfIsObjectFactory() { @@ -521,4 +532,18 @@ public boolean isUpdateMethod() { public boolean hasObjectFactoryAnnotation() { return hasObjectFactoryAnnotation; } + + @Override + public String describe() { + if ( verboseLogging ) { + return toString(); + } + else { + String mapper = declaringMapper != null ? declaringMapper.getName() + "." : ""; + String sourceTypes = getParameters().stream() + .map( Parameter::describe ) + .collect( Collectors.joining( ", " ) ); + return getResultType().describe() + " " + mapper + getName() + "(" + sourceTypes + ")"; + } + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java index 360eb56bac..7854534ed6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java @@ -268,4 +268,10 @@ public BuiltInConstructorFragment getConstructorFragment() { return null; } + @Override + public String describe() { + // the name of the builtin method is never fully qualified, so no need to distinguish + // between verbose or not. The type knows whether it should log verbose + return getResultType().describe() + ":" + getName() + "(" + getMappingSourceType().describe() + ")"; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java index 3b5cb1b036..f8f7eee5bc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java @@ -59,7 +59,8 @@ public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvir delegatingTypes, messager, roundContext, - notToBeImported + notToBeImported, + options.isVerbose() ); this.options = options; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 25f25c3c3f..adb0b8f24f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -110,7 +110,8 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, L typeUtils, typeFactory, new ArrayList<>( sourceModel ), - mapperReferences + mapperReferences, + options.isVerbose() ), mapperTypeElement, //sourceModel is passed only to fetch the after/before mapping methods in lifecycleCallbackFactory; @@ -325,7 +326,6 @@ else if ( method.isMapMapping() ) { .keySelectionParameters( keySelectionParameters ) .valueFormattingParameters( valueFormattingParameters ) .valueSelectionParameters( valueSelectionParameters ) - .nullValueMappingStrategy( nullValueMappingStrategy ) .build(); hasFactoryMethod = mapMappingMethod.getFactoryMethod() != null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index b11c157b66..672d905ee0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -44,6 +44,7 @@ import org.mapstruct.ap.internal.gem.ObjectFactoryGem; import org.mapstruct.ap.internal.gem.ValueMappingGem; import org.mapstruct.ap.internal.gem.ValueMappingsGem; +import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.AnnotationProcessingException; import org.mapstruct.ap.internal.util.Executables; @@ -74,6 +75,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor enumTransformationStrategies; private Types typeUtils; private Elements elementUtils; + private Options options; @Override public List process(ProcessorContext context, TypeElement mapperTypeElement, Void sourceModel) { @@ -83,6 +85,7 @@ public List process(ProcessorContext context, TypeElement mapperTy this.typeUtils = context.getTypeUtils(); this.elementUtils = context.getElementUtils(); this.enumTransformationStrategies = context.getEnumTransformationStrategies(); + this.options = context.getOptions(); this.messager.note( 0, Message.PROCESSING_NOTE, mapperTypeElement ); @@ -301,6 +304,7 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, .setTypeFactory( typeFactory ) .setPrototypeMethods( prototypeMethods ) .setContextProvidedMethods( contextProvidedMethods ) + .setVerboseLogging( options.isVerbose() ) .build(); } @@ -355,6 +359,7 @@ private SourceMethod getReferencedMethod(TypeElement usedMapper, ExecutableType .setExceptionTypes( exceptionTypes ) .setTypeUtils( typeUtils ) .setTypeFactory( typeFactory ) + .setVerboseLogging( options.isVerbose() ) .build(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 8fa20f079d..fe7635f7f6 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -75,7 +75,7 @@ */ public class MappingResolverImpl implements MappingResolver { - private static final int MAX_REPORTING_AMBIGUOUS = 5; + private static final int LIMIT_REPORTING_AMBIGUOUS = 5; private final FormattingMessager messager; private final Types typeUtils; @@ -88,6 +88,8 @@ public class MappingResolverImpl implements MappingResolver { private final BuiltInMappingMethods builtInMethods; private final MethodSelectors methodSelectors; + private final boolean verboseLogging; + private static final String JL_OBJECT_NAME = Object.class.getName(); /** @@ -98,7 +100,7 @@ public class MappingResolverImpl implements MappingResolver { public MappingResolverImpl(FormattingMessager messager, Elements elementUtils, Types typeUtils, TypeFactory typeFactory, List sourceModel, - List mapperReferences) { + List mapperReferences, boolean verboseLogging) { this.messager = messager; this.typeUtils = typeUtils; this.typeFactory = typeFactory; @@ -109,6 +111,8 @@ public MappingResolverImpl(FormattingMessager messager, Elements elementUtils, T this.conversions = new Conversions( typeFactory ); this.builtInMethods = new BuiltInMappingMethods( typeFactory ); this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, typeFactory, messager ); + + this.verboseLogging = verboseLogging; } @Override @@ -128,7 +132,8 @@ public Assignment getTargetAssignment(Method mappingMethod, ForgedMethodHistory positionHint, forger, builtInMethods.getBuiltInMethods(), - messager + messager, + verboseLogging ); return attempt.getTargetAssignment( sourceRHS.getSourceTypeForMatching(), targetType ); @@ -162,19 +167,21 @@ private class ResolvingAttempt { private final Supplier forger; private final List builtIns; private final FormattingMessager messager; + private final int reportingLimitAmbiguous; // resolving via 2 steps creates the possibility of wrong matches, first builtin method matches, // second doesn't. In that case, the first builtin method should not lead to a supported method // so this set must be cleared. private final Set supportingMethodCandidates; + // CHECKSTYLE:OFF private ResolvingAttempt(List sourceModel, Method mappingMethod, ForgedMethodHistory description, FormattingParameters formattingParameters, SourceRHS sourceRHS, SelectionCriteria criteria, AnnotationMirror positionHint, Supplier forger, List builtIns, - FormattingMessager messager) { + FormattingMessager messager, boolean verboseLogging) { this.mappingMethod = mappingMethod; this.description = description; @@ -188,7 +195,9 @@ private ResolvingAttempt(List sourceModel, Method mappingMethod, ForgedM this.forger = forger; this.builtIns = builtIns; this.messager = messager; + this.reportingLimitAmbiguous = verboseLogging ? Integer.MAX_VALUE : LIMIT_REPORTING_AMBIGUOUS; } + // CHECKSTYLE:ON private List filterPossibleCandidateMethods(List candidateMethods) { List result = new ArrayList<>( candidateMethods.size() ); @@ -469,7 +478,7 @@ private void reportErrorWhenAmbiguous(List> positionHint, Message.GENERAL_AMBIGUOUS_MAPPING_METHOD, descriptionStr, - target, + target.describe(), join( candidates ) ); } @@ -478,7 +487,7 @@ private void reportErrorWhenAmbiguous(List> mappingMethod.getExecutable(), positionHint, Message.GENERAL_AMBIGUOUS_FACTORY_METHOD, - target, + target.describe(), join( candidates ) ); } @@ -595,12 +604,12 @@ private boolean hasCompatibleCopyConstructor(Type sourceType, Type targetType) { private String join( List> candidates ) { String candidateStr = candidates.stream() - .limit( MAX_REPORTING_AMBIGUOUS ) - .map( m -> m.getMethod().shortName() ) + .limit( reportingLimitAmbiguous ) + .map( m -> m.getMethod().describe() ) .collect( Collectors.joining( ", " ) ); - if ( candidates.size() > MAX_REPORTING_AMBIGUOUS ) { - candidateStr += String.format( "... and %s more", candidates.size() - MAX_REPORTING_AMBIGUOUS ); + if ( candidates.size() > reportingLimitAmbiguous ) { + candidateStr += String.format( "... and %s more", candidates.size() - reportingLimitAmbiguous ); } return candidateStr; } @@ -642,8 +651,8 @@ private void report(FormattingMessager messager, ResolvingAttempt attempt, Messa attempt.positionHint, message, attempt.sourceRHS.getSourceErrorMessagePart(), - sourceType.toString(), - targetType.toString() + sourceType.describe(), + targetType.describe() ); } @@ -798,11 +807,11 @@ void reportAmbiguousError(Map, List>> xCan StringBuilder result = new StringBuilder(); xCandidates.entrySet() .stream() - .limit( MAX_REPORTING_AMBIGUOUS ) + .limit( attempt.reportingLimitAmbiguous ) .forEach( e -> result.append( "method(s)Y: " ) .append( attempt.join( e.getValue() ) ) .append( ", methodX: " ) - .append( e.getKey().getMethod().shortName() ) + .append( e.getKey().getMethod().describe() ) .append( "; " ) ); attempt.messager.printMessage( attempt.mappingMethod.getExecutable(), @@ -912,7 +921,7 @@ void reportAmbiguousError(Map>> xRe StringBuilder result = new StringBuilder(); xRefCandidates.entrySet() .stream() - .limit( MAX_REPORTING_AMBIGUOUS ) + .limit( attempt.reportingLimitAmbiguous ) .forEach( e -> result.append( "method(s)Y: " ) .append( attempt.join( e.getValue() ) ) .append( ", conversionX: " ) @@ -1029,7 +1038,7 @@ void reportAmbiguousError(Map>> yRe StringBuilder result = new StringBuilder(); yRefCandidates.entrySet() .stream() - .limit( MAX_REPORTING_AMBIGUOUS ) + .limit( attempt.reportingLimitAmbiguous ) .forEach( e -> result.append( "conversionY: " ) .append( e.getKey().shortName() ) .append( ", method(s)X: " ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 2005127fec..67cd1aba06 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -77,8 +77,8 @@ public enum Message { CONVERSION_LOSSY_WARNING( "%s has a possibly lossy conversion from %s to %s.", Diagnostic.Kind.WARNING ), CONVERSION_LOSSY_ERROR( "Can't map %s. It has a possibly lossy conversion from %s to %s." ), - CONSTANTMAPPING_MAPPING_NOT_FOUND( "Can't map \"%s %s\" to \"%s %s\"." ), - CONSTANTMAPPING_MAPPING_NOT_FOUND_WITH_DETAILS( "Can't map \"%s %s\" to \"%s %s\". Reason: %s." ), + CONSTANTMAPPING_MAPPING_NOT_FOUND( "Can't map %s to \"%s %s\"." ), + CONSTANTMAPPING_MAPPING_NOT_FOUND_WITH_DETAILS( "Can't map %s to \"%s %s\". Reason: %s." ), CONSTANTMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE( "No read accessor found for property \"%s\" in target type." ), CONSTANTMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s for property \"%s\"." ), diff --git a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactoryTest.java index 9552e881bd..7bbc80e51b 100755 --- a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactoryTest.java @@ -177,7 +177,8 @@ private Type typeWithFQN(String fullQualifiedName) { new HashMap<>( ), new HashMap<>( ), false, - false); + false, false + ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java index 2b65030625..f8cceee9c0 100755 --- a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java @@ -125,7 +125,8 @@ private Type typeWithFQN(String fullQualifiedName) { new HashMap<>( ), new HashMap<>( ), false, - false); + false, false + ); } private static class StatefulMessagerMock implements FormattingMessager { diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java index 3819545090..44aeb033fa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java @@ -38,9 +38,8 @@ public class ReferencedAccessibilityTest { @Diagnostic(type = SourceTargetMapperPrivate.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 22, - message = "Unmapped target property: \"bar\". Mapping from property \"org.mapstruct.ap" + - ".test.accessibility.referenced.ReferencedSource referencedSource\" to \"org.mapstruct" + - ".ap.test.accessibility.referenced.ReferencedTarget referencedTarget\".") + message = "Unmapped target property: \"bar\". Mapping from property " + + "\"ReferencedSource referencedSource\" to \"ReferencedTarget referencedTarget\".") } ) public void shouldNotBeAbleToAccessPrivateMethodInReferenced() { @@ -66,9 +65,8 @@ public void shouldBeAbleToAccessProtectedMethodInReferencedInSamePackage() { } @Diagnostic(type = SourceTargetMapperDefaultOther.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 24, - message = "Unmapped target property: \"bar\". Mapping from property \"org.mapstruct.ap" + - ".test.accessibility.referenced.ReferencedSource referencedSource\" to \"org.mapstruct" + - ".ap.test.accessibility.referenced.ReferencedTarget referencedTarget\".") + message = "Unmapped target property: \"bar\". Mapping " + + "from property \"ReferencedSource referencedSource\" to \"ReferencedTarget referencedTarget\".") } ) public void shouldNotBeAbleToAccessDefaultMethodInReferencedInOtherPackage() { @@ -89,9 +87,8 @@ public void shouldBeAbleToAccessProtectedMethodInBase() { } @Diagnostic(type = AbstractSourceTargetMapperPrivate.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 23, - message = "Unmapped target property: \"bar\". Mapping from property \"org.mapstruct.ap" + - ".test.accessibility.referenced.ReferencedSource referencedSource\" to \"org.mapstruct" + - ".ap.test.accessibility.referenced.ReferencedTarget referencedTarget\".") + message = "Unmapped target property: \"bar\". Mapping from property " + + "\"ReferencedSource referencedSource\" to \"ReferencedTarget referencedTarget\".") } ) public void shouldNotBeAbleToAccessPrivateMethodInBase() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java index 7f97d2db51..95509dc0ce 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java @@ -35,8 +35,7 @@ public class Issue1005Test { @Diagnostic(type = Issue1005ErroneousAbstractResultTypeMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - message = "The result type org.mapstruct.ap.test.bugs._1005.AbstractEntity may not be an " + - "abstract class nor interface.") + message = "The result type AbstractEntity may not be an abstract class nor interface.") }) public void shouldFailDueToAbstractResultType() { } @@ -48,9 +47,8 @@ public void shouldFailDueToAbstractResultType() { @Diagnostic(type = Issue1005ErroneousAbstractReturnTypeMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = - "The return type org.mapstruct.ap.test.bugs._1005.AbstractEntity is an abstract class or " + - "interface. Provide a non abstract / non interface result type or a factory method.") + message = "The return type AbstractEntity is an abstract class or interface. " + + "Provide a non abstract / non interface result type or a factory method.") }) public void shouldFailDueToAbstractReturnType() { } @@ -62,8 +60,7 @@ public void shouldFailDueToAbstractReturnType() { @Diagnostic(type = Issue1005ErroneousInterfaceResultTypeMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - message = "The result type org.mapstruct.ap.test.bugs._1005.HasPrimaryKey may not be an " + - "abstract class nor interface.") + message = "The result type HasPrimaryKey may not be an abstract class nor interface.") }) public void shouldFailDueToInterfaceResultType() { } @@ -75,9 +72,8 @@ public void shouldFailDueToInterfaceResultType() { @Diagnostic(type = Issue1005ErroneousInterfaceReturnTypeMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = - "The return type org.mapstruct.ap.test.bugs._1005.HasKey is an abstract class or interface. " + - "Provide a non abstract / non interface result type or a factory method.") + message = "The return type HasKey is an abstract class or interface. " + + "Provide a non abstract / non interface result type or a factory method.") }) public void shouldFailDueToInterfaceReturnType() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java index afebcbef43..037ac64069 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java @@ -33,8 +33,8 @@ public class Issue1029Test { @Diagnostic(kind = Kind.WARNING, line = 37, type = ErroneousIssue1029Mapper.class, message = "Unmapped target property: \"lastUpdated\"."), @Diagnostic(kind = Kind.ERROR, line = 42, type = ErroneousIssue1029Mapper.class, - message = "Unknown property \"unknownProp\" in result type " + - "org.mapstruct.ap.test.bugs._1029.ErroneousIssue1029Mapper.Deck. Did you mean \"knownProp\"?") + message = "Unknown property \"unknownProp\" in result type ErroneousIssue1029Mapper.Deck. " + + "Did you mean \"knownProp\"?") }) public void reportsProperWarningsAndError() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java index bc3e59922f..015268b53e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java @@ -27,19 +27,18 @@ public class Issue1153Test { @Diagnostic(type = ErroneousIssue1153Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - message = "Property \"readOnly\" has no write accessor in " + - "org.mapstruct.ap.test.bugs._1153.ErroneousIssue1153Mapper.Target."), + message = "Property \"readOnly\" has no write accessor in ErroneousIssue1153Mapper.Target."), @Diagnostic(type = ErroneousIssue1153Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - message = "Property \"readOnly\" has no write accessor in org.mapstruct.ap.test.bugs._1153" + - ".ErroneousIssue1153Mapper.Target.NestedTarget for target name \"nestedTarget.readOnly\"."), + message = + "Property \"readOnly\" has no write accessor in ErroneousIssue1153Mapper.Target.NestedTarget " + + "for target name \"nestedTarget.readOnly\"."), @Diagnostic(type = ErroneousIssue1153Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 23, - message = "Unknown property \"writable2\" in type org.mapstruct.ap.test.bugs._1153" + - ".ErroneousIssue1153Mapper.Target.NestedTarget for target name \"nestedTarget2.writable2\". Did " + - "you mean \"nestedTarget2.writable\"?") + message = "Unknown property \"writable2\" in type ErroneousIssue1153Mapper.Target.NestedTarget " + + "for target name \"nestedTarget2.writable2\". Did you mean \"nestedTarget2.writable\"?") }) @Test public void shouldReportErrorsCorrectly() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java index 80e1038127..c7e9c42f95 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java @@ -58,15 +58,12 @@ public void factoryMethodWithSourceParamIsChosen() { @Diagnostic(type = ErroneousIssue1242MapperMultipleSources.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.bugs._1242.TargetB: org" + - ".mapstruct.ap.test.bugs._1242.TargetB anotherTargetBCreator(org.mapstruct.ap.test.bugs._1242" + - ".SourceB source), org.mapstruct.ap.test.bugs._1242.TargetB org.mapstruct.ap.test.bugs._1242" + - ".TargetFactories.createTargetB(org.mapstruct.ap.test.bugs._1242.SourceB source, @TargetType java" + - ".lang.Class clazz), org.mapstruct.ap.test.bugs._1242" + - ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB(@TargetType java.lang" + - ".Class clazz), org.mapstruct.ap.test.bugs._1242" + - ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB()." + - " See https://mapstruct.org/faq/#ambiguous for more info." ) + message = "Ambiguous factory methods found for creating TargetB: " + + "TargetB anotherTargetBCreator(SourceB source), " + + "TargetB TargetFactories.createTargetB(SourceB source, @TargetType Class clazz), " + + "TargetB TargetFactories.createTargetB(@TargetType Class clazz), " + + "TargetB TargetFactories.createTargetB(). " + + "See https://mapstruct.org/faq/#ambiguous for more info.") }) public void ambiguousMethodErrorForTwoFactoryMethodsWithSourceParam() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java index b4ce1447f8..0d4fef255a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java @@ -33,7 +33,7 @@ public class Issue1283Test { @Diagnostic(type = ErroneousInverseTargetHasNoSuitableConstructorMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22L, - message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible constructor." + message = "Source does not have an accessible constructor." ) } ) @@ -48,7 +48,7 @@ public void inheritInverseConfigurationReturnTypeHasNoSuitableConstructor() { @Diagnostic(type = ErroneousTargetHasNoSuitableConstructorMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 18L, - message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible constructor." + message = "Source does not have an accessible constructor." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java index 47ab19f1dd..a7a3b58e75 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java @@ -24,9 +24,8 @@ public class Issue1698Test { diagnostics = { @Diagnostic(type = Erroneous1698Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - message = "Can't map property \"java.lang.String rabbit\" to \"org.mapstruct.ap.test.bugs._1698" + - ".Erroneous1698Mapper.Rabbit rabbit\". Consider to declare/implement a mapping method: \"org" + - ".mapstruct.ap.test.bugs._1698.Erroneous1698Mapper.Rabbit map(java.lang.String value)\".") + message = "Can't map property \"String rabbit\" to \"Erroneous1698Mapper.Rabbit rabbit\". " + + "Consider to declare/implement a mapping method: \"Erroneous1698Mapper.Rabbit map(String value)\".") }) public void testErrorMessage() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java index e716d70a34..60c3910c0a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java @@ -27,9 +27,8 @@ public class Issue590Test { diagnostics = { @Diagnostic(type = ErroneousSourceTargetMapper.class, kind = Kind.ERROR, - message = "Can't map property \"java.lang.String prop\" to \"java.util.logging.XMLFormatter " + - "prop\". Consider to declare/implement a mapping method: \"java.util.logging.XMLFormatter map" + - "(java.lang.String value)\".") + message = "Can't map property \"String prop\" to \"XMLFormatter prop\". " + + "Consider to declare/implement a mapping method: \"XMLFormatter map(String value)\".") }) public void showsCantMapPropertyError() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java index 0aaf28d318..9e7293919d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java @@ -54,9 +54,8 @@ public void shouldFailToGenerateImplementationBetweenCollectionAndNonCollection( @Diagnostic(type = ErroneousCollectionToPrimitivePropertyMapper.class, kind = Kind.ERROR, line = 13, - message = "Can't map property \"java.util.List strings\" to \"int strings\". " - + "Consider to declare/implement a mapping method: \"int map(java.util.List" - + " value)\".") + message = "Can't map property \"List strings\" to \"int strings\". " + + "Consider to declare/implement a mapping method: \"int map(List value)\".") } ) public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { @@ -104,10 +103,9 @@ public void shouldFailOnEmptyMapAnnotation() { @Diagnostic(type = ErroneousCollectionNoElementMappingFound.class, kind = Kind.ERROR, line = 25, - message = "No target bean properties found: can't map Collection element \"org.mapstruct.ap.test" + - ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + - "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + - ".mapstruct.ap.test.WithProperties value)\".") + message = "No target bean properties found: can't map Collection element " + + "\"WithProperties withProperties\" to \"NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"NoProperties map(WithProperties value)\".") } ) public void shouldFailOnNoElementMappingFound() { @@ -122,9 +120,8 @@ public void shouldFailOnNoElementMappingFound() { @Diagnostic(type = ErroneousCollectionNoElementMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 19, - message = "Can't map collection element \"java.text.AttributedString\" to \"java.lang.String \". " + - "Consider to declare/implement a mapping method: \"java.lang.String map(java.text" + - ".AttributedString value)\".") + message = "Can't map collection element \"AttributedString\" to \"String \". " + + "Consider to declare/implement a mapping method: \"String map(AttributedString value)\".") } ) public void shouldFailOnNoElementMappingFoundWithDisabledAuto() { @@ -139,10 +136,9 @@ public void shouldFailOnNoElementMappingFoundWithDisabledAuto() { @Diagnostic(type = ErroneousCollectionNoKeyMappingFound.class, kind = Kind.ERROR, line = 25, - message = "No target bean properties found: can't map Map key \"org.mapstruct.ap.test.WithProperties " + - "withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". Consider to " + - "declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org.mapstruct.ap" + - ".test.WithProperties value)\".") + message = "No target bean properties found: can't map Map key \"WithProperties withProperties\" to " + + "\"NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"NoProperties map(WithProperties value)\".") } ) public void shouldFailOnNoKeyMappingFound() { @@ -157,8 +153,8 @@ public void shouldFailOnNoKeyMappingFound() { @Diagnostic(type = ErroneousCollectionNoKeyMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 19, - message = "Can't map map key \"java.text.AttributedString\" to \"java.lang.String \". Consider to " + - "declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)\".") + message = "Can't map map key \"AttributedString\" to \"String \". Consider to " + + "declare/implement a mapping method: \"String map(AttributedString value)\".") } ) public void shouldFailOnNoKeyMappingFoundWithDisabledAuto() { @@ -173,10 +169,9 @@ public void shouldFailOnNoKeyMappingFoundWithDisabledAuto() { @Diagnostic(type = ErroneousCollectionNoValueMappingFound.class, kind = Kind.ERROR, line = 25, - message = "No target bean properties found: can't map Map value \"org.mapstruct.ap.test" + - ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + - "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + - ".mapstruct.ap.test.WithProperties value)\".") + message = "No target bean properties found: can't map Map value \"WithProperties withProperties\" " + + "to \"NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"NoProperties map(WithProperties value)\".") } ) public void shouldFailOnNoValueMappingFound() { @@ -191,8 +186,8 @@ public void shouldFailOnNoValueMappingFound() { @Diagnostic(type = ErroneousCollectionNoValueMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 19, - message = "Can't map map value \"java.text.AttributedString\" to \"java.lang.String \". Consider to " + - "declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)\".") + message = "Can't map map value \"AttributedString\" to \"String \". " + + "Consider to declare/implement a mapping method: \"String map(AttributedString value)\".") } ) public void shouldFailOnNoValueMappingFoundWithDisabledAuto() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java index 50c2ffcb96..9fe6fec56a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java @@ -88,10 +88,9 @@ public void shouldForgeNewMapMappingMethod() { @Diagnostic(type = ErroneousCollectionNonMappableSetMapper.class, kind = Kind.ERROR, line = 17, - message = "No target bean properties found: can't map Collection element \"org.mapstruct.ap" + - ".test.collection.forged.Foo nonMappableSet\" to \"org.mapstruct.ap.test.collection.forged.Bar " + - "nonMappableSet\". Consider to declare/implement a mapping method: \"org.mapstruct.ap.test" + - ".collection.forged.Bar map(org.mapstruct.ap.test.collection.forged.Foo value)\"."), + message = "No target bean properties found: " + + "can't map Collection element \"Foo nonMappableSet\" to \"Bar nonMappableSet\". " + + "Consider to declare/implement a mapping method: \"Bar map(Foo value)\"."), } ) public void shouldGenerateNonMappleMethodForSetMapping() { @@ -110,17 +109,15 @@ public void shouldGenerateNonMappleMethodForSetMapping() { @Diagnostic(type = ErroneousCollectionNonMappableMapMapper.class, kind = Kind.ERROR, line = 17, - message = "No target bean properties found: can't map Map key \"org.mapstruct.ap.test" + - ".collection.forged.Foo nonMappableMap{:key}\" to \"org.mapstruct.ap.test.collection.forged.Bar " + - "nonMappableMap{:key}\". Consider to declare/implement a mapping method: \"org.mapstruct.ap" + - ".test.collection.forged.Bar map(org.mapstruct.ap.test.collection.forged.Foo value)\"."), + message = "No target bean properties found: " + + "can't map Map key \"Foo nonMappableMap{:key}\" to \"Bar nonMappableMap{:key}\". " + + "Consider to declare/implement a mapping method: \"Bar map(Foo value)\"."), @Diagnostic(type = ErroneousCollectionNonMappableMapMapper.class, kind = Kind.ERROR, line = 17, - message = "No target bean properties found: can't map Map value \"org.mapstruct.ap.test" + - ".collection.forged.Foo nonMappableMap{:value}\" to \"org.mapstruct.ap.test.collection.forged.Bar" + - " nonMappableMap{:value}\". Consider to declare/implement a mapping method: \"org.mapstruct.ap" + - ".test.collection.forged.Bar map(org.mapstruct.ap.test.collection.forged.Foo value)\"."), + message = "No target bean properties found: " + + "can't map Map value \"Foo nonMappableMap{:value}\" to \"Bar nonMappableMap{:value}\". " + + "Consider to declare/implement a mapping method: \"Bar map(Foo value)\"."), } ) public void shouldGenerateNonMappleMethodForMapMapping() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java index ae61da8e19..1e5d275f43 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java @@ -71,8 +71,8 @@ public void testConversionFromLongToInt() { @Diagnostic(type = KitchenDrawerMapper2.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 24, - message = "property \"java.math.BigInteger numberOfKnifes\" has a possibly lossy conversion " - + "from java.math.BigInteger to java.lang.Integer.") + message = "property \"BigInteger numberOfKnifes\" has a possibly lossy conversion " + + "from BigInteger to Integer.") }) public void testConversionFromBigIntegerToInteger() { } @@ -84,8 +84,8 @@ public void testConversionFromBigIntegerToInteger() { @Diagnostic(type = ErroneousKitchenDrawerMapper3.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - message = "Can't map property \"org.mapstruct.ap.test.conversion.lossy.VerySpecialNumber " + - "numberOfSpoons\". It has a possibly lossy conversion from java.math.BigInteger to java.lang.Long.") + message = "Can't map property \"VerySpecialNumber numberOfSpoons\". " + + "It has a possibly lossy conversion from BigInteger to Long.") }) public void test2StepConversionFromBigIntegerToLong() { } @@ -97,8 +97,8 @@ public void test2StepConversionFromBigIntegerToLong() { @Diagnostic(type = ErroneousKitchenDrawerMapper4.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - message = "Can't map property \"java.lang.Double depth\". It has a possibly lossy conversion " - + "from java.lang.Double to float.") + message = + "Can't map property \"Double depth\". It has a possibly lossy conversion from Double to float.") }) public void testConversionFromDoubleToFloat() { } @@ -110,8 +110,8 @@ public void testConversionFromDoubleToFloat() { @Diagnostic(type = ErroneousKitchenDrawerMapper5.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 24, - message = "Can't map property \"java.math.BigDecimal length\". It has a possibly lossy conversion from " - + "java.math.BigDecimal to java.lang.Float.") + message = + "Can't map property \"BigDecimal length\". It has a possibly lossy conversion from BigDecimal to Float.") }) public void testConversionFromBigDecimalToFloat() { } @@ -135,8 +135,7 @@ public void test2StepConversionFromDoubleToFloat() { @Diagnostic(type = ListMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 21, - message = "collection element has a possibly lossy conversion from java.math.BigDecimal to " - + "java.math.BigInteger.") + message = "collection element has a possibly lossy conversion from BigDecimal to BigInteger.") }) public void testListElementConversion() { } @@ -148,11 +147,11 @@ public void testListElementConversion() { @Diagnostic(type = MapMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 19, - message = "map key has a possibly lossy conversion from java.lang.Long to java.lang.Integer."), + message = "map key has a possibly lossy conversion from Long to Integer."), @Diagnostic(type = MapMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 19, - message = "map value has a possibly lossy conversion from java.lang.Double to java.lang.Float.") + message = "map value has a possibly lossy conversion from Double to Float.") }) public void testMapElementConversion() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java index f2054b90b9..ff7bf00722 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java @@ -129,9 +129,8 @@ public void shouldHandleUpdateMethodsFromEntityToEntity() { @Diagnostic(type = ErroneousMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - message = "Can't map property \"org.mapstruct.ap.test.defaultvalue.Region region\" to \"java.lang" + - ".String region\". Consider to declare/implement a mapping method: \"java.lang.String map(org" + - ".mapstruct.ap.test.defaultvalue.Region value)\".") + message = "Can't map property \"Region region\" to \"String region\". " + + "Consider to declare/implement a mapping method: \"String map(Region value)\".") } ) public void errorOnDefaultValueAndConstant() { @@ -153,9 +152,8 @@ public void errorOnDefaultValueAndConstant() { @Diagnostic(type = ErroneousMapper2.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, - message = "Can't map property \"org.mapstruct.ap.test.defaultvalue.Region region\" to \"java.lang" + - ".String region\". Consider to declare/implement a mapping method: \"java.lang.String map(org" + - ".mapstruct.ap.test.defaultvalue.Region value)\".") + message = "Can't map property \"Region region\" to \"String region\". " + + "Consider to declare/implement a mapping method: \"String map(Region value)\".") } ) public void errorOnDefaultValueAndExpression() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java index 24cdbe3a53..3887cd4a46 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java @@ -30,13 +30,9 @@ public class AmbiguousAnnotatedFactoryTest { @Diagnostic(type = SourceTargetMapperAndBarFactory.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.erroneous" + - ".ambiguousannotatedfactorymethod.Bar: org.mapstruct.ap.test.erroneous" + - ".ambiguousannotatedfactorymethod.Bar createBar(org.mapstruct.ap.test.erroneous" + - ".ambiguousannotatedfactorymethod.Foo foo), org.mapstruct.ap.test.erroneous" + - ".ambiguousannotatedfactorymethod.Bar org.mapstruct.ap.test.erroneous" + - ".ambiguousannotatedfactorymethod.AmbiguousBarFactory.createBar(org.mapstruct.ap.test.erroneous" + - ".ambiguousannotatedfactorymethod.Foo foo)." + + message = "Ambiguous factory methods found for creating Bar: " + + "Bar createBar(Foo foo), " + + "Bar AmbiguousBarFactory.createBar(Foo foo)." + " See https://mapstruct.org/faq/#ambiguous for more info.") } diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java index 682c46d036..dbce2ab5b2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java @@ -34,10 +34,9 @@ public class FactoryTest { @Diagnostic(type = SourceTargetMapperAndBarFactory.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.erroneous" + - ".ambiguousfactorymethod.Bar: org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar " + - "createBar(), org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar org.mapstruct.ap.test" + - ".erroneous.ambiguousfactorymethod.a.BarFactory.createBar()." + + message = "Ambiguous factory methods found for creating Bar: " + + "Bar createBar(), " + + "Bar BarFactory.createBar()." + " See https://mapstruct.org/faq/#ambiguous for more info.") } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java index a04373b13f..c13f1edd2f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java @@ -12,6 +12,7 @@ import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @IssueKey("2156") @@ -26,16 +27,14 @@ public class AmbigiousMapperTest { @Diagnostic(type = ErroneousWithAmbiguousMethodsMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Ambiguous mapping methods found for mapping property " - + "\"org.mapstruct.ap.test.erroneous.ambiguousmapping." - + "ErroneousWithAmbiguousMethodsMapper.LeafDTO branch.leaf\" to " - + "org.mapstruct.ap.test.erroneous.ambiguousmapping." - + "ErroneousWithAmbiguousMethodsMapper.LeafEntity: " - + "LeafEntity:map1(LeafDTO), LeafEntity:map2(LeafDTO). " - + "See https://mapstruct.org/faq/#ambiguous for more info.") + message = + "Ambiguous mapping methods found for mapping property " + + "\"ErroneousWithAmbiguousMethodsMapper.LeafDTO branch.leaf\" to ErroneousWithAmbiguousMethodsMapper.LeafEntity: " + + "ErroneousWithAmbiguousMethodsMapper.LeafEntity map1(ErroneousWithAmbiguousMethodsMapper.LeafDTO dto), " + + "ErroneousWithAmbiguousMethodsMapper.LeafEntity map2(ErroneousWithAmbiguousMethodsMapper.LeafDTO dto). " + + "See https://mapstruct.org/faq/#ambiguous for more info." ) } ) - public void testErrorMessageForAmbiguous() { } @@ -47,21 +46,48 @@ public void testErrorMessageForAmbiguous() { @Diagnostic(type = ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - message = "Ambiguous mapping methods found for mapping property " - + "\"org.mapstruct.ap.test.erroneous.ambiguousmapping." - + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO branch.leaf\" to " - + "org.mapstruct.ap.test.erroneous.ambiguousmapping." - + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity: " - + "LeafEntity:map1(LeafDTO), " - + "LeafEntity:map2(LeafDTO), " - + "LeafEntity:map3(LeafDTO), " - + "LeafEntity:map4(LeafDTO), " - + "LeafEntity:map5(LeafDTO)" - + "... and 1 more. " - + "See https://mapstruct.org/faq/#ambiguous for more info.") + message = + // CHECKSTYLE:OFF + "Ambiguous mapping methods found for mapping property \"ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO branch.leaf\" to " + + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity: " + + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map1(ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto), " + + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map2(ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto), " + + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map3(ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto), " + + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map4(ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto), " + + "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map5(ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto)" + + "... and 1 more. See https://mapstruct.org/faq/#ambiguous for more info." + // CHECKSTYLE:ON + ) } ) public void testErrorMessageForManyAmbiguous() { } + @Test + @ProcessorOption(name = "mapstruct.verbose", value = "true") + @WithClasses( ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + message = + // CHECKSTYLE:OFF + "Ambiguous mapping methods found for mapping property \"org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO branch.leaf\" " + + "to org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity: " + + "org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map1(org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto), " + + "org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map2(org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto), " + + "org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map3(org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto), " + + "org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map4(org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto), " + + "org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map5(org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto), " + + "org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity map6(org.mapstruct.ap.test.erroneous.ambiguousmapping.ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO dto). " + + "See https://mapstruct.org/faq/#ambiguous for more info." + // CHECKSTYLE:ON + ) + } + ) + public void testErrorMessageForManyAmbiguousVerbose() { + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java index f95614132c..bc36e2af42 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java @@ -43,8 +43,7 @@ public class ErroneousMappingsTest { @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 18, - message = "Unknown property \"bar\" in result type " + - "org.mapstruct.ap.test.erroneous.attributereference.Target. Did you mean \"foo\"?"), + message = "Unknown property \"bar\" in result type Target. Did you mean \"foo\"?"), @Diagnostic(type = ErroneousMapper.class, kind = Kind.ERROR, line = 23, diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java index db97dc133a..627478d1c0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java @@ -27,9 +27,8 @@ public class ErroneousPropertyMappingTest { diagnostics = { @Diagnostic(kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass " + - "source\" to \"java.lang.String property\". Consider to declare/implement a mapping method: " + - "\"java.lang.String map(org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass value)\".") + message = "Can't map property \"UnmappableClass source\" to \"String property\". " + + "Consider to declare/implement a mapping method: \"String map(UnmappableClass value)\".") } ) public void testUnmappableSourceProperty() { @@ -42,10 +41,8 @@ public void testUnmappableSourceProperty() { diagnostics = { @Diagnostic(kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass " + - "nameBasedSource\" to \"java.lang.String nameBasedSource\". Consider to declare/implement a " + - "mapping method: \"java.lang.String map(org.mapstruct.ap.test.erroneous.propertymapping" + - ".UnmappableClass value)\".") + message = "Can't map property \"UnmappableClass nameBasedSource\" to \"String nameBasedSource\"." + + " Consider to declare/implement a mapping method: \"String map(UnmappableClass value)\".") } ) public void testUnmappableSourcePropertyWithNoSourceDefinedInMapping() { @@ -58,8 +55,7 @@ public void testUnmappableSourcePropertyWithNoSourceDefinedInMapping() { diagnostics = { @Diagnostic(kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map \"java.lang.String \"constant\"\" to \"org.mapstruct.ap.test.erroneous" + - ".propertymapping.UnmappableClass constant\".") + message = "Can't map \"constant\" to \"UnmappableClass constant\".") } ) public void testUnmappableConstantAssignment() { @@ -72,9 +68,8 @@ public void testUnmappableConstantAssignment() { diagnostics = { @Diagnostic(kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass " + - "source\" to \"java.lang.String property\". Consider to declare/implement a mapping method: " + - "\"java.lang.String map(org.mapstruct.ap.test.erroneous.propertymapping.UnmappableClass value)\".") + message = "Can't map property \"UnmappableClass source\" to \"String property\". " + + "Consider to declare/implement a mapping method: \"String map(UnmappableClass value)\".") } ) public void testUnmappableParameterAssignment() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java index e1549ef4a8..417a0aa26a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java @@ -82,8 +82,7 @@ public void propertyIsIgnoredInReverseMappingWhenSourceIsAlsoSpecifiedICWIgnore( @Diagnostic(type = ErroneousTargetHasNoWriteAccessorMapper.class, kind = Kind.ERROR, line = 22, - message = "Property \"hasClaws\" has no write accessor in " + - "org.mapstruct.ap.test.ignore.PreditorDto.") + message = "Property \"hasClaws\" has no write accessor in PreditorDto.") } ) public void shouldGiveErrorOnMappingForReadOnlyProp() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java index e290aed450..f00319dcf7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java @@ -44,8 +44,8 @@ public void shouldMapAttributeFromSuperType() { type = ErroneousTargetSourceMapper.class, kind = Kind.ERROR, line = 16, - message = "Can't map property \"java.lang.CharSequence foo\" to \"java.lang.String foo\". Consider to " + - "declare/implement a mapping method: \"java.lang.String map(java.lang.CharSequence value)\"." + message = "Can't map property \"CharSequence foo\" to \"String foo\". " + + "Consider to declare/implement a mapping method: \"String map(CharSequence value)\"." )) public void shouldReportErrorDueToUnmappableAttribute() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java index 7c2459b118..d67734423e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java @@ -69,11 +69,10 @@ public void shouldMapAttributesWithSuperTypeUsingOtherMapper() { kind = Kind.ERROR, type = ErroneousSourceCompositeTargetCompositeMapper.class, line = 19, - message = "Ambiguous mapping methods found for mapping property " - + "\"org.mapstruct.ap.test.inheritance.complex.SourceExt prop1\" " - + "to org.mapstruct.ap.test.inheritance.complex.Reference: " - + "Reference:asReference(SourceBase), Reference:asReference(AdditionalFooSource). " - + "See https://mapstruct.org/faq/#ambiguous for more info.") + message = "Ambiguous mapping methods found for mapping property \"SourceExt prop1\" to Reference: " + + "Reference AdditionalMappingHelper.asReference(SourceBase source), " + + "Reference AdditionalMappingHelper.asReference(AdditionalFooSource source). " + + "See https://mapstruct.org/faq/#ambiguous for more info.") ) public void ambiguousMappingMethodsReportError() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java index 4cc6ce3e66..e82ed945a6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java @@ -60,12 +60,8 @@ public void shouldFailToGenerateImplementationBetweenStreamAndNonStreamOrIterabl @Diagnostic(type = ErroneousStreamToPrimitivePropertyMapper.class, kind = Kind.ERROR, line = 13, - message = - "Can't map property \"java.util.stream.Stream strings\" to \"int strings\". " - + - "Consider to declare/implement a mapping method: \"int map(java.util.stream.Stream" - + " value)\".") + message = "Can't map property \"Stream strings\" to \"int strings\". " + + "Consider to declare/implement a mapping method: \"int map(Stream value)\".") } ) public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { @@ -104,10 +100,9 @@ public void shouldFailOnEmptyIterableAnnotationStreamMappings() { @Diagnostic(type = ErroneousStreamToStreamNoElementMappingFound.class, kind = Kind.ERROR, line = 24, - message = "No target bean properties found: can't map Stream element \"org.mapstruct.ap.test" + - ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + - "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + - ".mapstruct.ap.test.WithProperties value)\".") + message = "No target bean properties found: " + + "can't map Stream element \"WithProperties withProperties\" to \"NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"NoProperties map(WithProperties value)\".") } ) public void shouldFailOnNoElementMappingFoundForStreamToStream() { @@ -122,9 +117,8 @@ public void shouldFailOnNoElementMappingFoundForStreamToStream() { @Diagnostic(type = ErroneousStreamToStreamNoElementMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 19, - message = "Can't map stream element \"java.text.AttributedString\" to \"java.lang.String \". Consider" + - " to declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)" + - "\".") + message = "Can't map stream element \"AttributedString\" to \"String \". " + + "Consider to declare/implement a mapping method: \"String map(AttributedString value)\".") } ) public void shouldFailOnNoElementMappingFoundForStreamToStreamWithDisabledAuto() { @@ -138,10 +132,9 @@ public void shouldFailOnNoElementMappingFoundForStreamToStreamWithDisabledAuto() @Diagnostic(type = ErroneousListToStreamNoElementMappingFound.class, kind = Kind.ERROR, line = 25, - message = "No target bean properties found: can't map Stream element \"org.mapstruct.ap.test" + - ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + - "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + - ".mapstruct.ap.test.WithProperties value)\".") + message = "No target bean properties found: " + + "can't map Stream element \"WithProperties withProperties\" to \"NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"NoProperties map(WithProperties value)\".") } ) public void shouldFailOnNoElementMappingFoundForListToStream() { @@ -156,9 +149,8 @@ public void shouldFailOnNoElementMappingFoundForListToStream() { @Diagnostic(type = ErroneousListToStreamNoElementMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 20, - message = "Can't map stream element \"java.text.AttributedString\" to \"java.lang.String \". Consider" + - " to declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)" + - "\".") + message = "Can't map stream element \"AttributedString\" to \"String \". " + + "Consider to declare/implement a mapping method: \"String map(AttributedString value)\".") } ) public void shouldFailOnNoElementMappingFoundForListToStreamWithDisabledAuto() { @@ -172,10 +164,9 @@ public void shouldFailOnNoElementMappingFoundForListToStreamWithDisabledAuto() { @Diagnostic(type = ErroneousStreamToListNoElementMappingFound.class, kind = Kind.ERROR, line = 25, - message = "No target bean properties found: can't map Stream element \"org.mapstruct.ap.test" + - ".WithProperties withProperties\" to \"org.mapstruct.ap.test.NoProperties noProperties\". " + - "Consider to declare/implement a mapping method: \"org.mapstruct.ap.test.NoProperties map(org" + - ".mapstruct.ap.test.WithProperties value)\".") + message = "No target bean properties found: " + + "can't map Stream element \"WithProperties withProperties\" to \"NoProperties noProperties\". " + + "Consider to declare/implement a mapping method: \"NoProperties map(WithProperties value)\".") } ) public void shouldFailOnNoElementMappingFoundForStreamToList() { @@ -190,9 +181,8 @@ public void shouldFailOnNoElementMappingFoundForStreamToList() { @Diagnostic(type = ErroneousStreamToListNoElementMappingFoundDisabledAuto.class, kind = Kind.ERROR, line = 20, - message = "Can't map stream element \"java.text.AttributedString\" to \"java.lang.String \". Consider" + - " to declare/implement a mapping method: \"java.lang.String map(java.text.AttributedString value)" + - "\".") + message = "Can't map stream element \"AttributedString\" to \"String \". " + + "Consider to declare/implement a mapping method: \"String map(AttributedString value)\".") } ) public void shouldFailOnNoElementMappingFoundForStreamToListWithDisabledAuto() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java index a996974cb7..bcd35e5b5e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java @@ -70,10 +70,9 @@ public void shouldForgeNewIterableMappingMethod() { @Diagnostic(type = ErroneousStreamNonMappableStreamMapper.class, kind = Kind.ERROR, line = 17, - message = "No target bean properties found: can't map Stream element \"org.mapstruct.ap.test" + - ".java8stream.forged.Foo nonMappableStream\" to \"org.mapstruct.ap.test.java8stream.forged.Bar " + - "nonMappableStream\". Consider to declare/implement a mapping method: \"org.mapstruct.ap.test" + - ".java8stream.forged.Bar map(org.mapstruct.ap.test.java8stream.forged.Foo value)\".") + message = "No target bean properties found: " + + "can't map Stream element \"Foo nonMappableStream\" to \"Bar nonMappableStream\". " + + "Consider to declare/implement a mapping method: \"Bar map(Foo value)\".") } ) public void shouldGenerateNonMappableMethodForSetMapping() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java index 5807931ce1..5324f3f2cd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java @@ -128,10 +128,8 @@ public void testDeepCloningListsAndMaps() { @Diagnostic(type = ErroneousDirectMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - message = "Can't map property \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\" to \"org" + - ".mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\". Consider to declare/implement a mapping " + - "method: \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO map(org.mapstruct.ap.test" + - ".mappingcontrol.ShelveDTO value)\"." + message = "Can't map property \"ShelveDTO shelve\" to \"ShelveDTO shelve\". " + + "Consider to declare/implement a mapping method: \"ShelveDTO map(ShelveDTO value)\"." ) }) public void directSelectionNotAllowed() { @@ -157,9 +155,8 @@ public void methodSelectionAllowed() { @Diagnostic(type = ErroneousMethodMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - message = "Can't map property \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\" to \"int " + - "beerCount\". Consider to declare/implement a mapping method: \"int map(org.mapstruct.ap.test" + - ".mappingcontrol.ShelveDTO value)\"." + message = "Can't map property \"ShelveDTO shelve\" to \"int beerCount\". " + + "Consider to declare/implement a mapping method: \"int map(ShelveDTO value)\"." ) }) public void methodSelectionNotAllowed() { @@ -184,8 +181,8 @@ public void conversionSelectionAllowed() { @Diagnostic(type = ErroneousConversionMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"java.lang.String beerCount\" to \"int beerCount\". Consider to " + - "declare/implement a mapping method: \"int map(java.lang.String value)\"." + message = "Can't map property \"String beerCount\" to \"int beerCount\". " + + "Consider to declare/implement a mapping method: \"int map(String value)\"." ) }) public void conversionSelectionNotAllowed() { @@ -210,9 +207,8 @@ public void complexSelectionAllowed() { @Diagnostic(type = ErroneousComplexMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - message = "Can't map property \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\" to \"int " + - "beerCount\". Consider to declare/implement a mapping method: \"int map(org.mapstruct.ap.test" + - ".mappingcontrol.ShelveDTO value)\"." + message = "Can't map property \"ShelveDTO shelve\" to \"int beerCount\". " + + "Consider to declare/implement a mapping method: \"int map(ShelveDTO value)\"." ) }) public void complexSelectionNotAllowed() { @@ -225,9 +221,8 @@ public void complexSelectionNotAllowed() { @Diagnostic(type = ErroneousComplexMapperWithConfig.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - message = "Can't map property \"org.mapstruct.ap.test.mappingcontrol.ShelveDTO shelve\" to \"int " + - "beerCount\". Consider to declare/implement a mapping method: \"int map(org.mapstruct.ap.test" + - ".mappingcontrol.ShelveDTO value)\"." + message = "Can't map property \"ShelveDTO shelve\" to \"int beerCount\". " + + "Consider to declare/implement a mapping method: \"int map(ShelveDTO value)\"." ) }) public void complexSelectionNotAllowedWithConfig() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java index 94b2fa16a4..f9ad0b435b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java @@ -49,8 +49,7 @@ public void testAgeSuggestion() { @Diagnostic(type = PersonNameMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - message = "Unknown property \"fulName\" in result type org.mapstruct.ap.test.namesuggestion.Person. " + - "Did you mean \"fullName\"?") + message = "Unknown property \"fulName\" in result type Person. Did you mean \"fullName\"?") } ) public void testNameSuggestion() { @@ -66,8 +65,8 @@ public void testNameSuggestion() { @Diagnostic(type = PersonGarageWrongTargetMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, - message = "Unknown property \"colour\" in type org.mapstruct.ap.test.namesuggestion.Garage for target" + - " name \"garage.colour.rgb\". Did you mean \"garage.color\"?"), + message = "Unknown property \"colour\" in type Garage for target name \"garage.colour.rgb\". " + + "Did you mean \"garage.color\"?"), @Diagnostic(type = PersonGarageWrongTargetMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 20, @@ -75,8 +74,8 @@ public void testNameSuggestion() { @Diagnostic(type = PersonGarageWrongTargetMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 22, - message = "Unknown property \"colour\" in type org.mapstruct.ap.test.namesuggestion.Garage for" + - " target name \"garage.colour\". Did you mean \"garage.color\"?"), + message = "Unknown property \"colour\" in type Garage for target name \"garage.colour\". " + + "Did you mean \"garage.color\"?"), @Diagnostic(type = PersonGarageWrongTargetMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 23, diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java index 434ad225a6..bc9bded0cc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java @@ -33,9 +33,8 @@ public class DisablingNestedSimpleBeansMappingTest { @Diagnostic(type = ErroneousDisabledHouseMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.Roof roof\" to \"org.mapstruct.ap" + - ".test.nestedbeans.RoofDto roof\". Consider to declare/implement a mapping method: \"org" + - ".mapstruct.ap.test.nestedbeans.RoofDto map(org.mapstruct.ap.test.nestedbeans.Roof value)\"." + message = "Can't map property \"Roof roof\" to \"RoofDto roof\". " + + "Consider to declare/implement a mapping method: \"RoofDto map(Roof value)\"." ) }) @Test @@ -51,9 +50,8 @@ public void shouldUseDisabledMethodGenerationOnMapper() { @Diagnostic(type = ErroneousDisabledViaConfigHouseMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.Roof roof\" to \"org.mapstruct.ap" + - ".test.nestedbeans.RoofDto roof\". Consider to declare/implement a mapping method: \"org" + - ".mapstruct.ap.test.nestedbeans.RoofDto map(org.mapstruct.ap.test.nestedbeans.Roof value)\"." + message = "Can't map property \"Roof roof\" to \"RoofDto roof\". " + + "Consider to declare/implement a mapping method: \"RoofDto map(Roof value)\"." ) }) @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java index b1f58967ff..3117f17fc9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java @@ -100,8 +100,7 @@ public class DottedErrorMessageTest { kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, message = "Unmapped target property: \"rgb\". Mapping from " + PROPERTY + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Color house.roof.color\" to \"org.mapstruct.ap" + - ".test.nestedbeans.unmappable.ColorDto house.roof.color\".") + " \"Color house.roof.color\" to \"ColorDto house.roof.color\".") } ) public void testDeepNestedBeans() { @@ -118,8 +117,7 @@ public void testDeepNestedBeans() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, message = "Unmapped target property: \"left\". Mapping from " + COLLECTION_ELEMENT + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Wheel car.wheels\" to \"org.mapstruct.ap.test" + - ".nestedbeans.unmappable.WheelDto car.wheels\".") + " \"Wheel car.wheels\" to \"WheelDto car.wheels\".") } ) public void testIterables() { @@ -136,8 +134,7 @@ public void testIterables() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_KEY + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Word dictionary.wordMap{:key}\" to \"org" + - ".mapstruct.ap.test.nestedbeans.unmappable.WordDto dictionary.wordMap{:key}\".") + " \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\".") } ) public void testMapKeys() { @@ -154,8 +151,7 @@ public void testMapKeys() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_VALUE + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWord dictionary.wordMap{:value}\" " + - "to \"org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWordDto dictionary.wordMap{:value}\".") + " \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\".") } ) public void testMapValues() { @@ -172,8 +168,7 @@ public void testMapValues() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Info computers[].info\" to \"org.mapstruct.ap" + - ".test.nestedbeans.unmappable.InfoDto computers[].info\".") + " \"Info computers[].info\" to \"InfoDto computers[].info\".") } ) public void testCollectionElementProperty() { @@ -190,8 +185,7 @@ public void testCollectionElementProperty() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 10, message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Info catNameMap{:value}.info\" to \"org" + - ".mapstruct.ap.test.nestedbeans.unmappable.InfoDto catNameMap{:value}.info\".") + " \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\".") } ) public void testMapValueProperty() { @@ -208,10 +202,8 @@ public void testMapValueProperty() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 25, message = - "The following constants from the property \"org.mapstruct.ap.test.nestedbeans.unmappable" + - ".RoofType house.roof.type\" enum " + - "have no corresponding constant in the \"org.mapstruct.ap.test.nestedbeans.unmappable" + - ".ExternalRoofType house.roof.type\" enum and must " + + "The following constants from the property \"RoofType house.roof.type\" enum " + + "have no corresponding constant in the \"ExternalRoofType house.roof.type\" enum and must " + "be be mapped via adding additional mappings: NORMAL." ) } @@ -235,38 +227,32 @@ public void testMapEnumProperty() { kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, message = "Unmapped target property: \"rgb\". Mapping from " + PROPERTY + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Color house.roof.color\" to \"org.mapstruct.ap" + - ".test.nestedbeans.unmappable.ColorDto house.roof.color\"."), + " \"Color house.roof.color\" to \"ColorDto house.roof.color\"."), @Diagnostic(type = BaseDeepListMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, message = "Unmapped target property: \"left\". Mapping from " + COLLECTION_ELEMENT + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Wheel car.wheels\" to \"org.mapstruct.ap.test" + - ".nestedbeans.unmappable.WheelDto car.wheels\"."), + " \"Wheel car.wheels\" to \"WheelDto car.wheels\"."), @Diagnostic(type = BaseDeepMapKeyMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_KEY + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Word dictionary.wordMap{:key}\" to \"org" + - ".mapstruct.ap.test.nestedbeans.unmappable.WordDto dictionary.wordMap{:key}\"."), + " \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\"."), @Diagnostic(type = BaseDeepMapValueMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_VALUE + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWord dictionary.wordMap{:value}\" " + - "to \"org.mapstruct.ap.test.nestedbeans.unmappable.ForeignWordDto dictionary.wordMap{:value}\"."), + " \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\"."), @Diagnostic(type = BaseCollectionElementPropertyMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Info computers[].info\" to \"org.mapstruct.ap" + - ".test.nestedbeans.unmappable.InfoDto computers[].info\"."), + " \"Info computers[].info\" to \"InfoDto computers[].info\"."), @Diagnostic(type = BaseValuePropertyMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 10, message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + - " \"org.mapstruct.ap.test.nestedbeans.unmappable.Info catNameMap{:value}.info\" to \"org" + - ".mapstruct.ap.test.nestedbeans.unmappable.InfoDto catNameMap{:value}.info\".") + " \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\".") } ) public void testWarnUnmappedTargetProperties() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java index 267dfb438c..19abea74b3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java @@ -31,31 +31,25 @@ public class ErroneousJavaInternalTest { @Diagnostic(type = ErroneousJavaInternalMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.exclusions.Source.MyType date\" to " + - "\"java.util.Date date\". Consider to declare/implement a mapping method: \"java.util.Date map" + - "(org.mapstruct.ap.test.nestedbeans.exclusions.Source.MyType value)\"."), + message = "Can't map property \"Source.MyType date\" to \"Date date\". " + + "Consider to declare/implement a mapping method: \"Date map(Source.MyType value)\"."), @Diagnostic(type = ErroneousJavaInternalMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.exclusions.Source.MyType calendar\"" + - " to \"java.util.GregorianCalendar calendar\". Consider to declare/implement a mapping method: " + - "\"java.util.GregorianCalendar map(org.mapstruct.ap.test.nestedbeans.exclusions.Source.MyType " + - "value)\"."), + message = "Can't map property \"Source.MyType calendar\" to \"GregorianCalendar calendar\". " + + "Consider to declare/implement a mapping method: \"GregorianCalendar map(Source.MyType value)\"."), @Diagnostic(type = ErroneousJavaInternalMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"java.util.List types\" to \"java.util.List types\". Consider to declare/implement a " + - "mapping method: \"java.util.List map(java.util.List value)\"."), + message = "Can't map property \"List types\" to \"List types\". " + + "Consider to declare/implement a mapping method: \"List map(List value)\"."), @Diagnostic(type = ErroneousJavaInternalMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "Can't map property \"java.util.List nestedMyType.deepNestedType.types\" to \"java.util.List nestedMyType" + - ".deepNestedType.types\". Consider to declare/implement a mapping method: \"java.util.List map(java.util.List " + - "value)\".") + message = "Can't map property \"List nestedMyType.deepNestedType.types\" to " + + "\"List nestedMyType.deepNestedType.types\". " + + "Consider to declare/implement a mapping method: " + + "\"List map(List value)\".") }) @Test public void shouldNotNestIntoJavaPackageObjects() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java index 1eccfdb4d7..45cc9a2343 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java @@ -33,11 +33,8 @@ public class ErroneousCustomExclusionTest { @Diagnostic(type = ErroneousCustomExclusionMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, - message = "Can't map property \"org.mapstruct.ap.test.nestedbeans.exclusions.custom.Source" + - ".NestedSource nested\" to \"org.mapstruct.ap.test.nestedbeans.exclusions.custom.Target" + - ".NestedTarget nested\". Consider to declare/implement a mapping method: \"org.mapstruct.ap.test" + - ".nestedbeans.exclusions.custom.Target.NestedTarget map(org.mapstruct.ap.test.nestedbeans" + - ".exclusions.custom.Source.NestedSource value)\".") + message = "Can't map property \"Source.NestedSource nested\" to \"Target.NestedTarget nested\". " + + "Consider to declare/implement a mapping method: \"Target.NestedTarget map(Source.NestedSource value)\".") } ) @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java index f9d5b11eed..07d0a38611 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java @@ -171,8 +171,7 @@ public void shouldUseGetAsTargetAccessor() { @Diagnostic(type = ArtistToChartEntryErroneous.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 34, - message = "org.mapstruct.ap.test.nestedsourceproperties.ArtistToChartEntryErroneous.ChartPosition " + - "does not have an accessible constructor.") + message = "ArtistToChartEntryErroneous.ChartPosition does not have an accessible constructor.") } ) @WithClasses({ ArtistToChartEntryErroneous.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java index b508787cc7..9d17f1725b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java @@ -85,12 +85,9 @@ public void shouldApplyGenericTypeMapper() { diagnostics = { @Diagnostic(type = ErroneousSourceTargetMapper1.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + - ".generics.UpperBoundWrapper " + - "fooUpperBoundFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeA " + - "fooUpperBoundFailure\". Consider to declare/implement a mapping method: \"org.mapstruct.ap.test" + - ".selection.generics.TypeA map(org.mapstruct.ap.test.selection.generics.UpperBoundWrapper value)\".") + message = "No target bean properties found: can't map property " + + "\"UpperBoundWrapper fooUpperBoundFailure\" to \"TypeA fooUpperBoundFailure\". " + + "Consider to declare/implement a mapping method: \"TypeA map(UpperBoundWrapper value)\".") }) public void shouldFailOnUpperBound() { } @@ -102,12 +99,11 @@ public void shouldFailOnUpperBound() { @Diagnostic(type = ErroneousSourceTargetMapper2.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + - ".generics.WildCardExtendsWrapper " + - "fooWildCardExtendsTypeAFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeA " + - "fooWildCardExtendsTypeAFailure\". Consider to declare/implement a mapping method: \"org" + - ".mapstruct.ap.test.selection.generics.TypeA map(org.mapstruct.ap.test.selection.generics" + - ".WildCardExtendsWrapper value)\".") + message = "No target bean properties found: can't map property " + + "\"WildCardExtendsWrapper fooWildCardExtendsTypeAFailure\" to " + + "\"TypeA fooWildCardExtendsTypeAFailure\". " + + "Consider to declare/implement a mapping method: " + + "\"TypeA map(WildCardExtendsWrapper value)\".") }) public void shouldFailOnWildCardBound() { } @@ -119,12 +115,11 @@ public void shouldFailOnWildCardBound() { @Diagnostic(type = ErroneousSourceTargetMapper3.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + - ".generics.WildCardExtendsMBWrapper " + - "fooWildCardExtendsMBTypeBFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeB " + - "fooWildCardExtendsMBTypeBFailure\". Consider to declare/implement a mapping method: \"org" + - ".mapstruct.ap.test.selection.generics.TypeB map(org.mapstruct.ap.test.selection.generics" + - ".WildCardExtendsMBWrapper value)\".") + message = "No target bean properties found: can't map property " + + "\"WildCardExtendsMBWrapper fooWildCardExtendsMBTypeBFailure\" to " + + "\"TypeB fooWildCardExtendsMBTypeBFailure\". " + + "Consider to declare/implement a mapping method: " + + "\"TypeB map(WildCardExtendsMBWrapper value)\".") }) public void shouldFailOnWildCardMultipleBounds() { } @@ -136,12 +131,11 @@ public void shouldFailOnWildCardMultipleBounds() { @Diagnostic(type = ErroneousSourceTargetMapper4.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + - ".generics.WildCardSuperWrapper " + - "fooWildCardSuperTypeAFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeA " + - "fooWildCardSuperTypeAFailure\". Consider to declare/implement a mapping method: \"org.mapstruct" + - ".ap.test.selection.generics.TypeA map(org.mapstruct.ap.test.selection.generics" + - ".WildCardSuperWrapper value)\".") + message = "No target bean properties found: can't map property " + + "\"WildCardSuperWrapper fooWildCardSuperTypeAFailure\" to " + + "\"TypeA fooWildCardSuperTypeAFailure\". " + + "Consider to declare/implement a mapping method: " + + "\"TypeA map(WildCardSuperWrapper value)\".") }) public void shouldFailOnSuperBounds1() { } @@ -153,12 +147,9 @@ public void shouldFailOnSuperBounds1() { @Diagnostic(type = ErroneousSourceTargetMapper5.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 16, - message = "No target bean properties found: can't map property \"org.mapstruct.ap.test.selection" + - ".generics.WildCardSuperWrapper " + - "fooWildCardSuperTypeCFailure\" to \"org.mapstruct.ap.test.selection.generics.TypeC " + - "fooWildCardSuperTypeCFailure\". Consider to declare/implement a mapping method: \"org.mapstruct" + - ".ap.test.selection.generics.TypeC map(org.mapstruct.ap.test.selection.generics" + - ".WildCardSuperWrapper value)\".") + message = "No target bean properties found: can't map property \"WildCardSuperWrapper " + + "fooWildCardSuperTypeCFailure\" to \"TypeC fooWildCardSuperTypeCFailure\". " + + "Consider to declare/implement a mapping method: \"TypeC map(WildCardSuperWrapper value)\".") }) public void shouldFailOnSuperBounds2() { } @@ -172,13 +163,10 @@ public void shouldFailOnSuperBounds2() { }) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = ErroneousSourceTargetMapper6.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 17, message = - "No target bean properties found: can't map property \"org.mapstruct.ap.test.NoProperties " - + "foo.wrapped\" to" - + " \"org.mapstruct.ap.test.selection.generics.TypeA " - + "foo.wrapped\". Consider to declare/implement a mapping method: \"org" + - ".mapstruct.ap.test.selection.generics.TypeA map(org.mapstruct.ap.test.NoProperties " + - "value)\".") + line = 17, + message = "No target bean properties found: can't map property " + + "\"NoProperties foo.wrapped\" to \"TypeA foo.wrapped\". " + + "Consider to declare/implement a mapping method: \"TypeA map(NoProperties value)\".") }) public void shouldFailOnNonMatchingWildCards() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java index 37ecfb1be6..17c3c77863 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java @@ -109,9 +109,8 @@ public void shouldMatchClassAndMethod() { type = ErroneousMapper.class, kind = Kind.ERROR, line = 28, - message = - "Can't map property \"java.lang.String title\" to \"java.lang.String title\". Consider to " + - "declare/implement a mapping method: \"java.lang.String map(java.lang.String value)\".") + message = "Can't map property \"String title\" to \"String title\". " + + "Consider to declare/implement a mapping method: \"String map(String value)\".") } ) public void shouldNotProduceMatchingMethod() { @@ -188,9 +187,8 @@ public void testFactorySelectionWithQualifier() { @Diagnostic(type = ErroneousMovieFactoryMapper.class, kind = Kind.ERROR, line = 24, - message = "The return type org.mapstruct.ap.test.selection.qualifier.bean.AbstractEntry is an " + - "abstract class or interface. Provide a non abstract / non interface result type or a factory " + - "method.") + message = "The return type AbstractEntry is an abstract class or interface. " + + "Provide a non abstract / non interface result type or a factory method.") } ) public void testEmptyBeanMapping() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java index 9db58ec201..388532bbe8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java @@ -45,11 +45,10 @@ public class InheritanceSelectionTest { @Diagnostic(type = ErroneousFruitMapper.class, kind = Kind.ERROR, line = 23, - message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.selection.resulttype" + - ".Fruit: org.mapstruct.ap.test.selection.resulttype.Apple org.mapstruct.ap.test.selection" + - ".resulttype.ConflictingFruitFactory.createApple(), org.mapstruct.ap.test.selection.resulttype" + - ".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana()." + - " See https://mapstruct.org/faq/#ambiguous for more info." + message = "Ambiguous factory methods found for creating Fruit: " + + "Apple ConflictingFruitFactory.createApple(), " + + "Banana ConflictingFruitFactory.createBanana(). " + + "See https://mapstruct.org/faq/#ambiguous for more info." ) } ) @@ -65,8 +64,8 @@ public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() @Diagnostic(type = ErroneousResultTypeNoAccessibleConstructorMapper.class, kind = Kind.ERROR, line = 18, - message = "org.mapstruct.ap.test.selection.resulttype" + - ".ErroneousResultTypeNoAccessibleConstructorMapper.Banana does not have an accessible constructor.") + message = + "ErroneousResultTypeNoAccessibleConstructorMapper.Banana does not have an accessible constructor.") } ) public void testResultTypeHasNoSuitableAccessibleConstructor() { @@ -112,8 +111,8 @@ public void testResultTypeBasedConstructionOfResultForInterface() { @Diagnostic(type = ResultTypeConstructingFruitInterfaceErroneousMapper.class, kind = Kind.ERROR, line = 23, - message = "The return type org.mapstruct.ap.test.selection.resulttype.IsFruit is an abstract class or" + - " interface. Provide a non abstract / non interface result type or a factory method." + message = "The return type IsFruit is an abstract class or interface. " + + "Provide a non abstract / non interface result type or a factory method." ) } ) @@ -129,8 +128,7 @@ public void testResultTypeBasedConstructionOfResultForInterfaceErroneous() { @Diagnostic(type = ErroneousFruitMapper2.class, kind = Kind.ERROR, line = 22, - message = "org.mapstruct.ap.test.selection.resulttype.Banana not assignable to: org.mapstruct.ap.test" + - ".selection.resulttype.Apple.") + message = "Banana not assignable to: Apple.") } ) @IssueKey("434") diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java index e22150fe36..27c33d3bd7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java @@ -17,17 +17,19 @@ public class TwoStepMappingTest { @Test - @WithClasses( ErroneousMapperMM.class ) - @ExpectedCompilationOutcome( value = CompilationResult.FAILED, + @WithClasses(ErroneousMapperMM.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = @Diagnostic( kind = javax.tools.Diagnostic.Kind.ERROR, type = ErroneousMapperMM.class, line = 16, - message = "Ambiguous 2step methods found, mapping SourceType s to TargetType. " - + "Found methodY( methodX ( parameter ) ): " - + "method(s)Y: TargetType:methodY1(TypeInTheMiddleA), methodX: TypeInTheMiddleA:methodX1(SourceType); " - + "method(s)Y: TargetType:methodY2(TypeInTheMiddleB), methodX: TypeInTheMiddleB:methodX2(SourceType); ." - ) ) + message = "Ambiguous 2step methods found, mapping SourceType s to TargetType. " + + "Found methodY( methodX ( parameter ) ): " + + "method(s)Y: ErroneousMapperMM.TargetType methodY1(ErroneousMapperMM.TypeInTheMiddleA s), " + + "methodX: ErroneousMapperMM.TypeInTheMiddleA methodX1(ErroneousMapperMM.SourceType s); " + + "method(s)Y: ErroneousMapperMM.TargetType methodY2(ErroneousMapperMM.TypeInTheMiddleB s), " + + "methodX: ErroneousMapperMM.TypeInTheMiddleB methodX2(ErroneousMapperMM.SourceType s); ." + )) public void methodAndMethodTest() { } @@ -39,10 +41,10 @@ public void methodAndMethodTest() { kind = javax.tools.Diagnostic.Kind.ERROR, type = ErroneousMapperCM.class, line = 18, - message = "Ambiguous 2step methods found, mapping BigDecimal s to TargetType. " - + "Found methodY( conversionX ( parameter ) ): " - + "method(s)Y: TargetType:methodY1(String), conversionX: BigDecimal-->String; " - + "method(s)Y: TargetType:methodY2(Double), conversionX: BigDecimal-->Double; ." + message = "Ambiguous 2step methods found, mapping BigDecimal s to TargetType. " + + "Found methodY( conversionX ( parameter ) ): " + + "method(s)Y: ErroneousMapperCM.TargetType methodY1(String s), conversionX: BigDecimal-->String; " + + "method(s)Y: ErroneousMapperCM.TargetType methodY2(Double d), conversionX: BigDecimal-->Double; ." ), @Diagnostic( kind = javax.tools.Diagnostic.Kind.ERROR, @@ -62,10 +64,10 @@ public void conversionAndMethodTest() { kind = javax.tools.Diagnostic.Kind.ERROR, type = ErroneousMapperMC.class, line = 18, - message = "Ambiguous 2step methods found, mapping SourceType s to String. " - + "Found conversionY( methodX ( parameter ) ): " - + "conversionY: BigDecimal-->String, method(s)X: BigDecimal:methodX1(SourceType); " - + "conversionY: Double-->String, method(s)X: Double:methodX2(SourceType); ." + message = "Ambiguous 2step methods found, mapping SourceType s to String. " + + "Found conversionY( methodX ( parameter ) ): " + + "conversionY: BigDecimal-->String, method(s)X: BigDecimal methodX1(ErroneousMapperMC.SourceType s); " + + "conversionY: Double-->String, method(s)X: Double methodX2(ErroneousMapperMC.SourceType s); ." ), @Diagnostic( kind = javax.tools.Diagnostic.Kind.ERROR, diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java index 98a6970598..ce386ab7b0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java @@ -70,38 +70,33 @@ public void testNumericConstants() { @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 25, - message = "Can't map \"java.lang.String \"zz\"\" to \"boolean booleanValue\". Reason: only 'true' or " + - "'false' are supported."), + message = + "Can't map \"zz\" to \"boolean booleanValue\". Reason: only 'true' or 'false' are supported."), @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 26, - message = "Can't map \"java.lang.String \"'ba'\"\" to \"char charValue\". Reason: invalid character " + - "literal."), + message = "Can't map \"'ba'\" to \"char charValue\". Reason: invalid character literal."), @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 27, - message = "Can't map \"java.lang.String \"200\"\" to \"byte byteValue\". Reason: Value out of range. " + - "Value:\"200\" Radix:10."), + message = + "Can't map \"200\" to \"byte byteValue\". Reason: Value out of range. Value:\"200\" Radix:10."), @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 28, - message = "Can't map \"java.lang.String \"0xFFFF_FFFF_FFFF\"\" to \"int intValue\". Reason: integer " + - "number too large."), + message = "Can't map \"0xFFFF_FFFF_FFFF\" to \"int intValue\". Reason: integer number too large."), @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 29, - message = "Can't map \"java.lang.String \"1\"\" to \"long longValue\". Reason: L/l mandatory for long" + - " types."), + message = "Can't map \"1\" to \"long longValue\". Reason: L/l mandatory for long types."), @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 30, - message = "Can't map \"java.lang.String \"1.40e-_45f\"\" to \"float floatValue\". Reason: improperly " + - "placed underscores."), + message = "Can't map \"1.40e-_45f\" to \"float floatValue\". Reason: improperly placed underscores."), @Diagnostic(type = ErroneousConstantMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 31, - message = "Can't map \"java.lang.String \"1e-137000\"\" to \"double doubleValue\". Reason: floating " + - "point number too small.") + message = "Can't map \"1e-137000\" to \"double doubleValue\". Reason: floating point number too small.") } ) public void miscellaneousDetailMessages() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java index 7e7bc2d69e..3b307c233c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java @@ -192,13 +192,11 @@ public void shouldMapSameSourcePropertyToSeveralTargetPropertiesFromSeveralSourc @Diagnostic(type = ErroneousMapper5.class, kind = Kind.ERROR, line = 28, - message = "Constant \"DENMARK\" doesn't exist in enum type org.mapstruct.ap.test.source." - + "constants.CountryEnum for property \"country\"."), + message = "Constant \"DENMARK\" doesn't exist in enum type CountryEnum for property \"country\"."), @Diagnostic(type = ErroneousMapper5.class, kind = Kind.ERROR, line = 28, - message = "Can't map \"java.lang.String \"DENMARK\"\" to \"org.mapstruct.ap.test.source." - + "constants.CountryEnum country\".") + message = "Can't map \"DENMARK\" to \"CountryEnum country\".") } ) public void errorOnNonExistingEnumConstant() throws ParseException { @@ -219,8 +217,7 @@ public void errorOnNonExistingEnumConstant() throws ParseException { @Diagnostic(type = ErroneousMapper6.class, kind = Kind.ERROR, line = 25, - message = "Can't map \"java.lang.String \"3001\"\" to \"java.lang.Long " - + "longWrapperConstant\". Reason: L/l mandatory for long types.") + message = "Can't map \"3001\" to \"Long longWrapperConstant\". Reason: L/l mandatory for long types.") } ) public void cannotMapIntConstantToLong() throws ParseException { diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java index 1b93d04a53..d7e5bdea43 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java @@ -175,8 +175,7 @@ public void testShouldFailOnPropertyMappingNoPropertyGetter() { } @Diagnostic(type = ErroneousOrganizationMapper2.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 35, - message = "org.mapstruct.ap.test.updatemethods.ErroneousOrganizationMapper2.DepartmentEntity does not" + - " have an accessible constructor.") + message = "ErroneousOrganizationMapper2.DepartmentEntity does not have an accessible constructor.") }) public void testShouldFailOnConstantMappingNoPropertyGetter() { @@ -196,9 +195,8 @@ public void testShouldFailOnConstantMappingNoPropertyGetter() { @Diagnostic(type = CompanyMapper1.class, kind = javax.tools.Diagnostic.Kind.WARNING, line = 23, - message = "Unmapped target property: \"employees\". Mapping from property \"org.mapstruct.ap.test" + - ".updatemethods.UnmappableDepartmentDto department\" to \"org.mapstruct.ap.test.updatemethods" + - ".DepartmentEntity department\".") + message = "Unmapped target property: \"employees\". " + + "Mapping from property \"UnmappableDepartmentDto department\" to \"DepartmentEntity department\".") } ) public void testShouldNotUseTwoNestedUpdateMethods() { From 609824037b21693da664863a3491b2c951fad21d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 1 Aug 2020 11:43:46 +0200 Subject: [PATCH 084/597] #2167 Add missing @since 1.4 to new interfaces, classes and methods --- core/src/main/java/org/mapstruct/MapperConfig.java | 2 ++ core/src/main/java/org/mapstruct/control/MappingControl.java | 2 ++ core/src/main/java/org/mapstruct/control/MappingControls.java | 2 ++ .../java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java | 2 ++ .../src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java | 2 ++ .../org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java | 2 ++ .../mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java | 2 ++ .../mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java | 2 ++ .../org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java | 2 ++ 9 files changed, 18 insertions(+) diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java index 326041164f..24951fb348 100644 --- a/core/src/main/java/org/mapstruct/MapperConfig.java +++ b/core/src/main/java/org/mapstruct/MapperConfig.java @@ -83,6 +83,8 @@ * their simple name rather than their fully-qualified name. * * @return classes to add in the imports of the generated implementation. + * + * @since 1.4 */ Class[] imports() default { }; diff --git a/core/src/main/java/org/mapstruct/control/MappingControl.java b/core/src/main/java/org/mapstruct/control/MappingControl.java index 975ff52e90..1d416dafd7 100644 --- a/core/src/main/java/org/mapstruct/control/MappingControl.java +++ b/core/src/main/java/org/mapstruct/control/MappingControl.java @@ -87,6 +87,8 @@ * * * @author Sjaak Derksen + * + * @since 1.4 */ @Retention(RetentionPolicy.CLASS) @Repeatable(MappingControls.class) diff --git a/core/src/main/java/org/mapstruct/control/MappingControls.java b/core/src/main/java/org/mapstruct/control/MappingControls.java index d3b4fe1556..a3efef661f 100644 --- a/core/src/main/java/org/mapstruct/control/MappingControls.java +++ b/core/src/main/java/org/mapstruct/control/MappingControls.java @@ -14,6 +14,8 @@ * Allows multiple {@link MappingControl} on a class declaration. * * @author Sjaak Derksen + * + * @since 1.4 */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.ANNOTATION_TYPE) diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java index 526d0a24f2..a54df7bc06 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java @@ -11,6 +11,8 @@ /** * @author Filip Hrisafov + * + * @since 1.4 */ public class DefaultEnumNamingStrategy implements EnumNamingStrategy { diff --git a/processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java index c135cc9ab2..a2361eefd0 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java @@ -13,6 +13,8 @@ * A service provider interface for the mapping between different enum constants * * @author Arne Seime + * + * @since 1.4 */ @Experimental("This SPI can have it's signature changed in subsequent releases") public interface EnumNamingStrategy { diff --git a/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java index eaa7b75512..abb6e9cb4f 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java @@ -7,6 +7,8 @@ /** * @author Filip Hrisafov + * + * @since 1.4 */ public class PrefixEnumTransformationStrategy implements EnumTransformationStrategy { diff --git a/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java index d7f03c9b7b..d3f0c515b3 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java @@ -7,6 +7,8 @@ /** * @author Filip Hrisafov + * + * @since 1.4 */ public class StripPrefixEnumTransformationStrategy implements EnumTransformationStrategy { diff --git a/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java index 223837924e..cf239600d4 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java @@ -7,6 +7,8 @@ /** * @author Filip Hrisafov + * + * @since 1.4 */ public class StripSuffixEnumTransformationStrategy implements EnumTransformationStrategy { diff --git a/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java index 29a109fadf..4cd92b9af8 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java @@ -7,6 +7,8 @@ /** * @author Filip Hrisafov + * + * @since 1.4 */ public class SuffixEnumTransformationStrategy implements EnumTransformationStrategy { From ed16d62a91374553059a8296abf8ec297d9a34dd Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 1 Aug 2020 09:02:29 +0200 Subject: [PATCH 085/597] #2170 Make sure that an import is created for constructor mapping defined variables --- .../ap/internal/model/BeanMappingMethod.java | 4 ++ .../ap/test/bugs/_2170/Issue2170Test.java | 55 +++++++++++++++++++ .../ap/test/bugs/_2170/dto/AddressDto.java | 31 +++++++++++ .../ap/test/bugs/_2170/dto/PersonDto.java | 23 ++++++++ .../ap/test/bugs/_2170/entity/Address.java | 29 ++++++++++ .../ap/test/bugs/_2170/entity/Person.java | 21 +++++++ .../test/bugs/_2170/mapper/AddressMapper.java | 23 ++++++++ .../test/bugs/_2170/mapper/EntityMapper.java | 21 +++++++ .../test/bugs/_2170/mapper/PersonMapper.java | 18 ++++++ 9 files changed, 225 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/Issue2170Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/dto/AddressDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/dto/PersonDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/entity/Address.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/entity/Person.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/AddressMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/EntityMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/PersonMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 3d86a59299..b0562373ff 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1488,6 +1488,10 @@ public Set getImportTypes() { for ( PropertyMapping propertyMapping : propertyMappings ) { types.addAll( propertyMapping.getImportTypes() ); + if ( propertyMapping.isConstructorMapping() ) { + // We need to add the target type imports for a constructor mapper since we define its parameters + types.addAll( propertyMapping.getTargetType().getImportTypes() ); + } } if ( returnTypeToConstruct != null ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/Issue2170Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/Issue2170Test.java new file mode 100644 index 0000000000..779cc7fd03 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/Issue2170Test.java @@ -0,0 +1,55 @@ +/* + * 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._2170; + +import java.util.Collections; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.bugs._2170.dto.AddressDto; +import org.mapstruct.ap.test.bugs._2170.dto.PersonDto; +import org.mapstruct.ap.test.bugs._2170.entity.Address; +import org.mapstruct.ap.test.bugs._2170.entity.Person; +import org.mapstruct.ap.test.bugs._2170.mapper.AddressMapper; +import org.mapstruct.ap.test.bugs._2170.mapper.EntityMapper; +import org.mapstruct.ap.test.bugs._2170.mapper.PersonMapper; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2170") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Address.class, + Person.class, + AddressDto.class, + PersonDto.class, + AddressMapper.class, + PersonMapper.class, + EntityMapper.class, +}) +public class Issue2170Test { + + @Test + public void shouldGenerateCodeThatCompiles() { + + AddressDto addressDto = AddressMapper.INSTANCE.toDto( new Address( + "10000", + Collections.singletonList( new Person( "Tester" ) ) + ) ); + + assertThat( addressDto ).isNotNull(); + assertThat( addressDto.getZipCode() ).isEqualTo( "10000" ); + assertThat( addressDto.getPeople() ) + .extracting( PersonDto::getName ) + .containsExactly( "Tester" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/dto/AddressDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/dto/AddressDto.java new file mode 100644 index 0000000000..c0b9b06317 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/dto/AddressDto.java @@ -0,0 +1,31 @@ +/* + * 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._2170.dto; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class AddressDto { + + private final String zipCode; + private final List people; + + public AddressDto(String zipCode, + List people) { + this.zipCode = zipCode; + this.people = people; + } + + public String getZipCode() { + return zipCode; + } + + public List getPeople() { + return people; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/dto/PersonDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/dto/PersonDto.java new file mode 100644 index 0000000000..7e6d22e72a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/dto/PersonDto.java @@ -0,0 +1,23 @@ +/* + * 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._2170.dto; + +/** + * @author Filip Hrisafov + */ +public +class PersonDto { + + private final String name; + + public PersonDto(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/entity/Address.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/entity/Address.java new file mode 100644 index 0000000000..660ff0a260 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/entity/Address.java @@ -0,0 +1,29 @@ +/* + * 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._2170.entity; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class Address { + private final String zipCode; + private final List people; + + public Address(String zipCode, List people) { + this.zipCode = zipCode; + this.people = people; + } + + public String getZipCode() { + return zipCode; + } + + public List getPeople() { + return people; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/entity/Person.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/entity/Person.java new file mode 100644 index 0000000000..dc36699c5b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/entity/Person.java @@ -0,0 +1,21 @@ +/* + * 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._2170.entity; + +/** + * @author Filip Hrisafov + */ +public class Person { + private final String name; + + public Person(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/AddressMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/AddressMapper.java new file mode 100644 index 0000000000..e31937609e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/AddressMapper.java @@ -0,0 +1,23 @@ +/* + * 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._2170.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.bugs._2170.dto.AddressDto; +import org.mapstruct.ap.test.bugs._2170.entity.Address; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +//CHECKSTYLE:OFF +@Mapper(uses = { PersonMapper.class }) +//CHECKSTYLE:ON +public interface AddressMapper extends EntityMapper { + + AddressMapper INSTANCE = Mappers.getMapper( AddressMapper.class ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/EntityMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/EntityMapper.java new file mode 100644 index 0000000000..0f24b05483 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/EntityMapper.java @@ -0,0 +1,21 @@ +/* + * 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._2170.mapper; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public interface EntityMapper { + E toEntity(D dto); + + D toDto(E entity); + + List toEntity(List dtoList); + + List toDto(List entityList); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/PersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/PersonMapper.java new file mode 100644 index 0000000000..28e7d0a31e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/mapper/PersonMapper.java @@ -0,0 +1,18 @@ +/* + * 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._2170.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.bugs._2170.dto.PersonDto; +import org.mapstruct.ap.test.bugs._2170.entity.Person; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface PersonMapper extends EntityMapper { + +} From e6279d10c78795231cd6fb790760e53147d5459a Mon Sep 17 00:00:00 2001 From: Makoto Oda Date: Wed, 26 Aug 2020 03:34:02 +0900 Subject: [PATCH 086/597] Is 'r' missing? (#2188) --- .../src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc index bc9cf7646c..54c33ed938 100644 --- a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc +++ b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc @@ -2,7 +2,7 @@ == Using the MapStruct SPI === Custom Accessor Naming Strategy -MapStruct offers the possibility to override the `AccessorNamingStrategy` via the Service Provide Interface (SPI). A nice example is the use of the fluent API on the source object `GolfPlayer` and `GolfPlayerDto` below. +MapStruct offers the possibility to override the `AccessorNamingStrategy` via the Service Provider Interface (SPI). A nice example is the use of the fluent API on the source object `GolfPlayer` and `GolfPlayerDto` below. .Source object GolfPlayer with fluent API. ==== From 18a5f1bdc0de319e3f02def64891e55c409f1824 Mon Sep 17 00:00:00 2001 From: Makoto Oda Date: Wed, 26 Aug 2020 03:37:02 +0900 Subject: [PATCH 087/597] Documentation: Extra phrase inserted? (#2187) --- .../main/asciidoc/chapter-10-advanced-mapping-options.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 28d6e9d1e0..4220c4d305 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -185,7 +185,7 @@ For all other objects an new instance is created. Please note that a default con 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`. +The strategy works in a hierarchical fashion. Setting `nullValuePropertyMappingStrategy` on mapping method level will override `@Mapper#nullValuePropertyMappingStrategy`, and `@Mapper#nullValuePropertyMappingStrategy` will override `@MappingConfig#nullValuePropertyMappingStrategy`. [NOTE] ==== From 99a1fd609c2d71c3ce8a39c1e56a51bb3242f0b2 Mon Sep 17 00:00:00 2001 From: Makoto Oda Date: Wed, 26 Aug 2020 03:38:19 +0900 Subject: [PATCH 088/597] Documentation: typo? (#2186) --- .../main/asciidoc/chapter-10-advanced-mapping-options.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 4220c4d305..ddf7fbcaed 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -144,7 +144,7 @@ public class FruitFactory { ---- ==== -So, which `Fruit` must be factorized in the mapping method `Fruit map(FruitDto source);`? A `Banana` or an `Apple`? Here's were the `@BeanMapping#resultType` comes in handy. It controls the factory method to select, or in absence of a factory method, the return type to create. +So, which `Fruit` must be factorized in the mapping method `Fruit map(FruitDto source);`? A `Banana` or an `Apple`? Here's where the `@BeanMapping#resultType` comes in handy. It controls the factory method to select, or in absence of a factory method, the return type to create. [TIP] ==== From 3ce9786cf654050d6146ba9305f7216fee163371 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Aug 2020 08:59:20 +0200 Subject: [PATCH 089/597] #2174 Forged methods should inherit all thrown exceptions from their property mappings --- .../ap/internal/model/BeanMappingMethod.java | 14 +- .../ap/test/bugs/_2174/Issue2174Test.java | 45 ++++++ .../ap/test/bugs/_2174/UserMapper.java | 128 ++++++++++++++++++ 3 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/Issue2174Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/UserMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index b0562373ff..0062611e2e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -296,8 +296,18 @@ else if ( !method.isUpdateMethod() ) { existingVariableNames ); - if (factoryMethod != null && method instanceof ForgedMethod ) { - ( (ForgedMethod) method ).addThrownTypes( factoryMethod.getThrownTypes() ); + if ( method instanceof ForgedMethod ) { + ForgedMethod forgedMethod = (ForgedMethod) method; + if ( factoryMethod != null ) { + forgedMethod.addThrownTypes( factoryMethod.getThrownTypes() ); + } + + for ( PropertyMapping propertyMapping : propertyMappings ) { + if ( propertyMapping.getAssignment() != null ) { + forgedMethod.addThrownTypes( propertyMapping.getAssignment().getThrownTypes() ); + } + } + } MethodReference finalizeMethod = null; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/Issue2174Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/Issue2174Test.java new file mode 100644 index 0000000000..89359957c9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/Issue2174Test.java @@ -0,0 +1,45 @@ +/* + * 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._2174; + +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 static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2174") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses(UserMapper.class) +public class Issue2174Test { + + @Test + public void shouldNotWrapCheckedException() throws Exception { + + UserMapper.User user = UserMapper.INSTANCE.map( new UserMapper.UserDto( "Test City", "10000" ) ); + + assertThat( user ).isNotNull(); + assertThat( user.getAddress() ).isNotNull(); + assertThat( user.getAddress().getCity() ).isNotNull(); + assertThat( user.getAddress().getCity().getName() ).isEqualTo( "Test City" ); + assertThat( user.getAddress().getCode() ).isNotNull(); + assertThat( user.getAddress().getCode().getCode() ).isEqualTo( "10000" ); + + assertThatThrownBy( () -> UserMapper.INSTANCE.map( new UserMapper.UserDto( "Zurich", "10000" ) ) ) + .isInstanceOf( UserMapper.CityNotFoundException.class ) + .hasMessage( "City with name 'Zurich' does not exist" ); + + assertThatThrownBy( () -> UserMapper.INSTANCE.map( new UserMapper.UserDto( "Test City", "1000" ) ) ) + .isInstanceOf( UserMapper.PostalCodeNotFoundException.class ) + .hasMessage( "Postal code '1000' does not exist" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/UserMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/UserMapper.java new file mode 100644 index 0000000000..025201b30c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/UserMapper.java @@ -0,0 +1,128 @@ +/* + * 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._2174; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface UserMapper { + + UserMapper INSTANCE = Mappers.getMapper( UserMapper.class ); + + @Mapping(target = "address.city", source = "city") + @Mapping(target = "address.code", source = "postalCode") + User map(UserDto dto) throws CityNotFoundException, PostalCodeNotFoundException; + + default City mapCity(String city) throws CityNotFoundException { + if ( "Test City".equals( city ) ) { + return new City( city ); + } + + throw new CityNotFoundException( "City with name '" + city + "' does not exist" ); + } + + default PostalCode mapCode(String code) throws PostalCodeNotFoundException { + if ( "10000".equals( code ) ) { + return new PostalCode( code ); + } + + throw new PostalCodeNotFoundException( "Postal code '" + code + "' does not exist" ); + } + + class UserDto { + private final String city; + private final String postalCode; + + public UserDto(String city, String postalCode) { + this.city = city; + this.postalCode = postalCode; + } + + public String getCity() { + return city; + } + + public String getPostalCode() { + return postalCode; + } + } + + class User { + + private final Address address; + + public User(Address address) { + this.address = address; + } + + public Address getAddress() { + return address; + } + } + + class Address { + private final City city; + private final PostalCode code; + + public Address(City city, PostalCode code) { + this.city = city; + this.code = code; + } + + public City getCity() { + return city; + } + + public PostalCode getCode() { + return code; + } + } + + class City { + + private final String name; + + public City(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + class PostalCode { + + private final String code; + + public PostalCode(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + } + + class CityNotFoundException extends Exception { + + public CityNotFoundException(String message) { + super( message ); + } + } + + class PostalCodeNotFoundException extends Exception { + + public PostalCodeNotFoundException(String message) { + super( message ); + } + } +} From c1feafef4c68053999b3d92be030ad41bd8843ba Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Aug 2020 11:22:18 +0200 Subject: [PATCH 090/597] #2177 Mapping into a generic class / record with a typed constructor argument should work --- .../itest/records/CustomerMapper.java | 3 ++ .../itest/records/GenericRecord.java | 13 ++++++ .../mapstruct/itest/records/RecordsTest.java | 12 +++++ .../ap/internal/model/BeanMappingMethod.java | 27 +++++++++--- .../accessor/ParameterElementAccessor.java | 6 ++- .../ap/test/bugs/_2177/Issue2177Mapper.java | 44 +++++++++++++++++++ .../ap/test/bugs/_2177/Issue2177Test.java | 34 ++++++++++++++ 7 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/GenericRecord.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java index a3c7037f03..addb8c5906 100644 --- a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java @@ -24,4 +24,7 @@ public interface CustomerMapper { @InheritInverseConfiguration CustomerDto toRecord(CustomerEntity entity); + @Mapping(target = "value", source = "name") + GenericRecord toValue(CustomerEntity entity); + } diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/GenericRecord.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/GenericRecord.java new file mode 100644 index 0000000000..9a39b59968 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/GenericRecord.java @@ -0,0 +1,13 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +/** + * @author Filip Hrisafov + */ +public record GenericRecord(T value) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java index 824a763fc7..b404681806 100644 --- a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java @@ -35,4 +35,16 @@ public void shouldMapIntoRecord() { assertThat( customer.name() ).isEqualTo( "Kermit" ); assertThat( customer.email() ).isEqualTo( "kermit@test.com" ); } + + @Test + public void shouldMapIntoGenericRecord() { + CustomerEntity entity = new CustomerEntity(); + entity.setName( "Kermit" ); + entity.setMail( "kermit@test.com" ); + + GenericRecord value = CustomerMapper.INSTANCE.toValue( entity ); + + assertThat( value ).isNotNull(); + assertThat( value.value() ).isEqualTo( "Kermit" ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 0062611e2e..d6f06b49e6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -27,6 +27,7 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic; @@ -598,15 +599,21 @@ private ConstructorAccessor getConstructorAccessor(Type type) { List parameterBindings = new ArrayList<>( recordComponents.size() ); Map constructorAccessors = new LinkedHashMap<>(); for ( Element recordComponent : recordComponents ) { + TypeMirror recordComponentMirror = ctx.getTypeUtils() + .asMemberOf( (DeclaredType) type.getTypeMirror(), recordComponent ); String parameterName = recordComponent.getSimpleName().toString(); - Accessor accessor = createConstructorAccessor( recordComponent, parameterName ); + Accessor accessor = createConstructorAccessor( + recordComponent, + recordComponentMirror, + parameterName + ); constructorAccessors.put( parameterName, accessor ); parameterBindings.add( ParameterBinding.fromTypeAndName( - ctx.getTypeFactory().getType( recordComponent.asType() ), + ctx.getTypeFactory().getType( recordComponentMirror ), accessor.getSimpleName() ) ); } @@ -718,7 +725,11 @@ private ConstructorAccessor getConstructorAccessor(Type type, ExecutableElement for ( Parameter constructorParameter : constructorParameters ) { String parameterName = constructorParameter.getName(); Element parameterElement = constructorParameter.getElement(); - Accessor constructorAccessor = createConstructorAccessor( parameterElement, parameterName ); + Accessor constructorAccessor = createConstructorAccessor( + parameterElement, + constructorParameter.getType().getTypeMirror(), + parameterName + ); constructorAccessors.put( parameterName, constructorAccessor @@ -746,7 +757,11 @@ else if ( constructorProperties.size() != constructorParameters.size() ) { String parameterName = constructorProperties.get( i ); Parameter constructorParameter = constructorParameters.get( i ); Element parameterElement = constructorParameter.getElement(); - Accessor constructorAccessor = createConstructorAccessor( parameterElement, parameterName ); + Accessor constructorAccessor = createConstructorAccessor( + parameterElement, + constructorParameter.getType().getTypeMirror(), + parameterName + ); constructorAccessors.put( parameterName, constructorAccessor @@ -761,13 +776,13 @@ else if ( constructorProperties.size() != constructorParameters.size() ) { } } - private Accessor createConstructorAccessor(Element element, String parameterName) { + private Accessor createConstructorAccessor(Element element, TypeMirror accessedType, String parameterName) { String safeParameterName = Strings.getSafeVariableName( parameterName, existingVariableNames ); existingVariableNames.add( safeParameterName ); - return new ParameterElementAccessor( element, safeParameterName ); + return new ParameterElementAccessor( element, accessedType, safeParameterName ); } private boolean hasDefaultAnnotationFromAnyPackage(Element element) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java index e1877cb0bb..9991370059 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java @@ -17,10 +17,12 @@ public class ParameterElementAccessor extends AbstractAccessor { protected final String name; + protected final TypeMirror accessedType; - public ParameterElementAccessor(Element element, String name) { + public ParameterElementAccessor(Element element, TypeMirror accessedType, String name) { super( element ); this.name = name; + this.accessedType = accessedType; } @Override @@ -30,7 +32,7 @@ public String getSimpleName() { @Override public TypeMirror getAccessedType() { - return element.asType(); + return accessedType; } @Override diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Mapper.java new file mode 100644 index 0000000000..bf96c6b5b8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Mapper.java @@ -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._2177; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2177Mapper { + + Issue2177Mapper INSTANCE = Mappers.getMapper( Issue2177Mapper.class ); + + Target map(Source source); + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Target { + private final T value; + + public Target(T value) { + this.value = value; + } + + public T getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java new file mode 100644 index 0000000000..50c1d9f69b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java @@ -0,0 +1,34 @@ +/* + * 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._2177; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2177") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2177Mapper.class +}) +public class Issue2177Test { + + @Test + public void shouldCorrectlyUseGenericClassesWithConstructorMapping() { + + Issue2177Mapper.Target target = Issue2177Mapper.INSTANCE.map( new Issue2177Mapper.Source( "test" ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "test" ); + } +} From a9451b1159522c2203f3788222a678163d78b4ab Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Aug 2020 11:44:13 +0200 Subject: [PATCH 091/597] #2185 Fix StackOverflow error when recursive use of mapper in Mapper#uses --- .../processor/MethodRetrievalProcessor.java | 21 +++++++-- .../mapstruct/ap/internal/util/Message.java | 1 + .../ap/test/bugs/_2185/Issue2185Test.java | 45 ++++++++++++++++++ .../ap/test/bugs/_2185/TodoMapper.java | 46 +++++++++++++++++++ 4 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/Issue2185Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/TodoMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 672d905ee0..059a000bc5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -176,11 +176,22 @@ private List retrieveMethods(TypeElement usedMapper, TypeElement m //Add all methods of used mappers in order to reference them in the aggregated model if ( usedMapper.equals( mapperToImplement ) ) { for ( DeclaredType mapper : mapperOptions.uses() ) { - methods.addAll( retrieveMethods( - asTypeElement( mapper ), - mapperToImplement, - mapperOptions, - prototypeMethods ) ); + TypeElement usesMapperElement = asTypeElement( mapper ); + if ( !mapperToImplement.equals( usesMapperElement ) ) { + methods.addAll( retrieveMethods( + usesMapperElement, + mapperToImplement, + mapperOptions, + prototypeMethods ) ); + } + else { + messager.printMessage( + mapperToImplement, + mapperOptions.getAnnotationMirror(), + Message.RETRIEVAL_MAPPER_USES_CYCLE, + mapperToImplement + ); + } } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 67cd1aba06..6f0db248bf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -149,6 +149,7 @@ public enum Message { RETRIEVAL_WILDCARD_SUPER_BOUND_SOURCE( "Can't generate mapping method for a wildcard super bound source." ), RETRIEVAL_WILDCARD_EXTENDS_BOUND_RESULT( "Can't generate mapping method for a wildcard extends bound result." ), RETRIEVAL_CONTEXT_PARAMS_WITH_SAME_TYPE( "The types of @Context parameters must be unique." ), + RETRIEVAL_MAPPER_USES_CYCLE( "The mapper %s is referenced itself in Mapper#uses.", Diagnostic.Kind.WARNING ), INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ), INHERITINVERSECONFIGURATION_INVALID_NAME( "None of the candidates %s() matches given name: \"%s\"." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/Issue2185Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/Issue2185Test.java new file mode 100644 index 0000000000..3b26842c0a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/Issue2185Test.java @@ -0,0 +1,45 @@ +/* + * 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._2185; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2185") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + TodoMapper.class +}) +public class Issue2185Test { + + @Test + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = TodoMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 14, + message = "The mapper org.mapstruct.ap.test.bugs._2185.TodoMapper is referenced itself in Mapper#uses.") + } + ) + public void shouldCompile() { + + TodoMapper.TodoResponse response = TodoMapper.INSTANCE.toResponse( new TodoMapper.TodoEntity( "test" ) ); + + assertThat( response ).isNotNull(); + assertThat( response.getNote() ).isEqualTo( "test" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/TodoMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/TodoMapper.java new file mode 100644 index 0000000000..2d52bed1fc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/TodoMapper.java @@ -0,0 +1,46 @@ +/* + * 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._2185; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = TodoMapper.class) +public interface TodoMapper { + + TodoMapper INSTANCE = Mappers.getMapper( TodoMapper.class ); + + TodoResponse toResponse(TodoEntity entity); + + class TodoResponse { + + private final String note; + + public TodoResponse(String note) { + this.note = note; + } + + public String getNote() { + return note; + } + } + + class TodoEntity { + + private final String note; + + public TodoEntity(String note) { + this.note = note; + } + + public String getNote() { + return note; + } + } +} From 7dcbef349d234e986ba41a4838937274f55e5c14 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Aug 2020 13:53:30 +0200 Subject: [PATCH 092/597] #2169 Add support for defining a custom unexpected value mapping exception Expose definition via: * `@EnumMapping` * `@Mapper` * `@MapperConfig` * `EnumMappingStrategy` SPI Rename `EnumNamingStrategy` to `EnumMappingStrategy` --- .../main/java/org/mapstruct/EnumMapping.java | 25 ++++- core/src/main/java/org/mapstruct/Mapper.java | 23 ++++ .../main/java/org/mapstruct/MapperConfig.java | 22 ++++ .../chapter-13-using-mapstruct-spi.asciidoc | 8 +- .../internal/model/MappingBuilderContext.java | 12 +-- .../ap/internal/model/ValueMappingMethod.java | 48 +++++++-- .../internal/model/source/DefaultOptions.java | 6 ++ .../model/source/DelegatingOptions.java | 4 + .../model/source/EnumMappingOptions.java | 64 +++++++++-- .../model/source/MapperConfigOptions.java | 8 ++ .../internal/model/source/MapperOptions.java | 8 ++ .../DefaultModelElementProcessorContext.java | 6 +- .../processor/MapperCreationProcessor.java | 2 +- .../processor/ModelElementProcessor.java | 4 +- .../util/AnnotationProcessorContext.java | 16 +-- .../mapstruct/ap/internal/util/Message.java | 4 +- .../mapstruct/ap/internal/util/Strings.java | 4 + ...y.java => DefaultEnumMappingStrategy.java} | 11 +- ...Strategy.java => EnumMappingStrategy.java} | 11 +- .../ap/internal/model/ValueMappingMethod.ftl | 4 +- .../value/CustomIllegalArgumentException.java | 16 +++ .../erroneous/EmptyEnumMappingMapper.java | 25 +++++ .../erroneous/ErroneousEnumMappingTest.java | 63 +++++++++++ .../NoConfigurationEnumMappingMapper.java | 26 +++++ .../ap/test/value/exception/Config.java | 16 +++ ...dValueMappingExceptionDefinedInMapper.java | 35 ++++++ ...xceptionDefinedInMapperAndEnumMapping.java | 38 +++++++ ...MappingExceptionDefinedInMapperConfig.java | 34 ++++++ ...UnexpectedValueMappingExceptionMapper.java | 39 +++++++ ...omUnexpectedValueMappingExceptionTest.java | 48 +++++++++ .../ap/test/value/spi/CustomCheeseMapper.java | 3 + ...gy.java => CustomEnumMappingStrategy.java} | 12 ++- ...ava => CustomEnumMappingStrategyTest.java} | 10 +- ...> CustomErroneousEnumMappingStrategy.java} | 6 +- ...stomErroneousEnumMappingStrategyTest.java} | 14 +-- ...tionDefinedInMapperAndEnumMappingImpl.java | 101 ++++++++++++++++++ ...ingExceptionDefinedInMapperConfigImpl.java | 101 ++++++++++++++++++ ...ueMappingExceptionDefinedInMapperImpl.java | 101 ++++++++++++++++++ ...pectedValueMappingExceptionMapperImpl.java | 101 ++++++++++++++++++ .../value/spi/CustomCheeseMapperImpl.java | 7 +- 40 files changed, 1014 insertions(+), 72 deletions(-) rename processor/src/main/java/org/mapstruct/ap/spi/{DefaultEnumNamingStrategy.java => DefaultEnumMappingStrategy.java} (68%) rename processor/src/main/java/org/mapstruct/ap/spi/{EnumNamingStrategy.java => EnumMappingStrategy.java} (80%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/CustomIllegalArgumentException.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/erroneous/EmptyEnumMappingMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/erroneous/ErroneousEnumMappingTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/erroneous/NoConfigurationEnumMappingMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/exception/Config.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionTest.java rename processor/src/test/java/org/mapstruct/ap/test/value/spi/{CustomEnumNamingStrategy.java => CustomEnumMappingStrategy.java} (77%) rename processor/src/test/java/org/mapstruct/ap/test/value/spi/{CustomEnumNamingStrategyTest.java => CustomEnumMappingStrategyTest.java} (94%) rename processor/src/test/java/org/mapstruct/ap/test/value/spi/{CustomErroneousEnumNamingStrategy.java => CustomErroneousEnumMappingStrategy.java} (87%) rename processor/src/test/java/org/mapstruct/ap/test/value/spi/{CustomErroneousEnumNamingStrategyTest.java => CustomErroneousEnumMappingStrategyTest.java} (90%) create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapperImpl.java diff --git a/core/src/main/java/org/mapstruct/EnumMapping.java b/core/src/main/java/org/mapstruct/EnumMapping.java index c184448aad..2f908579e8 100644 --- a/core/src/main/java/org/mapstruct/EnumMapping.java +++ b/core/src/main/java/org/mapstruct/EnumMapping.java @@ -111,7 +111,7 @@ * * @return the name transformation strategy */ - String nameTransformationStrategy(); + String nameTransformationStrategy() default ""; /** * The configuration that should be passed on the appropriate name transformation strategy. @@ -119,5 +119,26 @@ * * @return the configuration to use */ - String configuration(); + String configuration() default ""; + + /** + * Exception that should be thrown by the generated code if no mapping matches. + * If no exception is configured, the exception given via {@link MapperConfig#unexpectedValueMappingException()} or + * {@link Mapper#unexpectedValueMappingException()} will be used, using {@link IllegalArgumentException} by default. + * + *

      + * Note: + *

        + *
      • + * The defined exception should at least have a constructor with a {@link String} parameter. + *
      • + *
      • + * If the defined exception is a checked exception then the enum mapping methods should have that exception + * in the throws clause. + *
      • + *
      + * + * @return the exception that should be used in the generated code + */ + Class unexpectedValueMappingException() default IllegalArgumentException.class; } diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index ce8eebf1cb..233907621a 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -296,4 +296,27 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default * @see org.mapstruct.control.MappingControl */ Class mappingControl() default MappingControl.class; + + /** + * Exception that should be thrown by the generated code if no mapping matches for enums. + * If no exception is configured, the exception given via {@link MapperConfig#unexpectedValueMappingException()} + * will be used, using {@link IllegalArgumentException} by default. + * + *

      + * Note: + *

        + *
      • + * The defined exception should at least have a constructor with a {@link String} parameter. + *
      • + *
      • + * If the defined exception is a checked exception then the enum mapping methods should have that exception + * in the throws clause. + *
      • + *
      + * + * @return the exception that should be used in the generated code + * + * @since 1.4 + */ + Class unexpectedValueMappingException() default IllegalArgumentException.class; } diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java index 24951fb348..d251ff3798 100644 --- a/core/src/main/java/org/mapstruct/MapperConfig.java +++ b/core/src/main/java/org/mapstruct/MapperConfig.java @@ -273,5 +273,27 @@ MappingInheritanceStrategy mappingInheritanceStrategy() */ Class mappingControl() default MappingControl.class; + /** + * Exception that should be thrown by the generated code if no mapping matches for enums. + * If no exception is configured, {@link IllegalArgumentException} will be used by default. + * + *

      + * Note: + *

        + *
      • + * The defined exception should at least have a constructor with a {@link String} parameter. + *
      • + *
      • + * If the defined exception is a checked exception then the enum mapping methods should have that exception + * in the throws clause. + *
      • + *
      + * + * @return the exception that should be used in the generated code + * + * @since 1.4 + */ + Class unexpectedValueMappingException() default IllegalArgumentException.class; + } diff --git a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc index 54c33ed938..51fec968bf 100644 --- a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc +++ b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc @@ -202,7 +202,7 @@ include::{processor-ap-main}/spi/NoOpBuilderProvider.java[tag=documentation] [[custom-enum-naming-strategy]] === Custom Enum Naming Strategy -MapStruct offers the possibility to override the `EnumNamingStrategy` via the Service Provider Interface (SPI). +MapStruct offers the possibility to override the `EnumMappingStrategy` via the Service Provider Interface (SPI). This can be used when you have certain enums that follow some conventions within your organization. For example all enums which implement an interface named `CustomEnumMarker` are prefixed with `CUSTOM_` and the default value for them when mapping from `null` is `UNSPECIFIED` @@ -250,15 +250,15 @@ public interface CheeseTypeMapper { ---- ==== -This can be achieved with implementing the SPI `org.mapstruct.ap.spi.EnumNamingStrategy` as in the following example. -Here’s an implemented `org.mapstruct.ap.spi.EnumNamingStrategy`: +This can be achieved with implementing the SPI `org.mapstruct.ap.spi.EnumMappingStrategy` as in the following example. +Here’s an implemented `org.mapstruct.ap.spi.EnumMappingStrategy`: .Custom enum naming strategy ==== [source, java, linenums] [subs="verbatim,attributes"] ---- -public class CustomEnumNamingStrategy extends DefaultEnumNamingStrategy { +public class CustomEnumMappingStrategy extends DefaultEnumMappingStrategy { @Override public String getDefaultNullEnumConstant(TypeElement enumType) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index f5c5887d54..8da5cb5085 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -30,7 +30,7 @@ import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Services; -import org.mapstruct.ap.spi.EnumNamingStrategy; +import org.mapstruct.ap.spi.EnumMappingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.MappingExclusionProvider; @@ -107,7 +107,7 @@ Assignment getTargetAssignment(Method mappingMethod, ForgedMethodHistory descrip private final Types typeUtils; private final FormattingMessager messager; private final AccessorNamingUtils accessorNaming; - private final EnumNamingStrategy enumNamingStrategy; + private final EnumMappingStrategy enumMappingStrategy; private final Map enumTransformationStrategies; private final Options options; private final TypeElement mapperTypeElement; @@ -124,7 +124,7 @@ public MappingBuilderContext(TypeFactory typeFactory, Types typeUtils, FormattingMessager messager, AccessorNamingUtils accessorNaming, - EnumNamingStrategy enumNamingStrategy, + EnumMappingStrategy enumMappingStrategy, Map enumTransformationStrategies, Options options, MappingResolver mappingResolver, @@ -136,7 +136,7 @@ public MappingBuilderContext(TypeFactory typeFactory, this.typeUtils = typeUtils; this.messager = messager; this.accessorNaming = accessorNaming; - this.enumNamingStrategy = enumNamingStrategy; + this.enumMappingStrategy = enumMappingStrategy; this.enumTransformationStrategies = enumTransformationStrategies; this.options = options; this.mappingResolver = mappingResolver; @@ -191,8 +191,8 @@ public AccessorNamingUtils getAccessorNaming() { return accessorNaming; } - public EnumNamingStrategy getEnumNamingStrategy() { - return enumNamingStrategy; + public EnumMappingStrategy getEnumMappingStrategy() { + return enumMappingStrategy; } public Map getEnumTransformationStrategies() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 72f8153c5e..9c7272f2ba 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -43,7 +43,9 @@ public class ValueMappingMethod extends MappingMethod { private final List valueMappings; private final String defaultTarget; private final String nullTarget; - private final boolean throwIllegalArgumentException; + + private final Type unexpectedValueMappingException; + private final boolean overridden; public static class Builder { @@ -90,7 +92,7 @@ public ValueMappingMethod build() { if ( targetType.isEnumType() && valueMappings.nullTarget == null ) { // If null target is not set it means that the user has not explicitly defined a mapping for null - valueMappings.nullValueTarget = ctx.getEnumNamingStrategy() + valueMappings.nullValueTarget = ctx.getEnumMappingStrategy() .getDefaultNullEnumConstant( targetType.getTypeElement() ); } @@ -118,14 +120,14 @@ else if ( sourceType.isString() && targetType.isEnumType() ) { mappingEntries, valueMappings.nullValueTarget, valueMappings.defaultTargetValue, - !valueMappings.hasDefaultValue, + determineUnexpectedValueMappingException(), beforeMappingMethods, afterMappingMethods ); } private void initializeEnumTransformationStrategy() { - if ( !enumMapping.hasAnnotation() ) { + if ( !enumMapping.hasNameTransformationStrategy() ) { enumTransformationInvoker = EnumTransformationStrategyInvoker.DEFAULT; } else { @@ -290,7 +292,7 @@ private List stringToEnumMapping(Method method, Type targetType ) } private String getEnumConstant(TypeElement typeElement, String enumConstant) { - return ctx.getEnumNamingStrategy().getEnumConstant( typeElement, enumConstant ); + return ctx.getEnumMappingStrategy().getEnumConstant( typeElement, enumConstant ); } private SelectionParameters getSelectionParameters(Method method, Types typeUtils) { @@ -408,12 +410,26 @@ else if ( valueMappings.nullTarget == null && valueMappings.nullValueTarget != n Message.VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI, valueMappings.nullValueTarget, method.getReturnType(), - ctx.getEnumNamingStrategy() + ctx.getEnumMappingStrategy() ); } return !foundIncorrectMapping; } + + private Type determineUnexpectedValueMappingException() { + if ( !valueMappings.hasDefaultValue ) { + TypeMirror unexpectedValueMappingException = enumMapping.getUnexpectedValueMappingException(); + if ( unexpectedValueMappingException != null ) { + return ctx.getTypeFactory().getType( unexpectedValueMappingException ); + } + + return ctx.getTypeFactory() + .getType( ctx.getEnumMappingStrategy().getUnexpectedValueMappingExceptionType() ); + } + + return null; + } } private static class EnumTransformationStrategyInvoker { @@ -485,16 +501,28 @@ String getValue(ValueMappingOptions valueMapping) { } private ValueMappingMethod(Method method, List enumMappings, String nullTarget, String defaultTarget, - boolean throwIllegalArgumentException, List beforeMappingMethods, + Type unexpectedValueMappingException, + List beforeMappingMethods, List afterMappingMethods) { super( method, beforeMappingMethods, afterMappingMethods ); this.valueMappings = enumMappings; this.nullTarget = nullTarget; this.defaultTarget = defaultTarget; - this.throwIllegalArgumentException = throwIllegalArgumentException; + this.unexpectedValueMappingException = unexpectedValueMappingException; this.overridden = method.overridesMethod(); } + @Override + public Set getImportTypes() { + Set importTypes = super.getImportTypes(); + + if ( unexpectedValueMappingException != null && !unexpectedValueMappingException.isJavaLangType() ) { + importTypes.addAll( unexpectedValueMappingException.getImportTypes() ); + } + + return importTypes; + } + public List getValueMappings() { return valueMappings; } @@ -507,8 +535,8 @@ public String getNullTarget() { return nullTarget; } - public boolean isThrowIllegalArgumentException() { - return throwIllegalArgumentException; + public Type getUnexpectedValueMappingException() { + return unexpectedValueMappingException; } public Parameter getSourceParameter() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index 77911be2e6..81f870e25b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.Set; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import org.mapstruct.ap.internal.option.Options; @@ -127,6 +128,11 @@ public MappingControl getMappingControl(Elements elementUtils) { return MappingControl.fromTypeMirror( mapper.mappingControl().getDefaultValue(), elementUtils ); } + @Override + public TypeMirror getUnexpectedValueMappingException() { + return null; + } + @Override public boolean hasAnnotation() { return false; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java index 8bc6ab982b..c458681988 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -105,6 +105,10 @@ public MappingControl getMappingControl(Elements elementUtils) { return next.getMappingControl( elementUtils ); } + public TypeMirror getUnexpectedValueMappingException() { + return next.getUnexpectedValueMappingException(); + } + DelegatingOptions next() { return next; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java index 576474e680..86c415a4f1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java @@ -7,6 +7,7 @@ import java.util.Map; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.gem.EnumMappingGem; import org.mapstruct.ap.internal.util.FormattingMessager; @@ -14,6 +15,8 @@ import org.mapstruct.ap.spi.EnumTransformationStrategy; import static org.mapstruct.ap.internal.util.Message.ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY; +import static org.mapstruct.ap.internal.util.Message.ENUMMAPPING_MISSING_CONFIGURATION; +import static org.mapstruct.ap.internal.util.Message.ENUMMAPPING_NO_ELEMENTS; /** * @author Filip Hrisafov @@ -40,12 +43,25 @@ public boolean isValid() { return valid; } + public boolean hasNameTransformationStrategy() { + return hasAnnotation() && Strings.isNotEmpty( getNameTransformationStrategy() ); + } + public String getNameTransformationStrategy() { - return enumMapping.nameTransformationStrategy().get(); + return enumMapping.nameTransformationStrategy().getValue(); } public String getNameTransformationConfiguration() { - return enumMapping.configuration().get(); + return enumMapping.configuration().getValue(); + } + + @Override + public TypeMirror getUnexpectedValueMappingException() { + if ( enumMapping != null && enumMapping.unexpectedValueMappingException().hasValue() ) { + return enumMapping.unexpectedValueMappingException().getValue(); + } + + return next().getUnexpectedValueMappingException(); } public boolean isInverse() { @@ -79,21 +95,47 @@ private static boolean isConsistent(EnumMappingGem gem, ExecutableElement method Map enumTransformationStrategies, FormattingMessager messager) { String strategy = gem.nameTransformationStrategy().getValue(); + String configuration = gem.configuration().getValue(); + + boolean isConsistent = false; + + if ( Strings.isNotEmpty( strategy ) || Strings.isNotEmpty( configuration ) ) { + if ( !enumTransformationStrategies.containsKey( strategy ) ) { + String registeredStrategies = Strings.join( enumTransformationStrategies.keySet(), ", " ); + messager.printMessage( + method, + gem.mirror(), + gem.nameTransformationStrategy().getAnnotationValue(), + ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY, + strategy, + registeredStrategies + ); + + return false; + } + else if ( Strings.isEmpty( configuration ) ) { + messager.printMessage( + method, + gem.mirror(), + gem.configuration().getAnnotationValue(), + ENUMMAPPING_MISSING_CONFIGURATION + ); + return false; + } + + isConsistent = true; + } - if ( !enumTransformationStrategies.containsKey( strategy ) ) { - String registeredStrategies = Strings.join( enumTransformationStrategies.keySet(), ", " ); + isConsistent = isConsistent || gem.unexpectedValueMappingException().hasValue(); + + if ( !isConsistent ) { messager.printMessage( method, gem.mirror(), - gem.nameTransformationStrategy().getAnnotationValue(), - ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY, - strategy, - registeredStrategies + ENUMMAPPING_NO_ELEMENTS ); - - return false; } - return true; + return isConsistent; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java index ef25fda053..07f668e655 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -7,6 +7,7 @@ import java.util.Set; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import org.mapstruct.ap.internal.gem.BuilderGem; @@ -137,6 +138,13 @@ public MappingControl getMappingControl(Elements elementUtils) { next().getMappingControl( elementUtils ); } + @Override + public TypeMirror getUnexpectedValueMappingException() { + return mapperConfig.unexpectedValueMappingException().hasValue() ? + mapperConfig.unexpectedValueMappingException().get() : + next().getUnexpectedValueMappingException(); + } + @Override public boolean hasAnnotation() { return mapperConfig != null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index 19b7d8f0b4..b054c628a9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -11,6 +11,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import org.mapstruct.ap.internal.option.Options; @@ -167,6 +168,13 @@ public MappingControl getMappingControl(Elements elementUtils) { next().getMappingControl( elementUtils ); } + @Override + public TypeMirror getUnexpectedValueMappingException() { + return mapper.unexpectedValueMappingException().hasValue() ? + mapper.unexpectedValueMappingException().get() : + next().getUnexpectedValueMappingException(); + } + // @Mapper specific public DeclaredType mapperConfigType() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java index f8f7eee5bc..1514f4f5e1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java @@ -26,7 +26,7 @@ import org.mapstruct.ap.internal.util.RoundContext; import org.mapstruct.ap.internal.util.workarounds.TypesDecorator; import org.mapstruct.ap.internal.version.VersionInformation; -import org.mapstruct.ap.spi.EnumNamingStrategy; +import org.mapstruct.ap.spi.EnumMappingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; /** @@ -101,8 +101,8 @@ public Map getEnumTransformationStrategies() } @Override - public EnumNamingStrategy getEnumNamingStrategy() { - return roundContext.getAnnotationProcessorContext().getEnumNamingStrategy(); + public EnumMappingStrategy getEnumMappingStrategy() { + return roundContext.getAnnotationProcessorContext().getEnumMappingStrategy(); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index adb0b8f24f..58c6f2513a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -101,7 +101,7 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, L typeUtils, messager, accessorNaming, - context.getEnumNamingStrategy(), + context.getEnumMappingStrategy(), context.getEnumTransformationStrategies(), options, new MappingResolverImpl( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java index 0689af54a5..ac0a161a28 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java @@ -17,7 +17,7 @@ import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.version.VersionInformation; -import org.mapstruct.ap.spi.EnumNamingStrategy; +import org.mapstruct.ap.spi.EnumMappingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; /** @@ -56,7 +56,7 @@ public interface ProcessorContext { Map getEnumTransformationStrategies(); - EnumNamingStrategy getEnumNamingStrategy(); + EnumMappingStrategy getEnumMappingStrategy(); Options getOptions(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java index 4b70bd60ad..86fb7e3092 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java @@ -24,8 +24,8 @@ import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy; import org.mapstruct.ap.spi.DefaultBuilderProvider; -import org.mapstruct.ap.spi.DefaultEnumNamingStrategy; -import org.mapstruct.ap.spi.EnumNamingStrategy; +import org.mapstruct.ap.spi.DefaultEnumMappingStrategy; +import org.mapstruct.ap.spi.EnumMappingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.FreeBuilderAccessorNamingStrategy; import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; @@ -43,7 +43,7 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen private BuilderProvider builderProvider; private AccessorNamingStrategy accessorNamingStrategy; - private EnumNamingStrategy enumNamingStrategy; + private EnumMappingStrategy enumMappingStrategy; private boolean initialized; private Map enumTransformationStrategies; @@ -113,13 +113,13 @@ else if ( elementUtils.getTypeElement( FreeBuilderConstants.FREE_BUILDER_FQN ) ! } this.accessorNaming = new AccessorNamingUtils( this.accessorNamingStrategy ); - this.enumNamingStrategy = Services.get( EnumNamingStrategy.class, new DefaultEnumNamingStrategy() ); - this.enumNamingStrategy.init( this ); + this.enumMappingStrategy = Services.get( EnumMappingStrategy.class, new DefaultEnumMappingStrategy() ); + this.enumMappingStrategy.init( this ); if ( verbose ) { messager.printMessage( Diagnostic.Kind.NOTE, "MapStruct: Using enum naming strategy: " - + this.enumNamingStrategy.getClass().getCanonicalName() + + this.enumMappingStrategy.getClass().getCanonicalName() ); } @@ -250,9 +250,9 @@ public AccessorNamingStrategy getAccessorNamingStrategy() { return accessorNamingStrategy; } - public EnumNamingStrategy getEnumNamingStrategy() { + public EnumMappingStrategy getEnumMappingStrategy() { initialize(); - return enumNamingStrategy; + return enumMappingStrategy; } public BuilderProvider getBuilderProvider() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 6f0db248bf..87d3a9abdd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -105,6 +105,8 @@ public enum Message { ENUMMAPPING_UNMAPPED_SOURCES( "The following constants from the source enum have no corresponding constant in the target enum and must be be mapped via adding additional mappings: %s." ), ENUMMAPPING_REMOVED( "Mapping of Enums via @Mapping is removed. Please use @ValueMapping instead!" ), ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY( "There is no registered EnumTransformationStrategy for '%s'. Registered strategies are: %s." ), + ENUMMAPPING_MISSING_CONFIGURATION( "Configuration has to be defined when strategy is defined." ), + ENUMMAPPING_NO_ELEMENTS( "'nameTransformationStrategy', 'configuration' and 'unexpectedValueMappingException' are undefined in @EnumMapping, define at least one of them." ), LIFECYCLEMETHOD_AMBIGUOUS_PARAMETERS( "Lifecycle method has multiple matching parameters (e. g. same type), in this case please ensure to name the parameters in the lifecycle and mapping method identical. This lifecycle method will not be used for the mapping method '%s'.", Diagnostic.Kind.WARNING), @@ -169,7 +171,7 @@ public enum Message { VALUEMAPPING_UNMAPPED_SOURCES( "The following constants from the %s enum have no corresponding constant in the %s enum and must be be mapped via adding additional mappings: %s." ), VALUEMAPPING_ANY_REMAINING_FOR_NON_ENUM( "Source = \"\" can only be used on targets of type enum and not for %s." ), VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING( "Source = \"\" or \"\" is advisable for mapping of type String to an enum type.", Diagnostic.Kind.WARNING ), - VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI( "Constant %s doesn't exist in enum type %s. Constant was returned from EnumNamingStrategy: %s"), + VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI( "Constant %s doesn't exist in enum type %s. Constant was returned from EnumMappingStrategy: %s"), VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." ); // CHECKSTYLE:ON diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java index 07049c17ad..f1eca88156 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java @@ -129,6 +129,10 @@ public static boolean isEmpty(String string) { return string == null || string.isEmpty(); } + public static boolean isNotEmpty(String string) { + return !isEmpty( string ); + } + public static String getSafeVariableName(String name, String... existingVariableNames) { return getSafeVariableName( name, Arrays.asList( existingVariableNames ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumMappingStrategy.java similarity index 68% rename from processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java rename to processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumMappingStrategy.java index a54df7bc06..62d6c32809 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumMappingStrategy.java @@ -14,7 +14,7 @@ * * @since 1.4 */ -public class DefaultEnumNamingStrategy implements EnumNamingStrategy { +public class DefaultEnumMappingStrategy implements EnumMappingStrategy { protected Elements elementUtils; protected Types typeUtils; @@ -34,4 +34,13 @@ public String getDefaultNullEnumConstant(TypeElement enumType) { public String getEnumConstant(TypeElement enumType, String enumConstant) { return enumConstant; } + + @Override + public TypeElement getUnexpectedValueMappingExceptionType() { + return elementUtils.getTypeElement( getUnexpectedValueMappingExceptionClass().getCanonicalName() ); + } + + protected Class getUnexpectedValueMappingExceptionClass() { + return IllegalArgumentException.class; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/EnumMappingStrategy.java similarity index 80% rename from processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java rename to processor/src/main/java/org/mapstruct/ap/spi/EnumMappingStrategy.java index a2361eefd0..97088cd8c6 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/EnumNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/EnumMappingStrategy.java @@ -13,11 +13,12 @@ * A service provider interface for the mapping between different enum constants * * @author Arne Seime + * @author Filip Hrisafov * * @since 1.4 */ @Experimental("This SPI can have it's signature changed in subsequent releases") -public interface EnumNamingStrategy { +public interface EnumMappingStrategy { /** * Initializes the enum value mapping strategy @@ -47,4 +48,12 @@ default void init(MapStructProcessingEnvironment processingEnvironment) { * never return null */ String getEnumConstant(TypeElement enumType, String enumConstant); + + /** + * Return the type element of the exception that should be used in the generated code + * for an unexpected enum constant. + * + * @return the type element of the exception that should be used, never {@code null} + */ + TypeElement getUnexpectedValueMappingExceptionType(); } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl index ba16d56a2d..530d5643e1 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -25,7 +25,7 @@ case <@writeSource source=valueMapping.source/>: ${resultName} = <@writeTarget target=valueMapping.target/>; break; - default: <#if throwIllegalArgumentException>throw new IllegalArgumentException( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/>; + default: <#if unexpectedValueMappingException??>throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/>; } <#list beforeMappingReferencesWithMappingTarget as callback> <#if callback_index = 0> @@ -40,7 +40,7 @@ <@includeModel object=callback targetBeanName=resultName targetType=resultType/> - <#if !(valueMappings.empty && throwIllegalArgumentException)> + <#if !(valueMappings.empty && unexpectedValueMappingException??)> return ${resultName}; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/CustomIllegalArgumentException.java b/processor/src/test/java/org/mapstruct/ap/test/value/CustomIllegalArgumentException.java new file mode 100644 index 0000000000..e5552d798a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/CustomIllegalArgumentException.java @@ -0,0 +1,16 @@ +/* + * 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.value; + +/** + * @author Filip Hrisafov + */ +public class CustomIllegalArgumentException extends RuntimeException { + + public CustomIllegalArgumentException(String message) { + super( message ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/EmptyEnumMappingMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/EmptyEnumMappingMapper.java new file mode 100644 index 0000000000..e0f2f112db --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/EmptyEnumMappingMapper.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.ap.test.value.erroneous; + +import org.mapstruct.EnumMapping; +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface EmptyEnumMappingMapper { + + @EnumMapping + @ValueMapping(source = "EXTRA", target = "SPECIAL") + @ValueMapping(source = "STANDARD", target = "DEFAULT") + @ValueMapping(source = "NORMAL", target = "DEFAULT") + ExternalOrderType onlyWithMappings(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/ErroneousEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/ErroneousEnumMappingTest.java new file mode 100644 index 0000000000..45d374f217 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/ErroneousEnumMappingTest.java @@ -0,0 +1,63 @@ +/* + * 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.value.erroneous; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2169") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + CustomIllegalArgumentException.class, + ExternalOrderType.class, + OrderType.class, +}) +public class ErroneousEnumMappingTest { + + @Test + @WithClasses({ + EmptyEnumMappingMapper.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = EmptyEnumMappingMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = "'nameTransformationStrategy', 'configuration' and 'unexpectedValueMappingException' are " + + "undefined in @EnumMapping, define at least one of them." + ) + } + ) + public void shouldCompileErrorWhenEnumMappingIsEmpty() { + } + + @Test + @WithClasses({ + NoConfigurationEnumMappingMapper.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = NoConfigurationEnumMappingMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 21, + message = "Configuration has to be defined when strategy is defined.") + } + ) + public void shouldCompileErrorWhenEnumMappingHasStrategyButNoConfiguration() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/NoConfigurationEnumMappingMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/NoConfigurationEnumMappingMapper.java new file mode 100644 index 0000000000..9d82ed059d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/NoConfigurationEnumMappingMapper.java @@ -0,0 +1,26 @@ +/* + * 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.value.erroneous; + +import org.mapstruct.EnumMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface NoConfigurationEnumMappingMapper { + + @EnumMapping(nameTransformationStrategy = MappingConstants.PREFIX_TRANSFORMATION) + @ValueMapping(source = "EXTRA", target = "SPECIAL") + @ValueMapping(source = "STANDARD", target = "DEFAULT") + @ValueMapping(source = "NORMAL", target = "DEFAULT") + ExternalOrderType onlyWithMappings(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/exception/Config.java b/processor/src/test/java/org/mapstruct/ap/test/value/exception/Config.java new file mode 100644 index 0000000000..627ac79700 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/exception/Config.java @@ -0,0 +1,16 @@ +/* + * 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.value.exception; + +import org.mapstruct.MapperConfig; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; + +/** + * @author Filip Hrisafov + */ +@MapperConfig(unexpectedValueMappingException = CustomIllegalArgumentException.class) +public interface Config { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapper.java new file mode 100644 index 0000000000..6fc94fb8a8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapper.java @@ -0,0 +1,35 @@ +/* + * 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.value.exception; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +/** + * @author Filip Hrisafov + */ +@Mapper(unexpectedValueMappingException = CustomIllegalArgumentException.class) +public interface CustomUnexpectedValueMappingExceptionDefinedInMapper { + + @ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = "DEFAULT") + ExternalOrderType withAnyUnmapped(OrderType orderType); + + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "DEFAULT") + ExternalOrderType withAnyRemaining(OrderType orderType); + + @ValueMapping(source = "EXTRA", target = "SPECIAL") + @ValueMapping(source = "STANDARD", target = "DEFAULT") + @ValueMapping(source = "NORMAL", target = "DEFAULT") + ExternalOrderType onlyWithMappings(OrderType orderType); + + @InheritInverseConfiguration(name = "onlyWithMappings") + OrderType inverseOnlyWithMappings(ExternalOrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping.java b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping.java new file mode 100644 index 0000000000..c5396ad480 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping.java @@ -0,0 +1,38 @@ +/* + * 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.value.exception; + +import org.mapstruct.EnumMapping; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +/** + * @author Filip Hrisafov + */ +@Mapper(unexpectedValueMappingException = CustomIllegalArgumentException.class) +public interface CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping { + + @ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = "DEFAULT") + ExternalOrderType withAnyUnmapped(OrderType orderType); + + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "DEFAULT") + ExternalOrderType withAnyRemaining(OrderType orderType); + + @ValueMapping(source = "EXTRA", target = "SPECIAL") + @ValueMapping(source = "STANDARD", target = "DEFAULT") + @ValueMapping(source = "NORMAL", target = "DEFAULT") + ExternalOrderType onlyWithMappings(OrderType orderType); + + // If unexpectedValueMappingException is explicitly defined then it should be used instead of what is in the SPI + @EnumMapping(unexpectedValueMappingException = IllegalArgumentException.class) + @InheritInverseConfiguration(name = "onlyWithMappings") + OrderType inverseOnlyWithMappings(ExternalOrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfig.java b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfig.java new file mode 100644 index 0000000000..3e3d7e2199 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfig.java @@ -0,0 +1,34 @@ +/* + * 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.value.exception; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +/** + * @author Filip Hrisafov + */ +@Mapper(config = Config.class) +public interface CustomUnexpectedValueMappingExceptionDefinedInMapperConfig { + + @ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = "DEFAULT") + ExternalOrderType withAnyUnmapped(OrderType orderType); + + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "DEFAULT") + ExternalOrderType withAnyRemaining(OrderType orderType); + + @ValueMapping(source = "EXTRA", target = "SPECIAL") + @ValueMapping(source = "STANDARD", target = "DEFAULT") + @ValueMapping(source = "NORMAL", target = "DEFAULT") + ExternalOrderType onlyWithMappings(OrderType orderType); + + @InheritInverseConfiguration(name = "onlyWithMappings") + OrderType inverseOnlyWithMappings(ExternalOrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapper.java new file mode 100644 index 0000000000..96c8b297c6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapper.java @@ -0,0 +1,39 @@ +/* + * 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.value.exception; + +import org.mapstruct.EnumMapping; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CustomUnexpectedValueMappingExceptionMapper { + + @EnumMapping(unexpectedValueMappingException = CustomIllegalArgumentException.class) + @ValueMapping( source = MappingConstants.ANY_UNMAPPED, target = "DEFAULT" ) + ExternalOrderType withAnyUnmapped(OrderType orderType); + + @EnumMapping(unexpectedValueMappingException = CustomIllegalArgumentException.class) + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "DEFAULT" ) + ExternalOrderType withAnyRemaining(OrderType orderType); + + @EnumMapping(unexpectedValueMappingException = CustomIllegalArgumentException.class) + @ValueMapping(source = "EXTRA", target = "SPECIAL") + @ValueMapping(source = "STANDARD", target = "DEFAULT") + @ValueMapping(source = "NORMAL", target = "DEFAULT") + ExternalOrderType onlyWithMappings(OrderType orderType); + + @InheritInverseConfiguration(name = "onlyWithMappings") + OrderType inverseOnlyWithMappings(ExternalOrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionTest.java new file mode 100644 index 0000000000..fbbf702854 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionTest.java @@ -0,0 +1,48 @@ +/* + * 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.value.exception; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +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; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2169") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Config.class, + CustomUnexpectedValueMappingExceptionDefinedInMapper.class, + CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping.class, + CustomUnexpectedValueMappingExceptionDefinedInMapperConfig.class, + CustomUnexpectedValueMappingExceptionMapper.class, + CustomIllegalArgumentException.class, + ExternalOrderType.class, + OrderType.class +}) +public class CustomUnexpectedValueMappingExceptionTest { + + @Rule + public final GeneratedSource generatedSource = new GeneratedSource(); + + @Test + public void shouldGenerateCustomUnexpectedValueMappingException() { + generatedSource.addComparisonToFixtureFor( + CustomUnexpectedValueMappingExceptionDefinedInMapper.class, + CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping.class, + CustomUnexpectedValueMappingExceptionDefinedInMapperConfig.class, + CustomUnexpectedValueMappingExceptionMapper.class + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseMapper.java index 6524f67c36..b042405c43 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomCheeseMapper.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.value.spi; +import org.mapstruct.EnumMapping; import org.mapstruct.Mapper; import org.mapstruct.MappingConstants; import org.mapstruct.ValueMapping; @@ -24,6 +25,8 @@ public interface CustomCheeseMapper { String mapToString(CustomCheeseType cheeseType); + // If unexpectedValueMappingException is explicitly defined then it should be used instead of what is in the SPI + @EnumMapping(unexpectedValueMappingException = IllegalArgumentException.class) String mapToString(CheeseType cheeseType); @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "CUSTOM_BRIE") diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategy.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategy.java similarity index 77% rename from processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategy.java rename to processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategy.java index f84ebaf7b3..b505dd640b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategy.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategy.java @@ -9,13 +9,14 @@ import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.gem.MappingConstantsGem; -import org.mapstruct.ap.spi.DefaultEnumNamingStrategy; -import org.mapstruct.ap.spi.EnumNamingStrategy; +import org.mapstruct.ap.spi.DefaultEnumMappingStrategy; +import org.mapstruct.ap.spi.EnumMappingStrategy; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; /** * @author Filip Hrisafov */ -public class CustomEnumNamingStrategy extends DefaultEnumNamingStrategy implements EnumNamingStrategy { +public class CustomEnumMappingStrategy extends DefaultEnumMappingStrategy implements EnumMappingStrategy { @Override public String getDefaultNullEnumConstant(TypeElement enumType) { @@ -51,4 +52,9 @@ protected boolean isCustomEnum(TypeElement enumType) { return false; } + + @Override + protected Class getUnexpectedValueMappingExceptionClass() { + return CustomIllegalArgumentException.class; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java similarity index 94% rename from processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategyTest.java rename to processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java index 47a0a5d64e..9cce52d7bd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumNamingStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java @@ -8,6 +8,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @@ -23,9 +24,10 @@ CheeseType.class, CustomCheeseType.class, CustomEnumMarker.class, + CustomIllegalArgumentException.class, }) -@WithServiceImplementation(CustomEnumNamingStrategy.class) -public class CustomEnumNamingStrategyTest { +@WithServiceImplementation(CustomEnumMappingStrategy.class) +public class CustomEnumMappingStrategyTest { @Rule public final GeneratedSource generatedSource = new GeneratedSource(); @@ -34,7 +36,7 @@ public class CustomEnumNamingStrategyTest { @WithClasses({ CustomCheeseMapper.class }) - public void shouldApplyCustomEnumNamingStrategy() { + public void shouldApplyCustomEnumMappingStrategy() { generatedSource.addComparisonToFixtureFor( CustomCheeseMapper.class ); CustomCheeseMapper mapper = CustomCheeseMapper.INSTANCE; @@ -80,7 +82,7 @@ public void shouldApplyCustomEnumNamingStrategy() { @WithClasses({ OverridesCustomCheeseMapper.class }) - public void shouldApplyDefinedMappingsInsteadOfCustomEnumNamingStrategy() { + public void shouldApplyDefinedMappingsInsteadOfCustomEnumMappingStrategy() { OverridesCustomCheeseMapper mapper = OverridesCustomCheeseMapper.INSTANCE; // CheeseType -> CustomCheeseType diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategy.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategy.java similarity index 87% rename from processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategy.java rename to processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategy.java index 671ad45c04..8a4f2b5bd9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategy.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategy.java @@ -9,13 +9,13 @@ import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.gem.MappingConstantsGem; -import org.mapstruct.ap.spi.DefaultEnumNamingStrategy; -import org.mapstruct.ap.spi.EnumNamingStrategy; +import org.mapstruct.ap.spi.DefaultEnumMappingStrategy; +import org.mapstruct.ap.spi.EnumMappingStrategy; /** * @author Filip Hrisafov */ -public class CustomErroneousEnumNamingStrategy extends DefaultEnumNamingStrategy implements EnumNamingStrategy { +public class CustomErroneousEnumMappingStrategy extends DefaultEnumMappingStrategy implements EnumMappingStrategy { @Override public String getDefaultNullEnumConstant(TypeElement enumType) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategyTest.java similarity index 90% rename from processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategyTest.java rename to processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategyTest.java index 82d6be4c26..9cb1337bc8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumNamingStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategyTest.java @@ -25,8 +25,8 @@ CustomCheeseType.class, CustomEnumMarker.class, }) -@WithServiceImplementation(CustomErroneousEnumNamingStrategy.class) -public class CustomErroneousEnumNamingStrategyTest { +@WithServiceImplementation(CustomErroneousEnumMappingStrategy.class) +public class CustomErroneousEnumMappingStrategyTest { @Test @WithClasses({ @@ -37,18 +37,18 @@ public class CustomErroneousEnumNamingStrategyTest { @Diagnostic( type = CustomCheeseMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 23, + line = 24, messageRegExp = "Constant INCORRECT doesn't exist in enum type " + "org\\.mapstruct\\.ap\\.test\\.value\\.spi\\.CustomCheeseType." + - " Constant was returned from EnumNamingStrategy: .*CustomErroneousEnumNamingStrategy@.*" + " Constant was returned from EnumMappingStrategy: .*CustomErroneousEnumMappingStrategy@.*" ), @Diagnostic( type = CustomCheeseMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 30, + line = 33, messageRegExp = "Constant INCORRECT doesn't exist in enum type " + "org\\.mapstruct\\.ap\\.test\\.value\\.spi\\.CustomCheeseType." + - " Constant was returned from EnumNamingStrategy: .*CustomErroneousEnumNamingStrategy@.*" + " Constant was returned from EnumMappingStrategy: .*CustomErroneousEnumMappingStrategy@.*" ) } ) @@ -59,7 +59,7 @@ public void shouldThrowCompileErrorWhenDefaultEnumDoesNotExist() { @WithClasses({ OverridesCustomCheeseMapper.class }) - public void shouldApplyDefinedMappingsInsteadOfCustomEnumNamingStrategy() { + public void shouldApplyDefinedMappingsInsteadOfCustomEnumMappingStrategy() { OverridesCustomCheeseMapper mapper = OverridesCustomCheeseMapper.INSTANCE; // CheeseType -> CustomCheeseType diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl.java new file mode 100644 index 0000000000..f0ae6eecd3 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl.java @@ -0,0 +1,101 @@ +/* + * 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.value.exception; + +import javax.annotation.Generated; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2020-08-29T09:36:57+0200", + comments = "version: , compiler: javac, environment: Java 11.0.2 (AdoptOpenJDK)" +) +public class CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMappingImpl implements CustomUnexpectedValueMappingExceptionDefinedInMapperAndEnumMapping { + + @Override + public ExternalOrderType withAnyUnmapped(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + default: externalOrderType = ExternalOrderType.DEFAULT; + } + + return externalOrderType; + } + + @Override + public ExternalOrderType withAnyRemaining(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + case RETAIL: externalOrderType = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType = ExternalOrderType.B2B; + break; + default: externalOrderType = ExternalOrderType.DEFAULT; + } + + return externalOrderType; + } + + @Override + public ExternalOrderType onlyWithMappings(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + case EXTRA: externalOrderType = ExternalOrderType.SPECIAL; + break; + case STANDARD: externalOrderType = ExternalOrderType.DEFAULT; + break; + case NORMAL: externalOrderType = ExternalOrderType.DEFAULT; + break; + case RETAIL: externalOrderType = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType = ExternalOrderType.B2B; + break; + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return externalOrderType; + } + + @Override + public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) { + if ( orderType == null ) { + return null; + } + + OrderType orderType1; + + switch ( orderType ) { + case SPECIAL: orderType1 = OrderType.EXTRA; + break; + case DEFAULT: orderType1 = OrderType.STANDARD; + break; + case RETAIL: orderType1 = OrderType.RETAIL; + break; + case B2B: orderType1 = OrderType.B2B; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return orderType1; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl.java new file mode 100644 index 0000000000..fee7a2bb1e --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl.java @@ -0,0 +1,101 @@ +/* + * 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.value.exception; + +import javax.annotation.Generated; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2020-08-29T09:36:57+0200", + comments = "version: , compiler: javac, environment: Java 11.0.2 (AdoptOpenJDK)" +) +public class CustomUnexpectedValueMappingExceptionDefinedInMapperConfigImpl implements CustomUnexpectedValueMappingExceptionDefinedInMapperConfig { + + @Override + public ExternalOrderType withAnyUnmapped(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + default: externalOrderType = ExternalOrderType.DEFAULT; + } + + return externalOrderType; + } + + @Override + public ExternalOrderType withAnyRemaining(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + case RETAIL: externalOrderType = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType = ExternalOrderType.B2B; + break; + default: externalOrderType = ExternalOrderType.DEFAULT; + } + + return externalOrderType; + } + + @Override + public ExternalOrderType onlyWithMappings(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + case EXTRA: externalOrderType = ExternalOrderType.SPECIAL; + break; + case STANDARD: externalOrderType = ExternalOrderType.DEFAULT; + break; + case NORMAL: externalOrderType = ExternalOrderType.DEFAULT; + break; + case RETAIL: externalOrderType = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType = ExternalOrderType.B2B; + break; + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return externalOrderType; + } + + @Override + public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) { + if ( orderType == null ) { + return null; + } + + OrderType orderType1; + + switch ( orderType ) { + case SPECIAL: orderType1 = OrderType.EXTRA; + break; + case DEFAULT: orderType1 = OrderType.STANDARD; + break; + case RETAIL: orderType1 = OrderType.RETAIL; + break; + case B2B: orderType1 = OrderType.B2B; + break; + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return orderType1; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperImpl.java new file mode 100644 index 0000000000..a897339181 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionDefinedInMapperImpl.java @@ -0,0 +1,101 @@ +/* + * 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.value.exception; + +import javax.annotation.Generated; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2020-08-29T09:36:57+0200", + comments = "version: , compiler: javac, environment: Java 11.0.2 (AdoptOpenJDK)" +) +public class CustomUnexpectedValueMappingExceptionDefinedInMapperImpl implements CustomUnexpectedValueMappingExceptionDefinedInMapper { + + @Override + public ExternalOrderType withAnyUnmapped(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + default: externalOrderType = ExternalOrderType.DEFAULT; + } + + return externalOrderType; + } + + @Override + public ExternalOrderType withAnyRemaining(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + case RETAIL: externalOrderType = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType = ExternalOrderType.B2B; + break; + default: externalOrderType = ExternalOrderType.DEFAULT; + } + + return externalOrderType; + } + + @Override + public ExternalOrderType onlyWithMappings(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + case EXTRA: externalOrderType = ExternalOrderType.SPECIAL; + break; + case STANDARD: externalOrderType = ExternalOrderType.DEFAULT; + break; + case NORMAL: externalOrderType = ExternalOrderType.DEFAULT; + break; + case RETAIL: externalOrderType = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType = ExternalOrderType.B2B; + break; + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return externalOrderType; + } + + @Override + public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) { + if ( orderType == null ) { + return null; + } + + OrderType orderType1; + + switch ( orderType ) { + case SPECIAL: orderType1 = OrderType.EXTRA; + break; + case DEFAULT: orderType1 = OrderType.STANDARD; + break; + case RETAIL: orderType1 = OrderType.RETAIL; + break; + case B2B: orderType1 = OrderType.B2B; + break; + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return orderType1; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapperImpl.java new file mode 100644 index 0000000000..c22ff08f60 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionMapperImpl.java @@ -0,0 +1,101 @@ +/* + * 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.value.exception; + +import javax.annotation.Generated; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2020-08-02T09:36:57+0200", + comments = "version: , compiler: javac, environment: Java 11.0.2 (AdoptOpenJDK)" +) +public class CustomUnexpectedValueMappingExceptionMapperImpl implements CustomUnexpectedValueMappingExceptionMapper { + + @Override + public ExternalOrderType withAnyUnmapped(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + default: externalOrderType = ExternalOrderType.DEFAULT; + } + + return externalOrderType; + } + + @Override + public ExternalOrderType withAnyRemaining(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + case RETAIL: externalOrderType = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType = ExternalOrderType.B2B; + break; + default: externalOrderType = ExternalOrderType.DEFAULT; + } + + return externalOrderType; + } + + @Override + public ExternalOrderType onlyWithMappings(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + case EXTRA: externalOrderType = ExternalOrderType.SPECIAL; + break; + case STANDARD: externalOrderType = ExternalOrderType.DEFAULT; + break; + case NORMAL: externalOrderType = ExternalOrderType.DEFAULT; + break; + case RETAIL: externalOrderType = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType = ExternalOrderType.B2B; + break; + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return externalOrderType; + } + + @Override + public OrderType inverseOnlyWithMappings(ExternalOrderType orderType) { + if ( orderType == null ) { + return null; + } + + OrderType orderType1; + + switch ( orderType ) { + case SPECIAL: orderType1 = OrderType.EXTRA; + break; + case DEFAULT: orderType1 = OrderType.STANDARD; + break; + case RETAIL: orderType1 = OrderType.RETAIL; + break; + case B2B: orderType1 = OrderType.B2B; + break; + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return orderType1; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java index 9798c41597..b2ba34a328 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/spi/CustomCheeseMapperImpl.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.value.spi; import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.value.CustomIllegalArgumentException; @Generated( value = "org.mapstruct.ap.MappingProcessor", @@ -31,7 +32,7 @@ public CheeseType map(CustomCheeseType cheese) { break; case UNRECOGNIZED: cheeseType = null; break; - default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + cheese ); } return cheeseType; @@ -50,7 +51,7 @@ public CustomCheeseType map(CheeseType cheese) { break; case ROQUEFORT: customCheeseType = CustomCheeseType.CUSTOM_ROQUEFORT; break; - default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheese ); + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + cheese ); } return customCheeseType; @@ -73,7 +74,7 @@ public String mapToString(CustomCheeseType cheeseType) { break; case UNRECOGNIZED: string = null; break; - default: throw new IllegalArgumentException( "Unexpected enum constant: " + cheeseType ); + default: throw new CustomIllegalArgumentException( "Unexpected enum constant: " + cheeseType ); } return string; From e0eb0f6bb85970e534ebc4a5ea99c85c23bcf8be Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sun, 30 Aug 2020 12:49:09 +0200 Subject: [PATCH 093/597] #2164 parameter matching should be done based on name when source name is absent (#2193) --- .../ap/internal/model/BeanMappingMethod.java | 29 ++++++++++---- .../ap/test/bugs/_2164/Issue2164Mapper.java | 40 +++++++++++++++++++ .../ap/test/bugs/_2164/Issue2164Test.java | 31 ++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index d6f06b49e6..a0ea064f1f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1068,10 +1068,23 @@ else if ( mapping.getJavaExpression() != null ) { // its a plain-old property mapping else { - // determine source parameter SourceReference sourceRef = mappingRef.getSourceReference(); + // sourceRef is not defined, check if a source property has the same name if ( sourceRef == null && method.getSourceParameters().size() == 1 ) { - sourceRef = getSourceRef( method.getSourceParameters().get( 0 ), targetPropertyName ); + sourceRef = getSourceRefByTargetName( method.getSourceParameters().get( 0 ), targetPropertyName ); + } + + if ( sourceRef == null ) { + // still no match. Try if one of the parameters has the same name + sourceRef = method.getSourceParameters() + .stream() + .filter( p -> targetPropertyName.equals( p.getName() ) ) + .findAny() + .map( p -> new SourceReference.BuilderFromProperty() + .sourceParameter( p ) + .name( targetPropertyName ) + .build() ) + .orElse( null ); } if ( sourceRef != null ) { @@ -1153,7 +1166,7 @@ private void applyPropertyNameBasedMapping() { List sourceReferences = new ArrayList<>(); for ( String targetPropertyName : unprocessedTargetProperties.keySet() ) { for ( Parameter sourceParameter : method.getSourceParameters() ) { - SourceReference sourceRef = getSourceRef( sourceParameter, targetPropertyName ); + SourceReference sourceRef = getSourceRefByTargetName( sourceParameter, targetPropertyName ); if ( sourceRef != null ) { sourceReferences.add( sourceRef ); } @@ -1251,7 +1264,7 @@ private void applyParameterNameBasedMapping() { } } - private SourceReference getSourceRef(Parameter sourceParameter, String targetPropertyName) { + private SourceReference getSourceRefByTargetName(Parameter sourceParameter, String targetPropertyName) { SourceReference sourceRef = null; @@ -1261,11 +1274,11 @@ private SourceReference getSourceRef(Parameter sourceParameter, String targetPro Accessor sourceReadAccessor = sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName ); - - Accessor sourcePresenceChecker = - sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName ); - if ( sourceReadAccessor != null ) { + // property mapping + Accessor sourcePresenceChecker = + sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName ); + DeclaredType declaredSourceType = (DeclaredType) sourceParameter.getType().getTypeMirror(); Type returnType = ctx.getTypeFactory().getReturnType( declaredSourceType, sourceReadAccessor ); sourceRef = new SourceReference.BuilderFromProperty().sourceParameter( sourceParameter ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Mapper.java new file mode 100644 index 0000000000..7d152e88c1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Mapper.java @@ -0,0 +1,40 @@ +/* + * 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._2164; + +import java.math.BigDecimal; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2164Mapper { + + Issue2164Mapper INSTANCE = Mappers.getMapper( Issue2164Mapper.class ); + + @Mapping(target = "value", qualifiedByName = "truncate2") + Target map(BigDecimal value); + + @Named( "truncate2" ) + default String truncate2(String in) { + return in.substring( 0, 2 ); + } + + class Target { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Test.java new file mode 100644 index 0000000000..3ed5cf1654 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Test.java @@ -0,0 +1,31 @@ +/* + * 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._2164; + +import java.math.BigDecimal; + +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 static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("2164") +@WithClasses(Issue2164Mapper.class) +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2164Test { + + @Test + public void shouldSelectProperMethod() { + + Issue2164Mapper.Target target = Issue2164Mapper.INSTANCE.map( new BigDecimal( "1234" ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "12" ); + } +} From c96296254641c503c79b75834300cb4b2ba366ad Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 30 Aug 2020 16:31:44 +0200 Subject: [PATCH 094/597] [maven-release-plugin] prepare release 1.4.0.CR1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 6c908377c8..55926697b2 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.CR1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 63311b5950..4e8a37fc77 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.CR1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index c74e0b8bb1..c128f1449d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.CR1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index b404ed3cee..bface19897 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.CR1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index ceb3cb1db6..ab9098fb10 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.CR1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index a643bab4b8..e7a98ec35f 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.CR1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index b26cf8683d..28c895e9a4 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.CR1 pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.CR1 diff --git a/pom.xml b/pom.xml index f9c649c168..25a602f932 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.CR1 parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.CR1 diff --git a/processor/pom.xml b/processor/pom.xml index c047301268..5b704cc379 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.CR1 ../parent/pom.xml From 8b22654abdfb005381124800ebc155af2de3c312 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 30 Aug 2020 16:31:45 +0200 Subject: [PATCH 095/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 55926697b2..6c908377c8 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.CR1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 4e8a37fc77..63311b5950 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.CR1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index c128f1449d..c74e0b8bb1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.CR1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index bface19897..b404ed3cee 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.CR1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index ab9098fb10..ceb3cb1db6 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.CR1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index e7a98ec35f..a643bab4b8 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.CR1 + 1.4.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 28c895e9a4..b26cf8683d 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0.CR1 + 1.4.0-SNAPSHOT pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.CR1 + HEAD diff --git a/pom.xml b/pom.xml index 25a602f932..f9c649c168 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0.CR1 + 1.4.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.CR1 + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 5b704cc379..c047301268 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.CR1 + 1.4.0-SNAPSHOT ../parent/pom.xml From 427f5023efec6f6a91fd8946bb2b6e86a1ed2a19 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sun, 6 Sep 2020 16:12:01 +0200 Subject: [PATCH 096/597] #2195 @Beanmapping#resultType should be used to construct return type also if it's a builder (#2196) --- .../ap/internal/model/BeanMappingMethod.java | 30 +++++++------- .../processor/MapperCreationProcessor.java | 13 +++++- .../ap/test/bugs/_2195/Issue2195Mapper.java | 23 +++++++++++ .../ap/test/bugs/_2195/Issue2195Test.java | 35 ++++++++++++++++ .../ap/test/bugs/_2195/dto/Source.java | 18 +++++++++ .../ap/test/bugs/_2195/dto/Target.java | 31 ++++++++++++++ .../ap/test/bugs/_2195/dto/TargetBase.java | 40 +++++++++++++++++++ 7 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/TargetBase.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index a0ea064f1f..c2434da51d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -87,6 +87,7 @@ public static class Builder { private MappingBuilderContext ctx; private Method method; + private Type userDefinedReturnType; /* returnType to construct can have a builder */ private BuilderType returnTypeBuilder; @@ -108,6 +109,11 @@ public Builder mappingContext(MappingBuilderContext mappingContext) { return this; } + public Builder userDefinedReturnType(Type userDefinedReturnType) { + this.userDefinedReturnType = userDefinedReturnType; + return this; + } + public Builder returnTypeBuilder( BuilderType returnTypeBuilder ) { this.returnTypeBuilder = returnTypeBuilder; return this; @@ -148,20 +154,22 @@ public BeanMappingMethod build() { // determine which return type to construct boolean cannotConstructReturnType = false; if ( !method.getReturnType().isVoid() ) { - Type returnTypeImpl = getReturnTypeToConstructFromSelectionParameters( selectionParameters ); - if ( returnTypeImpl != null ) { + Type returnTypeImpl = null; + if ( isBuilderRequired() ) { + // the userDefinedReturn type can also require a builder. That buildertype is already set + returnTypeImpl = returnTypeBuilder.getBuilder(); initializeFactoryMethod( returnTypeImpl, selectionParameters ); - if ( factoryMethod != null || canResultTypeFromBeanMappingBeConstructed( returnTypeImpl ) ) { + if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } else { cannotConstructReturnType = true; } } - else if ( isBuilderRequired() ) { - returnTypeImpl = returnTypeBuilder.getBuilder(); + else if ( userDefinedReturnType != null ) { + returnTypeImpl = userDefinedReturnType; initializeFactoryMethod( returnTypeImpl, selectionParameters ); - if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) { + if ( factoryMethod != null || canResultTypeFromBeanMappingBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } else { @@ -482,16 +490,6 @@ private void sortPropertyMappingsByDependencies() { } } - private Type getReturnTypeToConstructFromSelectionParameters(SelectionParameters selectionParams) { - // resultType only applies to method that actually has @BeanMapping annotation, never to forged methods - if ( !( method instanceof ForgedMethod ) - && selectionParams != null - && selectionParams.getResultType() != null ) { - return ctx.getTypeFactory().getType( selectionParams.getResultType() ); - } - return null; - } - private boolean canResultTypeFromBeanMappingBeConstructed(Type resultType) { boolean error = true; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 58c6f2513a..25632350be 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -367,11 +367,14 @@ else if ( method.isStreamMapping() ) { else { this.messager.note( 1, Message.BEANMAPPING_CREATE_NOTE, method ); BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); + Type userDefinedReturnType = getUserDesiredReturnType( method ); + Type builderBaseType = userDefinedReturnType != null ? userDefinedReturnType : method.getReturnType(); BeanMappingMethod.Builder beanMappingBuilder = new BeanMappingMethod.Builder(); BeanMappingMethod beanMappingMethod = beanMappingBuilder .mappingContext( mappingContext ) .sourceMethod( method ) - .returnTypeBuilder( typeFactory.builderTypeFor( method.getReturnType(), builder ) ) + .userDefinedReturnType( userDefinedReturnType ) + .returnTypeBuilder( typeFactory.builderTypeFor( builderBaseType, builder ) ) .build(); // We can consider that the bean mapping method can always be constructed. If there is a problem @@ -392,6 +395,14 @@ else if ( method.isStreamMapping() ) { return mappingMethods; } + private Type getUserDesiredReturnType(SourceMethod method) { + SelectionParameters selectionParameters = method.getOptions().getBeanMapping().getSelectionParameters(); + if ( selectionParameters != null && selectionParameters.getResultType() != null ) { + return typeFactory.getType( selectionParameters.getResultType() ); + } + return null; + } + private M createWithElementMappingMethod(SourceMethod method, MappingMethodOptions mappingMethodOptions, ContainerMappingMethodBuilder builder) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Mapper.java new file mode 100644 index 0000000000..cde09dd2e3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Mapper.java @@ -0,0 +1,23 @@ +/* + * 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._2195; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.bugs._2195.dto.Source; +import org.mapstruct.ap.test.bugs._2195.dto.Target; +import org.mapstruct.ap.test.bugs._2195.dto.TargetBase; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2195Mapper { + + Issue2195Mapper INSTANCE = Mappers.getMapper( Issue2195Mapper.class ); + + @BeanMapping( resultType = Target.class ) + TargetBase map(Source source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Test.java new file mode 100644 index 0000000000..8d8a754d7f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Test.java @@ -0,0 +1,35 @@ +/* + * 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._2195; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.bugs._2195.dto.Source; +import org.mapstruct.ap.test.bugs._2195.dto.Target; +import org.mapstruct.ap.test.bugs._2195.dto.TargetBase; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("2195") +@WithClasses( { Source.class, Target.class, TargetBase.class } ) +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2195Test { + + @Test + @WithClasses( Issue2195Mapper.class ) + public void test() { + + Source source = new Source(); + source.setName( "JohnDoe" ); + + TargetBase target = Issue2195Mapper.INSTANCE.map( source ); + + assertThat( target ).isInstanceOf( Target.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/Source.java new file mode 100644 index 0000000000..78a4bba1a6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/Source.java @@ -0,0 +1,18 @@ +/* + * 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._2195.dto; + +public class Source { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/Target.java new file mode 100644 index 0000000000..8a74d44104 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/Target.java @@ -0,0 +1,31 @@ +/* + * 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._2195.dto; + +public class Target extends TargetBase { + + protected Target(Builder builder) { + super( builder ); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder extends TargetBase.Builder { + + protected Builder() { + } + + public Target build() { + return new Target( this ); + } + + public Builder name(String name) { + return this; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/TargetBase.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/TargetBase.java new file mode 100644 index 0000000000..ffb2eff2bc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/dto/TargetBase.java @@ -0,0 +1,40 @@ +/* + * 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._2195.dto; + +public class TargetBase { + + private final String name; + + protected TargetBase(Builder builder) { + this.name = builder.name; + } + + public static Builder builder() { + return new Builder(); + } + + public String getName() { + return name; + } + + public static class Builder { + + protected Builder() { + } + + private String name; + + public TargetBase build() { + return new TargetBase( this ); + } + + public Builder name(String name) { + this.name = name; + return this; + } + } +} From 52ab22bbd87c89610e913ecf6b4a891475846d33 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 19 Sep 2020 09:54:55 +0200 Subject: [PATCH 097/597] #2197 Strip leading underscores and digits when sanitizing identifier name --- .../mapstruct/ap/internal/util/Strings.java | 13 +++--- .../ap/internal/util/StringsTest.java | 11 +++++ .../ap/test/bugs/_2197/Issue2197Mapper.java | 46 +++++++++++++++++++ .../ap/test/bugs/_2197/Issue2197Test.java | 34 ++++++++++++++ .../test/bugs/_2197/Issue2197MapperImpl.java | 31 +++++++++++++ 5 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Test.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2197/Issue2197MapperImpl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java index f1eca88156..42727ed816 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java @@ -174,15 +174,16 @@ public static String getSafeVariableName(String name, Collection existin public static String sanitizeIdentifierName(String identifier) { if ( identifier != null && identifier.length() > 0 ) { - int firstNonUnderScoreIndex = 0; - while ( firstNonUnderScoreIndex < identifier.length() && - identifier.charAt( firstNonUnderScoreIndex ) == UNDERSCORE ) { - firstNonUnderScoreIndex++; + int firstAlphabeticIndex = 0; + while ( firstAlphabeticIndex < identifier.length() && + ( identifier.charAt( firstAlphabeticIndex ) == UNDERSCORE || + Character.isDigit( identifier.charAt( firstAlphabeticIndex ) ) ) ) { + firstAlphabeticIndex++; } - if ( firstNonUnderScoreIndex < identifier.length()) { + if ( firstAlphabeticIndex < identifier.length()) { // If it is not consisted of only underscores - return identifier.substring( firstNonUnderScoreIndex ).replace( "[]", "Array" ); + return identifier.substring( firstAlphabeticIndex ).replace( "[]", "Array" ); } return identifier.replace( "[]", "Array" ); diff --git a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java index e0eb961d10..9d6dc88655 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java @@ -85,6 +85,8 @@ public void testGetSaveVariableNameWithArrayExistingVariables() { assertThat( Strings.getSafeVariableName( "prop", "prop", "prop_" ) ).isEqualTo( "prop1" ); assertThat( Strings.getSafeVariableName( "_Test" ) ).isEqualTo( "test" ); assertThat( Strings.getSafeVariableName( "__Test" ) ).isEqualTo( "test" ); + assertThat( Strings.getSafeVariableName( "_0Test" ) ).isEqualTo( "test" ); + assertThat( Strings.getSafeVariableName( "_0123Test" ) ).isEqualTo( "test" ); } @Test @@ -103,6 +105,10 @@ public void testGetSaveVariableNameWithCollection() { assertThat( Strings.getSafeVariableName( "_Test", new ArrayList<>() ) ).isEqualTo( "test" ); assertThat( Strings.getSafeVariableName( "__Test", Arrays.asList( "test" ) ) ).isEqualTo( "test1" ); assertThat( Strings.getSafeVariableName( "___", new ArrayList<>() ) ).isEqualTo( "___" ); + assertThat( Strings.getSafeVariableName( "_0Test", new ArrayList<>() ) ).isEqualTo( "test" ); + assertThat( Strings.getSafeVariableName( "__0Test", Arrays.asList( "test" ) ) ).isEqualTo( "test1" ); + assertThat( Strings.getSafeVariableName( "___0", new ArrayList<>() ) ).isEqualTo( "___0" ); + assertThat( Strings.getSafeVariableName( "__0123456789Test", Arrays.asList( "test" ) ) ).isEqualTo( "test1" ); } @Test @@ -114,6 +120,11 @@ public void testSanitizeIdentifierName() { assertThat( Strings.sanitizeIdentifierName( "__int[]" ) ).isEqualTo( "intArray" ); assertThat( Strings.sanitizeIdentifierName( "test_" ) ).isEqualTo( "test_" ); assertThat( Strings.sanitizeIdentifierName( "___" ) ).isEqualTo( "___" ); + assertThat( Strings.sanitizeIdentifierName( "_0Test" ) ).isEqualTo( "Test" ); + assertThat( Strings.sanitizeIdentifierName( "_0123456789Test" ) ).isEqualTo( "Test" ); + assertThat( Strings.sanitizeIdentifierName( "_0int[]" ) ).isEqualTo( "intArray" ); + assertThat( Strings.sanitizeIdentifierName( "__0int[]" ) ).isEqualTo( "intArray" ); + assertThat( Strings.sanitizeIdentifierName( "___0" ) ).isEqualTo( "___0" ); } @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Mapper.java new file mode 100644 index 0000000000..def6f8f601 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Mapper.java @@ -0,0 +1,46 @@ +/* + * 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._2197; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2197Mapper { + + Issue2197Mapper INSTANCE = Mappers.getMapper( Issue2197Mapper.class ); + + _0Target map(Source source); + + // CHECKSTYLE:OFF + class _0Target { + // CHECKSTYLE:ON + private final String value; + + public _0Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Test.java new file mode 100644 index 0000000000..52be67564c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Test.java @@ -0,0 +1,34 @@ +/* + * 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._2197; + +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; + +@IssueKey("2197") +@WithClasses(Issue2197Mapper.class) +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2197Test { + + @Rule + public final GeneratedSource generatedSource = new GeneratedSource() + .addComparisonToFixtureFor( Issue2197Mapper.class ); + + @Test + public void underscoreAndDigitPrefixShouldBeStrippedFromGeneratedLocalVariables() { + Issue2197Mapper._0Target target = Issue2197Mapper.INSTANCE.map( new Issue2197Mapper.Source( "value1" ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "value1" ); + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2197/Issue2197MapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2197/Issue2197MapperImpl.java new file mode 100644 index 0000000000..dcf0bd2fed --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2197/Issue2197MapperImpl.java @@ -0,0 +1,31 @@ +/* + * 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._2197; + +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2019-02-10T09:58:11+0100", + comments = "version: , compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)" +) +public class Issue2197MapperImpl implements Issue2197Mapper { + + @Override + public _0Target map(Source source) { + if ( source == null ) { + return null; + } + + String value = null; + + value = source.getValue(); + + _0Target target = new _0Target( value ); + + return target; + } +} From e17e744b209d8c1d622c628469375c28b3c80d6f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 20 Sep 2020 11:06:33 +0200 Subject: [PATCH 098/597] Dependency upgrades Upgrades: * Maven Enforcer Plugin to 3.0.0-M3 * Maven Surefire Plugin to 3.0.0-M5 * Maven Checkstyle Plugin to 3.1.1 * Maven Bundle Plugin to 5.1.1 * Jacoco Maven Plugin to 0.8.6 * Checkstyle to 8.36.1 * JUnit Jipiter to 5.7.0 * AssertJ to 3.17.2 * Guava to 29.0-jre Fix AssertJ breaking changes Use Java 8 or Apache Commons IO instead of Guava where possible Update GitHub Actions to use JDK 14 and JDK 15-ea --- .github/workflows/java-ea.yml | 2 +- .github/workflows/main.yml | 2 +- parent/pom.xml | 19 +++++++++---------- .../ap/test/bugs/_1338/Issue1338Test.java | 6 ++---- .../ap/test/bugs/_1359/Issue1359Test.java | 6 +++--- .../ap/test/bugs/_895/Issue895Test.java | 2 +- .../nestedprop/expanding/FlattenedStock.java | 6 +++--- .../mapstruct/ap/test/collection/Target.java | 9 ++++----- .../StringListMapper.java | 4 +--- .../testutil/assertions/JavaFileAssert.java | 6 ++---- .../testutil/runner/CompilingStatement.java | 8 ++++---- 11 files changed, 31 insertions(+), 39 deletions(-) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index c20151eccd..a5afa065c7 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -7,7 +7,7 @@ jobs: strategy: fail-fast: false matrix: - java: [15-ea] + java: [16-ea] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d1f65d18f2..08278a1ade 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - java: [11, 13, 14] + java: [11, 13, 15] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: diff --git a/parent/pom.xml b/parent/pom.xml index b26cf8683d..e28caa20b8 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -22,17 +22,16 @@ UTF-8 1.0.0.Alpha2 - - 3.0.0-M1 - 3.0.0-M3 + 3.0.0-M3 + 3.0.0-M5 3.1.0 4.0.3.RELEASE 1.6.0 - 8.29 - 5.6.0 + 8.36.1 + 5.7.0 1 - 3.11.1 + 3.17.2 jdt_apt @@ -112,7 +111,7 @@ com.google.guava guava - 19.0 + 29.0-jre org.mapstruct.tools.gem @@ -307,7 +306,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.0 + 3.1.1 build-config/checkstyle.xml true @@ -383,7 +382,7 @@ org.apache.felix maven-bundle-plugin - 4.0.0 + 5.1.1 bundle-manifest @@ -529,7 +528,7 @@ org.jacoco jacoco-maven-plugin - 0.8.5 + 0.8.6 org.jvnet.jaxb2.maven2 diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1338/Issue1338Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1338/Issue1338Test.java index 8274b748ae..83c5212d23 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1338/Issue1338Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1338/Issue1338Test.java @@ -14,7 +14,6 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.atIndex; /** * @author Filip Hrisafov @@ -34,9 +33,8 @@ public void shouldCorrectlyUseAdder() { source.setProperties( Arrays.asList( "first", "second" ) ); Target target = Issue1338Mapper.INSTANCE.map( source ); - assertThat( target ) - .extracting( "properties" ) - .contains( Arrays.asList( "first", "second" ), atIndex( 0 ) ); + assertThat( target.getProperties() ) + .containsExactly( "first", "second" ); Source mapped = Issue1338Mapper.INSTANCE.map( target ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1359/Issue1359Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1359/Issue1359Test.java index 7434afa9fd..aa49cc59fe 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1359/Issue1359Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1359/Issue1359Test.java @@ -15,7 +15,7 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.atIndex; +import static org.assertj.core.api.InstanceOfAssertFactories.ITERABLE; /** * @author Filip Hrisafov @@ -33,14 +33,14 @@ public class Issue1359Test { public void shouldCompile() { Target target = new Target(); - assertThat( target ).extracting( "properties" ).contains( null, atIndex( 0 ) ); + assertThat( target ).extracting( "properties" ).isNull(); Set properties = new HashSet<>(); properties.add( "first" ); Source source = new Source( properties ); Issue1359Mapper.INSTANCE.map( target, source ); - assertThat( target ).extracting( "properties" ).contains( properties, atIndex( 0 ) ); + assertThat( target ).extracting( "properties", ITERABLE ).containsExactly( "first" ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_895/Issue895Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_895/Issue895Test.java index 9de76b43d8..d9f25f5fec 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_895/Issue895Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_895/Issue895Test.java @@ -32,6 +32,6 @@ public void properlyMapsMultiDimensionalArrays() { assertThat( listOfByteArray.getBytes() ).containsExactly( new byte[] { 0, 1 }, new byte[] { 1, 2 } ); arrayOfByteArray = Mappers.getMapper( MultiArrayMapper.class ).convert( listOfByteArray ); - assertThat( arrayOfByteArray.getBytes() ).containsExactly( new byte[] { 0, 1 }, new byte[] { 1, 2 } ); + assertThat( arrayOfByteArray.getBytes() ).isDeepEqualTo( new byte[][] { { 0, 1 }, { 1, 2 } } ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/expanding/FlattenedStock.java b/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/expanding/FlattenedStock.java index 70cbbb7f84..f7cce1a03f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/expanding/FlattenedStock.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/expanding/FlattenedStock.java @@ -5,7 +5,7 @@ */ package org.mapstruct.ap.test.builder.nestedprop.expanding; -import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; public class FlattenedStock { private String article1; @@ -16,8 +16,8 @@ public FlattenedStock() { } public FlattenedStock(String article1, String article2, int count) { - this.article1 = checkNotNull( article1 ); - this.article2 = checkNotNull( article2 ); + this.article1 = requireNonNull( article1 ); + this.article2 = requireNonNull( article2 ); this.count = count; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/Target.java b/processor/src/test/java/org/mapstruct/ap/test/collection/Target.java index 51a1edddab..2ff6cbfb2c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/Target.java @@ -8,14 +8,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - public class Target { //CHECKSTYLE:OFF @@ -54,10 +52,11 @@ public class Target { private StringHolderToLongMap nonGenericMapStringtoLong; public Target() { - otherStringLongMap = Maps.newHashMap(); + otherStringLongMap = new HashMap<>(); otherStringLongMap.put( "not-present-after-mapping", 42L ); - otherStringList = Lists.newArrayList( "not-present-after-mapping" ); + otherStringList = new ArrayList<>(); + otherStringList.add( "not-present-after-mapping" ); } public List getStringList() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/StringListMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/StringListMapper.java index 1cbe6e128d..93983acf55 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/StringListMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/StringListMapper.java @@ -8,12 +8,10 @@ import java.util.Arrays; import java.util.List; -import com.google.common.base.Joiner; - public class StringListMapper { public String stringListToString(List strings) { - return strings == null ? null : Joiner.on( "-" ).join( strings ); + return strings == null ? null : String.join( "-", strings ); } public List stringToStringList(String string) { diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java b/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java index 7a14619b04..2bcfbd94d3 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.io.FileUtils; import org.assertj.core.api.AbstractCharSequenceAssert; import org.assertj.core.api.Assertions; import org.assertj.core.api.FileAssert; @@ -23,9 +24,6 @@ import org.assertj.core.internal.Failures; import org.assertj.core.util.diff.Delta; -import com.google.common.base.Charsets; -import com.google.common.io.Files; - /** * Allows to perform assertions on .java source files. * @@ -58,7 +56,7 @@ public AbstractCharSequenceAssert content() { isFile(); try { - return Assertions.assertThat( Files.toString( actual, Charsets.UTF_8 ) ); + return Assertions.assertThat( FileUtils.readFileToString( actual, StandardCharsets.UTF_8 ) ); } catch ( IOException e ) { failWithMessage( "Unable to read" + actual.toString() + ". Exception: " + e.getMessage() ); diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java index 59916e9818..3e61d8bd9e 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; @@ -26,6 +27,7 @@ import java.util.stream.Stream; import com.puppycrawl.tools.checkstyle.api.AutomaticBean; +import org.apache.commons.io.output.NullOutputStream; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; import org.mapstruct.ap.testutil.WithClasses; @@ -41,8 +43,6 @@ import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor; import org.xml.sax.InputSource; -import com.google.common.collect.Lists; -import com.google.common.io.ByteStreams; import com.puppycrawl.tools.checkstyle.Checker; import com.puppycrawl.tools.checkstyle.ConfigurationLoader; import com.puppycrawl.tools.checkstyle.DefaultLogger; @@ -217,7 +217,7 @@ private void assertCheckstyleRules() throws Exception { ByteArrayOutputStream errorStream = new ByteArrayOutputStream(); checker.addListener( new DefaultLogger( - ByteStreams.nullOutputStream(), + NullOutputStream.NULL_OUTPUT_STREAM, AutomaticBean.OutputStreamOptions.CLOSE, errorStream, AutomaticBean.OutputStreamOptions.CLOSE @@ -234,7 +234,7 @@ private void assertCheckstyleRules() throws Exception { } private static List findGeneratedFiles(File file) { - final List files = Lists.newLinkedList(); + final List files = new LinkedList<>(); if ( file.canRead() ) { if ( file.isDirectory() ) { From 060f17e3e2e6d8ec9cd029d9721af2fa25a4c393 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 20 Sep 2020 12:48:47 +0200 Subject: [PATCH 099/597] #2125 Report an error when source parameter could not be determined from target mapping --- .../ap/internal/model/BeanMappingMethod.java | 27 +++++++ .../mapstruct/ap/internal/util/Message.java | 2 + .../bugs/_2077/Issue2077ErroneousMapper.java | 3 - .../ap/test/bugs/_2077/Issue2077Test.java | 5 +- .../mapstruct/ap/test/bugs/_2125/Comment.java | 27 +++++++ .../bugs/_2125/Issue2125ErroneousMapper.java | 28 ++++++++ .../ap/test/bugs/_2125/Issue2125Mapper.java | 28 ++++++++ .../ap/test/bugs/_2125/Issue2125Test.java | 71 +++++++++++++++++++ .../ap/test/bugs/_2125/Repository.java | 28 ++++++++ 9 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Comment.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125ErroneousMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Repository.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index c2434da51d..deeadc7eaf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -65,6 +65,8 @@ import static org.mapstruct.ap.internal.util.Message.GENERAL_ABSTRACT_RETURN_TYPE; import static org.mapstruct.ap.internal.util.Message.GENERAL_AMBIGUOUS_CONSTRUCTORS; import static org.mapstruct.ap.internal.util.Message.GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS; +import static org.mapstruct.ap.internal.util.Message.PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET; +import static org.mapstruct.ap.internal.util.Message.PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PROPERTY_FROM_TARGET; /** * A {@link MappingMethod} implemented by a {@link Mapper} class which maps one bean type to another, optionally @@ -1113,6 +1115,31 @@ else if ( mapping.getJavaExpression() != null ) { errorOccured = true; } } + else { + errorOccured = true; + + if ( method.getSourceParameters().size() == 1 ) { + ctx.getMessager() + .printMessage( + method.getExecutable(), + mapping.getMirror(), + mapping.getTargetAnnotationValue(), + PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PROPERTY_FROM_TARGET, + method.getSourceParameters().get( 0 ).getName(), + targetPropertyName + ); + } + else { + ctx.getMessager() + .printMessage( + method.getExecutable(), + mapping.getMirror(), + mapping.getTargetAnnotationValue(), + PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET, + targetPropertyName + ); + } + } } // remaining are the mappings without a 'source' so, 'only' a date format or qualifiers if ( propertyMapping != null ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 87d3a9abdd..67aa5849bc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -73,6 +73,8 @@ public enum Message { PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE( "No read accessor found for property \"%s\" in target type." ), PROPERTYMAPPING_NO_WRITE_ACCESSOR_FOR_TARGET_TYPE( "No write accessor found for property \"%s\" in target type." ), PROPERTYMAPPING_WHITESPACE_TRIMMED( "The property named \"%s\" has whitespaces, using trimmed property \"%s\" instead.", Diagnostic.Kind.WARNING ), + PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PROPERTY_FROM_TARGET("The type of parameter \"%s\" has no property named \"%s\". Please define the source property explicitly."), + PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET("No property named \"%s\" exists in source parameter(s). Please define the source explicitly."), CONVERSION_LOSSY_WARNING( "%s has a possibly lossy conversion from %s to %s.", Diagnostic.Kind.WARNING ), CONVERSION_LOSSY_ERROR( "Can't map %s. It has a possibly lossy conversion from %s to %s." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077ErroneousMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077ErroneousMapper.java index ecbff63802..9d38e8bf45 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077ErroneousMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077ErroneousMapper.java @@ -8,7 +8,6 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; -import org.mapstruct.factory.Mappers; /** * @author Sjaak Derksen @@ -16,8 +15,6 @@ @Mapper( unmappedTargetPolicy = ReportingPolicy.ERROR ) public interface Issue2077ErroneousMapper { - Issue2077ErroneousMapper INSTANCE = Mappers.getMapper( Issue2077ErroneousMapper.class ); - @Mapping(target = "s1", defaultValue = "xyz" ) Target map(String source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java index d8c3a1e5d7..332c3034dc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java @@ -30,8 +30,9 @@ public class Issue2077Test { diagnostics = { @Diagnostic(type = Issue2077ErroneousMapper.class, kind = ERROR, - line = 22, - message = "Unmapped target property: \"s1\".") + line = 18, + message = "The type of parameter \"source\" has no property named \"s1\". Please define the source " + + "property explicitly.") } ) public void shouldNotCompile() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Comment.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Comment.java new file mode 100644 index 0000000000..13fe680bb0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Comment.java @@ -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._2125; + +/** + * @author Filip Hrisafov + */ +public class Comment { + private final Integer issueId; + private final String comment; + + public Comment(Integer issueId, String comment) { + this.issueId = issueId; + this.comment = comment; + } + + public Integer getIssueId() { + return issueId; + } + + public String getComment() { + return comment; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125ErroneousMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125ErroneousMapper.java new file mode 100644 index 0000000000..bdca172003 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125ErroneousMapper.java @@ -0,0 +1,28 @@ +/* + * 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._2125; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +@Mapper +public interface Issue2125ErroneousMapper { + + @Mapping(target = "issueId", qualifiedByName = "mapIssueNumber") + @Mapping( target = "comment", ignore = true) + Comment clone(Repository repository); + + @Mapping(target = "issueId", qualifiedByName = "mapIssueNumber") + @Mapping( target = "comment", ignore = true) + Comment clone(Comment comment, Repository repository); + + @Named("mapIssueNumber") + default Integer mapIssueNumber(Integer issueNumber) { + return issueNumber != null ? issueNumber + 1 : null; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Mapper.java new file mode 100644 index 0000000000..3a867baf46 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Mapper.java @@ -0,0 +1,28 @@ +/* + * 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._2125; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2125Mapper { + + Issue2125Mapper INSTANCE = Mappers.getMapper( Issue2125Mapper.class ); + + Comment clone(Comment comment, Integer issueId); + + @Mapping(target = "issueId", qualifiedByName = "mapIssueNumber") + Comment cloneWithQualifier(Comment comment, Integer issueId); + + @Named("mapIssueNumber") + default Integer mapIssueNumber(Integer issueNumber) { + return issueNumber != null ? issueNumber + 1 : null; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java new file mode 100644 index 0000000000..38f235509f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java @@ -0,0 +1,71 @@ +/* + * 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._2125; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("2125") +@WithClasses({ + Comment.class, + Repository.class, +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2125Test { + + @Test + @WithClasses({ + Issue2125Mapper.class + }) + public void shouldSelectProperMethod() { + + Comment comment = Issue2125Mapper.INSTANCE.clone( + new Comment( 2125, "Fix issue" ), + 1000 + ); + + assertThat( comment ).isNotNull(); + assertThat( comment.getIssueId() ).isEqualTo( 2125 ); + + comment = Issue2125Mapper.INSTANCE.cloneWithQualifier( + new Comment( 2125, "Fix issue" ), + 1000 + ); + + assertThat( comment ).isNotNull(); + assertThat( comment.getIssueId() ).isEqualTo( 1001 ); + } + + @Test + @WithClasses({ + Issue2125ErroneousMapper.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = Issue2125ErroneousMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 15, + alternativeLine = 17, // For some reason javac reports the error on the method instead of the annotation + message = "The type of parameter \"repository\" has no property named \"issueId\". Please define the " + + "source property explicitly."), + @Diagnostic(type = Issue2125ErroneousMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 19, + alternativeLine = 21, // For some reason javac reports the error on the method instead of the annotation + message = "No property named \"issueId\" exists in source parameter(s). Please define the source " + + "explicitly.") + }) + public void shouldReportErrorWhenMultipleSourcesMatch() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Repository.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Repository.java new file mode 100644 index 0000000000..bf0c80984f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Repository.java @@ -0,0 +1,28 @@ +/* + * 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._2125; + +/** + * @author Filip Hrisafov + */ +public class Repository { + + private final String owner; + private final String name; + + public Repository(String owner, String name) { + this.owner = owner; + this.name = name; + } + + public String getOwner() { + return owner; + } + + public String getName() { + return name; + } +} From 9973b92ccb15d57dc6c871b7887fc9684a7950d5 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 26 Sep 2020 10:20:45 +0200 Subject: [PATCH 100/597] [maven-release-plugin] prepare release 1.4.0.Final --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 6c908377c8..b37ad28030 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Final ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 63311b5950..2adae1cbab 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Final ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index c74e0b8bb1..c5619f6226 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Final ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index b404ed3cee..5c4fa50c27 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Final ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index ceb3cb1db6..60cb9bac5b 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Final ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index a643bab4b8..9521e4e54a 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Final ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index e28caa20b8..8164300775 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Final pom MapStruct Parent @@ -63,7 +63,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.Final diff --git a/pom.xml b/pom.xml index f9c649c168..bb275f9902 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Final parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.0.Final diff --git a/processor/pom.xml b/processor/pom.xml index c047301268..5fdcae01f0 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0-SNAPSHOT + 1.4.0.Final ../parent/pom.xml From eb5b8bb71efd7a6bfa6f880bef2c503abdf007cf Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 26 Sep 2020 10:20:47 +0200 Subject: [PATCH 101/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index b37ad28030..aaae7b5404 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 2adae1cbab..293043e6f9 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index c5619f6226..b02b809ed7 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 5c4fa50c27..30beb1d319 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 60cb9bac5b..0426805375 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 9521e4e54a..f19ecfe343 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 8164300775..b38472cef0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Final + 1.5.0-SNAPSHOT pom MapStruct Parent @@ -63,7 +63,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.Final + HEAD diff --git a/pom.xml b/pom.xml index bb275f9902..28c35aea74 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Final + 1.5.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.0.Final + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 5fdcae01f0..bbec83bf29 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.0.Final + 1.5.0-SNAPSHOT ../parent/pom.xml From 2d750193d1f5fb8324836edb1b805ea8aaa62b26 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 26 Sep 2020 11:45:27 +0200 Subject: [PATCH 102/597] Update versions in readme to 1.4.0.Final --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index 5daba3f777..f92ea452b8 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.3.1.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.4.0.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.3.1.Final + 1.4.0.Final ... @@ -119,10 +119,10 @@ apply plugin: 'net.ltgt.apt-eclipse' dependencies { ... - compile 'org.mapstruct:mapstruct:1.3.1.Final' + compile 'org.mapstruct:mapstruct:1.4.0.Final' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.3.1.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.3.1.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.0.Final' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.0.Final' // if you are using mapstruct in test code } ... ``` From 4480e0f367b4a34a448d16c9c0f39c0640742d49 Mon Sep 17 00:00:00 2001 From: Jasper Vandemalle Date: Fri, 2 Oct 2020 20:43:11 +0200 Subject: [PATCH 103/597] Fix minor typos --- .../main/asciidoc/chapter-3-defining-a-mapper.asciidoc | 2 +- .../asciidoc/chapter-6-mapping-collections.asciidoc | 2 +- .../main/asciidoc/chapter-8-mapping-values.asciidoc | 2 +- .../mapstruct/ap/test/collection/adder/AdderTest.java | 2 +- ...bigiousMapperTest.java => AmbiguousMapperTest.java} | 2 +- .../ErroneousWithAmbiguousMethodsMapper.java | 2 +- ...rroneousWithMoreThanFiveAmbiguousMethodsMapper.java | 10 +++++----- .../manysourcearguments/ManySourceArgumentsTest.java | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) rename processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/{AmbigiousMapperTest.java => AmbiguousMapperTest.java} (99%) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 7d2dd052f6..54a0dbd709 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -540,7 +540,7 @@ When there are multiple constructors then the following is done to pick the one * If a constructor is annotated with an annotation named `@Default` (from any package) it will be used. * If a single public constructor exists then it will be used to construct the object, and the other non public constructors will be ignored. * If a parameterless constructor exists then it will be used to construct the object, and the other constructors will be ignored. -* If there are multiple eligible constructors then there will be a compilation error due to ambigious constructors. In order to break the ambiquity an annotation named `@Default` (from any package) can used. +* If there are multiple eligible constructors then there will be a compilation error due to ambiguous constructors. In order to break the ambiguity an annotation named `@Default` (from any package) can used. .Deciding which constructor to use ==== diff --git a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc index 0713316a46..dcce306789 100644 --- a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc +++ b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc @@ -151,7 +151,7 @@ public Map stringStringMapToLongDateMap(Map source) MapStruct has a `CollectionMappingStrategy`, with the possible values: `ACCESSOR_ONLY`, `SETTER_PREFERRED`, `ADDER_PREFERRED` and `TARGET_IMMUTABLE`. -In the table below, the dash `-` indicates a property name. Next, the trailing `s` indicates the plural form. The table explains the options and how they are applied to the presence/absense of a `set-s`, `add-` and / or `get-s` method on the target object: +In the table below, the dash `-` indicates a property name. Next, the trailing `s` indicates the plural form. The table explains the options and how they are applied to the presence/absence of a `set-s`, `add-` and / or `get-s` method on the target object: .Collection mapping strategy options |=== diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index 000fd06523..e38cee215b 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -155,7 +155,7 @@ MapStruct supports enum to a String mapping along the same lines as is described *`String` to enum* -1. Similarity: All not explicit defined mappings will result in the target enum constant mapped from the `String` value when that maches the target enum constant name. +1. Similarity: All not explicit defined mappings will result in the target enum constant mapped from the `String` value when that matches the target enum constant name. 2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. 3. Similarity: `` will create a mapping for each target enum constant and proceed to the switch/default clause value. 4. Difference: A switch/default value needs to be provided to have a determined outcome (enum has a limited set of values, `String` has unlimited options). Failing to specify `` or ` will result in a warning. diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/AdderTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/AdderTest.java index 3398a99d79..d3c667099c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/AdderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/AdderTest.java @@ -178,7 +178,7 @@ public void testShouldPreferHumanSingular() { } @Test - public void testShouldFallBackToDaliSingularInAbsenseOfHumanSingular() { + public void testShouldFallBackToDaliSingularInAbsenceOfHumanSingular() { AdderUsageObserver.setUsed( false ); SourceTeeth source = new SourceTeeth(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbiguousMapperTest.java similarity index 99% rename from processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java rename to processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbiguousMapperTest.java index c13f1edd2f..3c4bd00a7e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbigiousMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbiguousMapperTest.java @@ -17,7 +17,7 @@ @IssueKey("2156") @RunWith(AnnotationProcessorTestRunner.class) -public class AmbigiousMapperTest { +public class AmbiguousMapperTest { @Test @WithClasses( ErroneousWithAmbiguousMethodsMapper.class) diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithAmbiguousMethodsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithAmbiguousMethodsMapper.java index 7ebce015d1..165f8d5945 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithAmbiguousMethodsMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithAmbiguousMethodsMapper.java @@ -19,7 +19,7 @@ default LeafEntity map1(LeafDTO dto) { return new LeafEntity(); } - // duplicated method, triggering ambigious mapping method + // duplicated method, triggering ambiguous mapping method default LeafEntity map2(LeafDTO dto) { return new LeafEntity(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithMoreThanFiveAmbiguousMethodsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithMoreThanFiveAmbiguousMethodsMapper.java index 1e050cea71..2ca70ccaf4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithMoreThanFiveAmbiguousMethodsMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/ErroneousWithMoreThanFiveAmbiguousMethodsMapper.java @@ -20,28 +20,28 @@ default LeafEntity map1(LeafDTO dto) { return new LeafEntity(); } - // duplicated method, triggering ambigious mapping method + // duplicated method, triggering ambiguous mapping method default LeafEntity map2(LeafDTO dto) { return new LeafEntity(); } - // duplicated method, triggering ambigious mapping method + // duplicated method, triggering ambiguous mapping method default LeafEntity map3(LeafDTO dto) { return new LeafEntity(); } - // duplicated method, triggering ambigious mapping method + // duplicated method, triggering ambiguous mapping method default LeafEntity map4(LeafDTO dto) { return new LeafEntity(); } - // duplicated method, triggering ambigious mapping method + // duplicated method, triggering ambiguous mapping method default LeafEntity map5(LeafDTO dto) { return new LeafEntity(); } - // duplicated method, triggering ambigious mapping method + // duplicated method, triggering ambiguous mapping method default LeafEntity map6(LeafDTO dto) { return new LeafEntity(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java index 549397def7..63aaf8b654 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java @@ -176,7 +176,7 @@ public void shouldUseConfig() { line = 16, message = "Several possible source properties for target property \"description\".") }) - public void shouldFailToGenerateMappingsForAmbigiousSourceProperty() { + public void shouldFailToGenerateMappingsForAmbiguousSourceProperty() { } @Test From 233fc6de98aa662172bd1b00401c574e25ae371d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 3 Oct 2020 12:46:14 +0200 Subject: [PATCH 104/597] #2215: Avoid NPE in IntelliJ EAP 2020.3 Starting from IntelliJ 2020.3 (Build 203.4203.26) the ProcessingEnvironment is wrapped in a Proxy class by IntelliJ. MapStruct should gracefully handle that and not throw an NPE. Additionally, we should do best effort to put the used compiler in the generated annotation info --- .../processor/DefaultVersionInformation.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java index 60e5f112d2..c4baf1bf5f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java @@ -6,6 +6,8 @@ package org.mapstruct.ap.internal.processor; import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.net.URL; import java.util.jar.Manifest; @@ -103,7 +105,28 @@ static DefaultVersionInformation fromProcessingEnvironment(ProcessingEnvironment } private static String getCompiler(ProcessingEnvironment processingEnv) { - String className = processingEnv.getClass().getName(); + String className; + if ( Proxy.isProxyClass( processingEnv.getClass() ) ) { + // IntelliJ IDEA wraps the ProcessingEnvironment in a Proxy. + // Therefore we need smarter logic to determine the type of the compiler + String processingEnvToString = processingEnv.toString(); + if ( processingEnvToString.contains( COMPILER_NAME_JAVAC ) ) { + // The toString of the javac ProcessingEnvironment is "javac ProcessingEnvironment" + className = JAVAC_PE_CLASS; + } + else if ( processingEnvToString.startsWith( JDT_BATCH_PE_CLASS ) ) { + // The toString of the JDT Batch is from Object#toString so it contains the class name + className = JDT_BATCH_PE_CLASS; + } + else { + InvocationHandler invocationHandler = Proxy.getInvocationHandler( processingEnv ); + return "Proxy handler " + invocationHandler.getClass() + " from " + + getLibraryName( invocationHandler.getClass(), false ); + } + } + else { + className = processingEnv.getClass().getName(); + } if ( className.equals( JAVAC_PE_CLASS ) ) { return COMPILER_NAME_JAVAC; @@ -135,7 +158,10 @@ private static String getLibraryName(Class clazz, boolean preferVersionOnly) } } - if ( "jar".equals( resource.getProtocol() ) ) { + if ( resource == null ) { + return ""; + } + else if ( "jar".equals( resource.getProtocol() ) ) { return extractJarFileName( resource.getFile() ); } else if ( "jrt".equals( resource.getProtocol() ) ) { @@ -149,6 +175,9 @@ else if ( "bundleresource".equals( resource.getProtocol() ) && manifest != null } private static Manifest openManifest(String classFileName, URL resource) { + if ( resource == null ) { + return null; + } try { URL manifestUrl = createManifestUrl( classFileName, resource ); return new Manifest( manifestUrl.openStream() ); From 823b5edd9f5215d00321b37153e1838fa0485977 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 3 Oct 2020 10:20:03 +0200 Subject: [PATCH 105/597] #2213: primitive arrays should be directly mapped (we are cloning them anyways) Additionally fix problem when annotations `ElementType.TYPE_USE` not handled correctly for javac --- .../ap/internal/model/common/TypeFactory.java | 4 +- .../creation/MappingResolverImpl.java | 2 +- .../org/mapstruct/ap/test/bugs/_2213/Car.java | 28 ++++++++++ .../mapstruct/ap/test/bugs/_2213/Car2.java | 28 ++++++++++ .../ap/test/bugs/_2213/CarMapper.java | 22 ++++++++ .../ap/test/bugs/_2213/Issue2213Test.java | 53 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2213/NotNull.java | 22 ++++++++ .../ap/test/bugs/_2213/CarMapperImpl.java | 37 +++++++++++++ 8 files changed, 194 insertions(+), 2 deletions(-) create mode 100755 processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Car.java create mode 100755 processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Car2.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/CarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Issue2213Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/NotNull.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2213/CarMapperImpl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 9cfe18c176..ba1dcc1920 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -274,7 +274,9 @@ else if (componentTypeMirror.getKind().isPrimitive()) { else { isEnumType = false; isInterface = false; - name = mirror.toString(); + // When the component type is primitive and is annotated with ElementType.TYPE_USE then + // the typeMirror#toString returns (@CustomAnnotation :: byte) for the javac compiler + name = mirror.getKind().isPrimitive() ? NativeTypes.getName( mirror.getKind() ) : mirror.toString(); packageName = null; qualifiedName = name; typeElement = null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index fe7635f7f6..73392a00f8 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -367,7 +367,7 @@ private boolean allowDirect(Type type) { } if ( type.isArrayType() ) { - return type.isJavaLangType(); + return type.isJavaLangType() || type.getComponentType().isPrimitive(); } if ( type.isIterableOrStreamType() ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Car.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Car.java new file mode 100755 index 0000000000..4f2ec92af6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Car.java @@ -0,0 +1,28 @@ +/* + * 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._2213; + +public class Car { + private int[] intData; + private Long[] longData; + + public int[] getIntData() { + return intData; + } + + public void setIntData(int[] intData) { + this.intData = intData; + } + + public Long[] getLongData() { + return longData; + } + + public void setLongData(Long[] longData) { + this.longData = longData; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Car2.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Car2.java new file mode 100755 index 0000000000..ec0cd6de6d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Car2.java @@ -0,0 +1,28 @@ +/* + * 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._2213; + +public class Car2 { + private int[] intData; + private Long[] longData; + + public @NotNull int[] getIntData() { + return intData; + } + + public void setIntData(int[] intData) { + this.intData = intData; + } + + public @NotNull Long[] getLongData() { + return longData; + } + + public void setLongData(Long[] longData) { + this.longData = longData; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/CarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/CarMapper.java new file mode 100644 index 0000000000..f3b25c0213 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/CarMapper.java @@ -0,0 +1,22 @@ +/* + * 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._2213; + +import org.mapstruct.Mapper; +import org.mapstruct.control.DeepClone; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(mappingControl = DeepClone.class) +public interface CarMapper { + + CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); + + Car toCar(Car2 car2); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Issue2213Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Issue2213Test.java new file mode 100644 index 0000000000..613b981d04 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Issue2213Test.java @@ -0,0 +1,53 @@ +/* + * 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._2213; + +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; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + NotNull.class, + CarMapper.class, + Car.class, + Car2.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +@IssueKey("2213") +public class Issue2213Test { + + @Rule + public final GeneratedSource generatedSource = new GeneratedSource() + .addComparisonToFixtureFor( CarMapper.class ); + + @Test + public void testShouldNotGenerateIntermediatePrimitiveMappingMethod() { + Car2 car = new Car2(); + int[] sourceInt = { 1, 2, 3 }; + car.setIntData( sourceInt ); + Long[] sourceLong = { 1L, 2L, 3L }; + car.setLongData( sourceLong ); + Car target = CarMapper.INSTANCE.toCar( car ); + + assertThat( target ).isNotNull(); + assertThat( target.getIntData() ) + .containsExactly( 1, 2, 3 ) + .isNotSameAs( sourceInt ); + assertThat( target.getLongData() ) + .containsExactly( 1L, 2L, 3L ) + .isNotSameAs( sourceLong ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/NotNull.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/NotNull.java new file mode 100644 index 0000000000..3b45f03ed4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/NotNull.java @@ -0,0 +1,22 @@ +/* + * 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._2213; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ + ElementType.METHOD, + ElementType.TYPE_USE +}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface NotNull { + +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2213/CarMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2213/CarMapperImpl.java new file mode 100644 index 0000000000..794d6d4864 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2213/CarMapperImpl.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.bugs._2213; + +import java.util.Arrays; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2020-10-03T10:12:15+0200", + comments = "version: , compiler: javac, environment: Java 11.0.4 (AdoptOpenJDK)" +) +public class CarMapperImpl implements CarMapper { + + @Override + public Car toCar(Car2 car2) { + if ( car2 == null ) { + return null; + } + + Car car = new Car(); + + int[] intData = car2.getIntData(); + if ( intData != null ) { + car.setIntData( Arrays.copyOf( intData, intData.length ) ); + } + Long[] longData = car2.getLongData(); + if ( longData != null ) { + car.setLongData( Arrays.copyOf( longData, longData.length ) ); + } + + return car; + } +} From a5f49e591ed3bb2e5c2b7565c0a9f968be47643d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 7 Oct 2020 23:03:16 +0200 Subject: [PATCH 106/597] #2221: Fix configuration inheritance when there are multiple matching source parameters of the same type --- .../model/beanmapping/SourceReference.java | 24 ++++++++--- .../ap/test/bugs/_2221/Issue2221Test.java | 43 +++++++++++++++++++ .../ap/test/bugs/_2221/RestConfig.java | 21 +++++++++ .../ap/test/bugs/_2221/RestSiteDto.java | 34 +++++++++++++++ .../ap/test/bugs/_2221/RestSiteMapper.java | 22 ++++++++++ .../mapstruct/ap/test/bugs/_2221/SiteDto.java | 34 +++++++++++++++ 6 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/Issue2221Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestSiteDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestSiteMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/SiteDto.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index cf52c96a2d..1d747c190e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.type.DeclaredType; @@ -239,11 +238,24 @@ private Parameter getSourceParameterFromMethodOrTemplate(String parameterName ) if ( isForwarded ) { Parameter parameter = Parameter.getSourceParameter( templateMethod.getParameters(), parameterName ); if ( parameter != null ) { - result = method.getSourceParameters() - .stream() - .filter( p -> p.getType().isAssignableTo( parameter.getType() ) ) - .collect( Collectors.reducing( (a, b) -> null ) ) - .orElse( null ); + + // When forward inheriting we should find the matching source parameter by type + // If there are multiple parameters of the same type + // then we fallback to match the parameter name to the current method source parameters + for ( Parameter sourceParameter : method.getSourceParameters() ) { + if ( sourceParameter.getType().isAssignableTo( parameter.getType() ) ) { + if ( result == null ) { + result = sourceParameter; + } + else { + // When we reach here it means that we found a second source parameter + // that has the same type, then fallback to the matching source parameter + // in the current method + result = Parameter.getSourceParameter( method.getParameters(), parameterName ); + break; + } + } + } } } else { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/Issue2221Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/Issue2221Test.java new file mode 100644 index 0000000000..34514de929 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/Issue2221Test.java @@ -0,0 +1,43 @@ +/* + * 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._2221; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2221") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + RestConfig.class, + RestSiteDto.class, + RestSiteMapper.class, + SiteDto.class, +}) +public class Issue2221Test { + + @Test + public void multiSourceInheritConfigurationShouldWork() { + SiteDto site = RestSiteMapper.INSTANCE.convert( + new RestSiteDto( "restTenant", "restSite", "restCti" ), + "parameterTenant", + "parameterSite" + ); + + assertThat( site ).isNotNull(); + assertThat( site.getTenantId() ).isEqualTo( "parameterTenant" ); + assertThat( site.getSiteId() ).isEqualTo( "parameterSite" ); + assertThat( site.getCtiId() ).isEqualTo( "restCti" ); + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestConfig.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestConfig.java new file mode 100644 index 0000000000..9a0f79f82d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestConfig.java @@ -0,0 +1,21 @@ +/* + * 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._2221; + +import org.mapstruct.MapperConfig; +import org.mapstruct.Mapping; + +/** + * @author Filip Hrisafov + */ +@MapperConfig +public interface RestConfig { + + @Mapping(target = "tenantId", source = "tenantId") + @Mapping(target = "siteId", source = "siteId") + @Mapping(target = "ctiId", source = "source.cti", defaultValue = "unknown") + SiteDto convert(RestSiteDto source, String tenantId, String siteId); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestSiteDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestSiteDto.java new file mode 100644 index 0000000000..eb1a56b344 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestSiteDto.java @@ -0,0 +1,34 @@ +/* + * 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._2221; + +/** + * @author Filip Hrisafov + */ +public class RestSiteDto { + + private final String tenantId; + private final String siteId; + private final String cti; + + public RestSiteDto(String tenantId, String siteId, String cti) { + this.tenantId = tenantId; + this.siteId = siteId; + this.cti = cti; + } + + public String getTenantId() { + return tenantId; + } + + public String getSiteId() { + return siteId; + } + + public String getCti() { + return cti; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestSiteMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestSiteMapper.java new file mode 100644 index 0000000000..cf7281f44f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/RestSiteMapper.java @@ -0,0 +1,22 @@ +/* + * 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._2221; + +import org.mapstruct.InheritConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(config = RestConfig.class) +public interface RestSiteMapper { + + RestSiteMapper INSTANCE = Mappers.getMapper( RestSiteMapper.class ); + + @InheritConfiguration + SiteDto convert(RestSiteDto source, String tenantId, String siteId); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/SiteDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/SiteDto.java new file mode 100644 index 0000000000..7c80410029 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/SiteDto.java @@ -0,0 +1,34 @@ +/* + * 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._2221; + +/** + * @author Filip Hrisafov + */ +public class SiteDto { + + private final String tenantId; + private final String siteId; + private final String ctiId; + + public SiteDto(String tenantId, String siteId, String ctiId) { + this.tenantId = tenantId; + this.siteId = siteId; + this.ctiId = ctiId; + } + + public String getTenantId() { + return tenantId; + } + + public String getSiteId() { + return siteId; + } + + public String getCtiId() { + return ctiId; + } +} From 53a5c34ed62739feb786a6840b5fe885296f10da Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 7 Oct 2020 21:42:26 +0200 Subject: [PATCH 107/597] #2206, #2214, #2220: Source property should be correctly determined when only target is defined When having multiple source properties and only target is defined then the same rules should be applied as if there was no mapping: * First we check for a matching property in any of the source type * Second we check if the parameter name matches --- .../ap/internal/model/BeanMappingMethod.java | 32 ++++++++++++++- .../ap/test/bugs/_2125/Issue2125Mapper.java | 7 ++++ .../ap/test/bugs/_2125/Issue2125Test.java | 14 ++++--- .../CountryMapperMultipleSources.java | 23 +++++++++++ .../test/defaultvalue/DefaultValueTest.java | 34 ++++++++++++++++ .../ErroneousMissingSourceMapper.java | 39 +++++++++++++++++++ 6 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultvalue/CountryMapperMultipleSources.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultvalue/ErroneousMissingSourceMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index deeadc7eaf..87b8a28b4d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1070,8 +1070,36 @@ else if ( mapping.getJavaExpression() != null ) { SourceReference sourceRef = mappingRef.getSourceReference(); // sourceRef is not defined, check if a source property has the same name - if ( sourceRef == null && method.getSourceParameters().size() == 1 ) { - sourceRef = getSourceRefByTargetName( method.getSourceParameters().get( 0 ), targetPropertyName ); + if ( sourceRef == null ) { + // Here we follow the same rules as when we implicitly map + // When we implicitly map we first do property name based mapping + // i.e. look for matching properties in the source types + // and then do parameter name based mapping + for ( Parameter sourceParameter : method.getSourceParameters() ) { + SourceReference matchingSourceRef = getSourceRefByTargetName( + sourceParameter, + targetPropertyName + ); + if ( matchingSourceRef != null ) { + if ( sourceRef != null ) { + errorOccured = true; + // This can only happen when the target property matches multiple properties + // within the different source parameters + ctx.getMessager() + .printMessage( + method.getExecutable(), + mappingRef.getMapping().getMirror(), + Message.BEANMAPPING_SEVERAL_POSSIBLE_SOURCES, + targetPropertyName + ); + break; + } + // We can't break here since it is possible that the same property exists in multiple + // source parameters + sourceRef = matchingSourceRef; + } + } + } if ( sourceRef == null ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Mapper.java index 3a867baf46..83cf0e1b28 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Mapper.java @@ -15,11 +15,18 @@ public interface Issue2125Mapper { Issue2125Mapper INSTANCE = Mappers.getMapper( Issue2125Mapper.class ); + // In this case the issueId from the Comment is used Comment clone(Comment comment, Integer issueId); + // When source is not defined then we will use the issueId from the Comment, + // same as when there was no mapping @Mapping(target = "issueId", qualifiedByName = "mapIssueNumber") Comment cloneWithQualifier(Comment comment, Integer issueId); + // When source is defined then we will source the parameter name + @Mapping(target = "issueId", source = "issueId", qualifiedByName = "mapIssueNumber") + Comment cloneWithQualifierExplicitSource(Comment comment, Integer issueId); + @Named("mapIssueNumber") default Integer mapIssueNumber(Integer issueNumber) { return issueNumber != null ? issueNumber + 1 : null; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java index 38f235509f..e98afe7d52 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java @@ -43,6 +43,14 @@ public void shouldSelectProperMethod() { 1000 ); + assertThat( comment ).isNotNull(); + assertThat( comment.getIssueId() ).isEqualTo( 2126 ); + + comment = Issue2125Mapper.INSTANCE.cloneWithQualifierExplicitSource( + new Comment( 2125, "Fix issue" ), + 1000 + ); + assertThat( comment ).isNotNull(); assertThat( comment.getIssueId() ).isEqualTo( 1001 ); } @@ -59,12 +67,6 @@ public void shouldSelectProperMethod() { alternativeLine = 17, // For some reason javac reports the error on the method instead of the annotation message = "The type of parameter \"repository\" has no property named \"issueId\". Please define the " + "source property explicitly."), - @Diagnostic(type = Issue2125ErroneousMapper.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 19, - alternativeLine = 21, // For some reason javac reports the error on the method instead of the annotation - message = "No property named \"issueId\" exists in source parameter(s). Please define the source " + - "explicitly.") }) public void shouldReportErrorWhenMultipleSourcesMatch() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/CountryMapperMultipleSources.java b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/CountryMapperMultipleSources.java new file mode 100644 index 0000000000..11a424d929 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/CountryMapperMultipleSources.java @@ -0,0 +1,23 @@ +/* + * 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.defaultvalue; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CountryMapperMultipleSources { + + CountryMapperMultipleSources INSTANCE = Mappers.getMapper( CountryMapperMultipleSources.class ); + + @Mapping(target = "code", defaultValue = "CH") + @Mapping(target = "region", source = "regionCode") + CountryDts map(CountryEntity entity, String regionCode); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java index ff7bf00722..e834df3e3f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java @@ -159,4 +159,38 @@ public void errorOnDefaultValueAndConstant() { public void errorOnDefaultValueAndExpression() { } + @Test + @IssueKey("2214") + @WithClasses({ + CountryMapperMultipleSources.class, + Region.class, + }) + public void shouldBeAbleToDetermineDefaultValueBasedOnOnlyTargetType() { + CountryEntity entity = new CountryEntity(); + CountryDts target = CountryMapperMultipleSources.INSTANCE.map( entity, "ZH" ); + + assertThat( target ).isNotNull(); + assertThat( target.getCode() ).isEqualTo( "CH" ); + } + + @Test + @IssueKey("2220") + @WithClasses({ + ErroneousMissingSourceMapper.class, + Region.class, + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = ErroneousMissingSourceMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + message = "The type of parameter \"tenant\" has no property named \"type\"." + + " Please define the source property explicitly."), + } + ) + public void errorWhenOnlyTargetDefinedAndSourceDoesNotHaveProperty() { + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/ErroneousMissingSourceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/ErroneousMissingSourceMapper.java new file mode 100644 index 0000000000..54fd3cc645 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/ErroneousMissingSourceMapper.java @@ -0,0 +1,39 @@ +/* + * 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.defaultvalue; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousMissingSourceMapper { + + @Mapping(target = "type", defaultValue = "STANDARD") + Tenant map(TenantDto tenant); + + class Tenant { + + private final String id; + private final String type; + + public Tenant(String id, String type) { + this.id = id; + this.type = type; + } + } + + class TenantDto { + + private final String id; + + public TenantDto(String id) { + this.id = id; + } + } +} From 2b95e07d8e2648dccfbdbd8ba1f2fc3a4f67a303 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 10 Oct 2020 10:22:43 +0200 Subject: [PATCH 108/597] Turn of Maven connection pooling to avoid connection issues on CI The problem and solution are explained in https://github.com/actions/virtual-environments/issues/1499#issuecomment-689467080 --- .github/workflows/java-ea.yml | 5 ++++- .github/workflows/main.yml | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index a5afa065c7..3e901b6119 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -2,6 +2,9 @@ name: Java EA on: [push] +env: + MAVEN_ARGS: -V -B --no-transfer-progress -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 + jobs: test_jdk_ea: strategy: @@ -18,4 +21,4 @@ jobs: with: java-version: ${{ matrix.java }} - name: 'Test' - run: ./mvnw -V -B --no-transfer-progress install -DskipDistribution=true + run: ./mvnw ${MAVEN_ARGS} install -DskipDistribution=true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 08278a1ade..f63a183d4b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,6 +5,7 @@ on: [push, pull_request] env: SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + MAVEN_ARGS: -V -B --no-transfer-progress -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 jobs: test_jdk: @@ -22,7 +23,7 @@ jobs: with: java-version: ${{ matrix.java }} - name: 'Test' - run: ./mvnw -V -B --no-transfer-progress install -DskipDistribution=true + run: ./mvnw ${MAVEN_ARGS} install -DskipDistribution=true linux: name: 'Linux JDK 8' runs-on: ubuntu-latest @@ -34,7 +35,7 @@ jobs: with: java-version: 8 - name: 'Test' - run: ./mvnw -V -B --no-transfer-progress install + run: ./mvnw ${MAVEN_ARGS} install - name: 'Generate coverage report' run: ./mvnw jacoco:report - name: 'Upload coverage to Codecov' @@ -52,7 +53,7 @@ jobs: with: java-version: 8 - name: 'Test' - run: ./mvnw -V -B --no-transfer-progress install + run: ./mvnw ${MAVEN_ARGS} install mac: name: 'Mac OS' runs-on: macos-latest @@ -63,4 +64,4 @@ jobs: with: java-version: 8 - name: 'Test' - run: ./mvnw -V -B --no-transfer-progress install + run: ./mvnw ${MAVEN_ARGS} install From d8f22f83116833b2a90469d748c7c966e93215e5 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 11 Oct 2020 09:35:30 +0200 Subject: [PATCH 109/597] [maven-release-plugin] prepare release 1.4.1.Final --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index aaae7b5404..bd8b1c943d 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.4.1.Final ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 293043e6f9..5367c3e97e 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.4.1.Final ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index b02b809ed7..d01dd8efcf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.4.1.Final ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 30beb1d319..9529a1f85c 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.4.1.Final ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 0426805375..47c5a55a33 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.4.1.Final ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index f19ecfe343..51bd59a6c9 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.4.1.Final ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index b38472cef0..e8255cb881 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.4.1.Final pom MapStruct Parent @@ -63,7 +63,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.1.Final diff --git a/pom.xml b/pom.xml index 28c35aea74..3ee2ea53f8 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.4.1.Final parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.4.1.Final diff --git a/processor/pom.xml b/processor/pom.xml index bbec83bf29..4f63f685dd 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.4.1.Final ../parent/pom.xml From 58dbaee472c32a5f98535df293df621481561879 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 11 Oct 2020 09:35:30 +0200 Subject: [PATCH 110/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index bd8b1c943d..aaae7b5404 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.1.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 5367c3e97e..293043e6f9 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.1.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index d01dd8efcf..b02b809ed7 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.1.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 9529a1f85c..30beb1d319 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.1.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 47c5a55a33..0426805375 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.1.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 51bd59a6c9..f19ecfe343 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.1.Final + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index e8255cb881..b38472cef0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.4.1.Final + 1.5.0-SNAPSHOT pom MapStruct Parent @@ -63,7 +63,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.1.Final + HEAD diff --git a/pom.xml b/pom.xml index 3ee2ea53f8..28c35aea74 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.4.1.Final + 1.5.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.4.1.Final + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 4f63f685dd..bbec83bf29 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.4.1.Final + 1.5.0-SNAPSHOT ../parent/pom.xml From 0e902d6412d07d27a8efb2b72ec72ebcbbced55e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 11 Oct 2020 10:00:24 +0200 Subject: [PATCH 111/597] Update versions in readme to 1.4.1.Final --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index f92ea452b8..c39a408ac2 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.4.0.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.4.1.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.4.0.Final + 1.4.1.Final ... @@ -119,10 +119,10 @@ apply plugin: 'net.ltgt.apt-eclipse' dependencies { ... - compile 'org.mapstruct:mapstruct:1.4.0.Final' + compile 'org.mapstruct:mapstruct:1.4.1.Final' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.0.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.0.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.1.Final' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.1.Final' // if you are using mapstruct in test code } ... ``` From 4ddfd2ff510cfe5dcb630a1d371cb837ff915391 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 23:18:51 +0000 Subject: [PATCH 112/597] Bump junit from 4.12 to 4.13.1 in /parent Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index b38472cef0..9e5c04f27a 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -126,7 +126,7 @@ junit junit - 4.12 + 4.13.1 com.puppycrawl.tools From 50aa9cdbdc5cd721dfe48721059544faf2c818ac Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 17 Oct 2020 12:30:31 +0200 Subject: [PATCH 113/597] Use junit version defined in parent in integration tests --- .../faultyAstModifyingAnnotationProcessorTest/usage/pom.xml | 1 - .../src/test/resources/superTypeGenerationTest/usage/pom.xml | 1 - .../src/test/resources/targetTypeGenerationTest/usage/pom.xml | 1 - .../src/test/resources/usesTypeGenerationTest/usage/pom.xml | 1 - 4 files changed, 4 deletions(-) diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml index cc0d0e4f98..90c95aa83b 100644 --- a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml @@ -23,7 +23,6 @@ junit junit - 4.12 test diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml index 07e89a9808..ee0d556b9f 100644 --- a/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml @@ -23,7 +23,6 @@ junit junit - 4.12 test diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml index 8b0852ff07..da72f667a4 100644 --- a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml +++ b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml @@ -23,7 +23,6 @@ junit junit - 4.12 test diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml index e8b8d7bf07..79696df47d 100644 --- a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml @@ -23,7 +23,6 @@ junit junit - 4.12 test From 6102d0cc8ee1a62e26462ad78d5cf4fd507a3139 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 18 Oct 2020 16:11:18 +0200 Subject: [PATCH 114/597] #2236 Different nested target mappings should generate different intermediate methods Make sure that MappingReferences are taken into consideration when comparing whether 2 mapping methods are equal. This makes sure that when using nested target mappings that have the same property mappings, but different mappings 2 distinct methods will be created --- .../ap/internal/model/BeanMappingMethod.java | 23 +++++++- .../model/beanmapping/MappingReferences.java | 33 +++++++++++ .../org/mapstruct/ap/test/bugs/_2236/Car.java | 49 ++++++++++++++++ .../mapstruct/ap/test/bugs/_2236/CarDto.java | 58 +++++++++++++++++++ .../ap/test/bugs/_2236/CarMapper.java | 27 +++++++++ .../ap/test/bugs/_2236/Issue2236Test.java | 57 ++++++++++++++++++ .../mapstruct/ap/test/bugs/_2236/Owner.java | 31 ++++++++++ .../mapstruct/ap/test/bugs/_2236/Vehicle.java | 30 ++++++++++ .../nestedbeans/mixed/FishTankMapperImpl.java | 41 ++++++++++++- 9 files changed, 344 insertions(+), 5 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Car.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/CarDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/CarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Issue2236Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Owner.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Vehicle.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 87b8a28b4d..4d086b5126 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -85,6 +85,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { private final BuilderType returnTypeBuilder; private final MethodReference finalizerMethod; + private final MappingReferences mappingReferences; + public static class Builder { private MappingBuilderContext ctx; @@ -337,7 +339,8 @@ else if ( !method.isUpdateMethod() ) { returnTypeBuilder, beforeMappingMethods, afterMappingMethods, - finalizeMethod + finalizeMethod, + mappingReferences ); } @@ -1488,6 +1491,7 @@ private ConstructorAccessor( } } + //CHECKSTYLE:OFF private BeanMappingMethod(Method method, Collection existingVariableNames, List propertyMappings, @@ -1497,7 +1501,8 @@ private BeanMappingMethod(Method method, BuilderType returnTypeBuilder, List beforeMappingReferences, List afterMappingReferences, - MethodReference finalizerMethod) { + MethodReference finalizerMethod, + MappingReferences mappingReferences) { super( method, existingVariableNames, @@ -1506,10 +1511,12 @@ private BeanMappingMethod(Method method, beforeMappingReferences, afterMappingReferences ); + //CHECKSTYLE:ON this.propertyMappings = propertyMappings; this.returnTypeBuilder = returnTypeBuilder; this.finalizerMethod = finalizerMethod; + this.mappingReferences = mappingReferences; // intialize constant mappings as all mappings, but take out the ones that can be contributed to a // parameter mapping. @@ -1628,7 +1635,17 @@ public boolean equals(Object obj) { if ( !super.equals( obj ) ) { return false; } - return Objects.equals( propertyMappings, that.propertyMappings ); + + if ( !Objects.equals( propertyMappings, that.propertyMappings ) ) { + return false; + } + + if ( !Objects.equals( mappingReferences, that.mappingReferences ) ) { + return false; + } + + + return true; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java index 06f0ed06c9..569c75e0eb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java @@ -9,6 +9,7 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import org.mapstruct.ap.internal.model.common.Type; @@ -139,6 +140,38 @@ public List getTargetThisReferences() { return targetThisReferences; } + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( !( o instanceof MappingReferences ) ) { + return false; + } + + MappingReferences that = (MappingReferences) o; + + if ( restrictToDefinedMappings != that.restrictToDefinedMappings ) { + return false; + } + if ( forForgedMethods != that.forForgedMethods ) { + return false; + } + if ( !Objects.equals( mappingReferences, that.mappingReferences ) ) { + return false; + } + if ( Objects.equals( targetThisReferences, that.targetThisReferences ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return mappingReferences != null ? mappingReferences.hashCode() : 0; + } + /** * MapStruct filters automatically inversed invalid methods out. TODO: this is a principle we should discuss! * @param mappingRef diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Car.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Car.java new file mode 100644 index 0000000000..70bb9a413c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Car.java @@ -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._2236; + +/** + * @author Filip Hrisafov + */ +public class Car { + + private String name; + private String type; + private Owner ownerA; + private Owner ownerB; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Owner getOwnerA() { + return ownerA; + } + + public void setOwnerA(Owner ownerA) { + this.ownerA = ownerA; + } + + public Owner getOwnerB() { + return ownerB; + } + + public void setOwnerB(Owner ownerB) { + this.ownerB = ownerB; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/CarDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/CarDto.java new file mode 100644 index 0000000000..909bb3649b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/CarDto.java @@ -0,0 +1,58 @@ +/* + * 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._2236; + +/** + * @author Filip Hrisafov + */ +public class CarDto { + + private String name; + private String ownerNameA; + private String ownerNameB; + private String ownerCityA; + private String ownerCityB; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getOwnerNameA() { + return ownerNameA; + } + + public void setOwnerNameA(String ownerNameA) { + this.ownerNameA = ownerNameA; + } + + public String getOwnerNameB() { + return ownerNameB; + } + + public void setOwnerNameB(String ownerNameB) { + this.ownerNameB = ownerNameB; + } + + public String getOwnerCityA() { + return ownerCityA; + } + + public void setOwnerCityA(String ownerCityA) { + this.ownerCityA = ownerCityA; + } + + public String getOwnerCityB() { + return ownerCityB; + } + + public void setOwnerCityB(String ownerCityB) { + this.ownerCityB = ownerCityB; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/CarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/CarMapper.java new file mode 100644 index 0000000000..d3366ea2a6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/CarMapper.java @@ -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._2236; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CarMapper { + + CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); + + @Mapping(target = "ownerA.name", source = "carDto.ownerNameA") + @Mapping(target = "ownerA.city", source = "carDto.ownerCityA") + @Mapping(target = "ownerB.name", source = "carDto.ownerNameB") + @Mapping(target = "ownerB.city", source = "carDto.ownerCityB") + @Mapping(target = "name", source = "carDto.name") + @Mapping(target = "type", source = "type") + Car vehicleToCar(Vehicle vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Issue2236Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Issue2236Test.java new file mode 100644 index 0000000000..17f42c8dc3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Issue2236Test.java @@ -0,0 +1,57 @@ +/* + * 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._2236; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2236") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Car.class, + CarDto.class, + CarMapper.class, + Owner.class, + Vehicle.class, +}) +public class Issue2236Test { + + @Test + public void shouldCorrectlyMapSameTypesWithDifferentNestedMappings() { + + Vehicle vehicle = new Vehicle(); + vehicle.setType( "Sedan" ); + CarDto carDto = new CarDto(); + vehicle.setCarDto( carDto ); + + carDto.setName( "Private car" ); + carDto.setOwnerNameA( "Owner A" ); + carDto.setOwnerCityA( "Zurich" ); + + carDto.setOwnerNameB( "Owner B" ); + carDto.setOwnerCityB( "Bern" ); + + Car car = CarMapper.INSTANCE.vehicleToCar( vehicle ); + + assertThat( car ).isNotNull(); + assertThat( car.getType() ).isEqualTo( "Sedan" ); + assertThat( car.getName() ).isEqualTo( "Private car" ); + assertThat( car.getOwnerA() ).isNotNull(); + assertThat( car.getOwnerA().getName() ).isEqualTo( "Owner A" ); + assertThat( car.getOwnerA().getCity() ).isEqualTo( "Zurich" ); + assertThat( car.getOwnerB() ).isNotNull(); + assertThat( car.getOwnerB().getName() ).isEqualTo( "Owner B" ); + assertThat( car.getOwnerB().getCity() ).isEqualTo( "Bern" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Owner.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Owner.java new file mode 100644 index 0000000000..5f200f527a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Owner.java @@ -0,0 +1,31 @@ +/* + * 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._2236; + +/** + * @author Filip Hrisafov + */ +public class Owner { + + private String name; + private String city; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Vehicle.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Vehicle.java new file mode 100644 index 0000000000..fc8e744e82 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Vehicle.java @@ -0,0 +1,30 @@ +/* + * 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._2236; + +/** + * @author Filip Hrisafov + */ +public class Vehicle { + private CarDto carDto; + private String type; + + public CarDto getCarDto() { + return carDto; + } + + public void setCarDto(CarDto carDto) { + this.carDto = carDto; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java index 2b81718dba..3642d15340 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java @@ -57,9 +57,9 @@ public FishTankDto mapAsWell(FishTank source) { FishTankDto fishTankDto = new FishTankDto(); - fishTankDto.setFish( fishToFishDto( source.getFish() ) ); + fishTankDto.setFish( fishToFishDto1( source.getFish() ) ); fishTankDto.setMaterial( fishTankToMaterialDto1( source ) ); - fishTankDto.setQuality( waterQualityToWaterQualityDto( source.getQuality() ) ); + fishTankDto.setQuality( waterQualityToWaterQualityDto1( source.getQuality() ) ); fishTankDto.setOrnament( ornamentToOrnamentDto( sourceInteriorOrnament( source ) ) ); fishTankDto.setPlant( waterPlantToWaterPlantDto( source.getPlant() ) ); fishTankDto.setName( source.getName() ); @@ -197,6 +197,18 @@ protected WaterPlantDto waterPlantToWaterPlantDto(WaterPlant waterPlant) { return waterPlantDto; } + protected FishDto fishToFishDto1(Fish fish) { + if ( fish == null ) { + return null; + } + + FishDto fishDto = new FishDto(); + + fishDto.setKind( fish.getType() ); + + return fishDto; + } + protected MaterialDto fishTankToMaterialDto1(FishTank fishTank) { if ( fishTank == null ) { return null; @@ -221,6 +233,31 @@ protected WaterQualityOrganisationDto waterQualityReportToWaterQualityOrganisati return waterQualityOrganisationDto; } + protected WaterQualityReportDto waterQualityReportToWaterQualityReportDto1(WaterQualityReport waterQualityReport) { + if ( waterQualityReport == null ) { + return null; + } + + WaterQualityReportDto waterQualityReportDto = new WaterQualityReportDto(); + + waterQualityReportDto.setOrganisation( waterQualityReportToWaterQualityOrganisationDto1( waterQualityReport ) ); + waterQualityReportDto.setVerdict( waterQualityReport.getVerdict() ); + + return waterQualityReportDto; + } + + protected WaterQualityDto waterQualityToWaterQualityDto1(WaterQuality waterQuality) { + if ( waterQuality == null ) { + return null; + } + + WaterQualityDto waterQualityDto = new WaterQualityDto(); + + waterQualityDto.setReport( waterQualityReportToWaterQualityReportDto1( waterQuality.getReport() ) ); + + return waterQualityDto; + } + protected Fish fishDtoToFish(FishDto fishDto) { if ( fishDto == null ) { return null; From 74d06fea5db684cc7e6967680d40761ede7b5ff4 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 19 Oct 2020 23:34:59 +0200 Subject: [PATCH 115/597] #2233 Allow generic of generics in types when matching e.g. method signature such as T fromOptional(Optional optional) when T resolves to another generic class such as Collection --- .../internal/model/source/MethodMatcher.java | 11 +++- .../ap/test/bugs/_2233/Issue2233Test.java | 50 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2233/Program.java | 30 +++++++++++ .../ap/test/bugs/_2233/ProgramAggregate.java | 25 ++++++++++ .../ap/test/bugs/_2233/ProgramDto.java | 30 +++++++++++ .../ap/test/bugs/_2233/ProgramMapper.java | 31 ++++++++++++ .../test/bugs/_2233/ProgramResponseDto.java | 25 ++++++++++ 7 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Issue2233Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Program.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramAggregate.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramResponseDto.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index cade509b15..2c509924c0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -296,8 +296,17 @@ private DeclaredType toRawType(DeclaredType t) { public Boolean visitTypeVariable(TypeVariable t, TypeMirror p) { if ( genericTypesMap.containsKey( t ) ) { // when already found, the same mapping should apply + // Then we should visit the resolved generic type. + // Which can potentially be another generic type + // e.g. + // T fromOptional(Optional optional) + // T resolves to Collection + // We know what T resolves to, so we should treat it as if the method signature was + // Collection fromOptional(Optional optional) TypeMirror p1 = genericTypesMap.get( t ); - return typeUtils.isSubtype( p, p1 ); + // p (Integer) should be a subType of p1 (Number) + // i.e. you can assign p (Integer) to p1 (Number) + return visit( p, p1 ); } else { // check if types are in bound diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Issue2233Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Issue2233Test.java new file mode 100644 index 0000000000..645de0d24b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Issue2233Test.java @@ -0,0 +1,50 @@ +/* + * 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._2233; + +import java.util.Collections; +import java.util.Optional; + +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 static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2233") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses( { + Program.class, + ProgramAggregate.class, + ProgramDto.class, + ProgramMapper.class, + ProgramResponseDto.class, +} ) +public class Issue2233Test { + + @Test + public void shouldCorrectlyMapFromOptionalToCollection() { + ProgramResponseDto response = ProgramMapper.INSTANCE.map( new ProgramAggregate( Collections.singleton( + new Program( + "Optional Mapping", + "123" + ) ) ) ); + + assertThat( response ).isNotNull(); + assertThat( response.getPrograms() ).isPresent(); + assertThat( response.getPrograms().get() ) + .extracting( ProgramDto::getName, ProgramDto::getNumber ) + .containsExactly( + tuple( Optional.of( "Optional Mapping" ), Optional.of( "123" ) ) + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Program.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Program.java new file mode 100644 index 0000000000..748ad4901c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Program.java @@ -0,0 +1,30 @@ +/* + * 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._2233; + +import java.util.Optional; + +/** + * @author Filip Hrisafov + */ +public class Program { + + private final String name; + private final String number; + + public Program(String name, String number) { + this.name = name; + this.number = number; + } + + public Optional getName() { + return Optional.ofNullable( name ); + } + + public Optional getNumber() { + return Optional.ofNullable( number ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramAggregate.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramAggregate.java new file mode 100644 index 0000000000..c3fdabe151 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramAggregate.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.ap.test.bugs._2233; + +import java.util.Collection; +import java.util.Optional; + +/** + * @author Filip Hrisafov + */ +public class ProgramAggregate { + + private final Collection programs; + + public ProgramAggregate(Collection programs) { + this.programs = programs; + } + + public Optional> getPrograms() { + return Optional.ofNullable( programs ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramDto.java new file mode 100644 index 0000000000..d567998667 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramDto.java @@ -0,0 +1,30 @@ +/* + * 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._2233; + +import java.util.Optional; + +/** + * @author Filip Hrisafov + */ +public class ProgramDto { + + private final String name; + private final String number; + + public ProgramDto(String name, String number) { + this.name = name; + this.number = number; + } + + public Optional getName() { + return Optional.ofNullable( name ); + } + + public Optional getNumber() { + return Optional.ofNullable( number ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramMapper.java new file mode 100644 index 0000000000..e2737f8e3b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramMapper.java @@ -0,0 +1,31 @@ +/* + * 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._2233; + +import java.util.Collection; +import java.util.Optional; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ProgramMapper { + + ProgramMapper INSTANCE = Mappers.getMapper( ProgramMapper.class ); + + ProgramResponseDto map(ProgramAggregate programAggregate); + + ProgramDto map(Program sourceProgramDto); + + Collection mapPrograms(Collection sourcePrograms); + + default T fromOptional(Optional optional) { + return optional.orElse( null ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramResponseDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramResponseDto.java new file mode 100644 index 0000000000..61e7df4d00 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/ProgramResponseDto.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.ap.test.bugs._2233; + +import java.util.Collection; +import java.util.Optional; + +/** + * @author Filip Hrisafov + */ +public class ProgramResponseDto { + + private final Collection programs; + + public ProgramResponseDto(Collection programs) { + this.programs = programs; + } + + public Optional> getPrograms() { + return Optional.ofNullable( programs ); + } +} From c2e803403027f3fae92bd15b0ba50ab7df5063e6 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Wed, 21 Oct 2020 20:02:28 +0200 Subject: [PATCH 116/597] 861 remove compiler specific workarounds (#2227) --- .../ap/internal/model/GeneratedType.java | 6 +- .../internal/model/MappingBuilderContext.java | 16 +- .../ap/internal/model/ValueMappingMethod.java | 4 +- .../ap/internal/model/common/BuilderType.java | 4 +- .../ap/internal/model/common/Type.java | 25 +- .../ap/internal/model/common/TypeFactory.java | 20 +- .../model/source/BeanMappingOptions.java | 8 +- .../internal/model/source/DefaultOptions.java | 4 +- .../model/source/DelegatingOptions.java | 4 +- .../model/source/IterableMappingOptions.java | 8 +- .../model/source/MapMappingOptions.java | 11 +- .../model/source/MapperConfigOptions.java | 4 +- .../internal/model/source/MapperOptions.java | 4 +- .../internal/model/source/MappingControl.java | 8 +- .../internal/model/source/MappingOptions.java | 11 +- .../internal/model/source/MethodMatcher.java | 16 +- .../model/source/SelectionParameters.java | 8 +- .../internal/model/source/SourceMethod.java | 8 +- .../source/selector/MethodSelectors.java | 6 +- .../source/selector/QualifierSelector.java | 8 +- .../source/selector/TargetTypeSelector.java | 6 +- .../selector/XmlElementDeclSelector.java | 6 +- .../DefaultModelElementProcessorContext.java | 19 +- .../processor/MapperCreationProcessor.java | 8 +- .../processor/MethodRetrievalProcessor.java | 30 +- .../processor/ModelElementProcessor.java | 10 +- .../creation/MappingResolverImpl.java | 8 +- .../util/AbstractElementUtilsDecorator.java | 286 ++++++++++++++++++ ...r.java => AbstractTypeUtilsDecorator.java} | 36 +-- .../ap/internal/util/ClassUtils.java | 4 +- .../util/EclipseElementUtilsDecorator.java | 40 +++ .../util/EclipseTypeUtilsDecorator.java | 15 + .../ap/internal/util/ElementUtils.java | 46 +++ .../ap/internal/util/Executables.java | 157 ---------- .../mapstruct/ap/internal/util/Fields.java | 53 ---- .../mapstruct/ap/internal/util/Filters.java | 5 +- .../util/JavacElementUtilsDecorator.java | 21 ++ .../util/JavacTypeUtilsDecorator.java | 15 + .../mapstruct/ap/internal/util/TypeUtils.java | 26 ++ .../EclipseAsMemberOfWorkaround.java | 182 ----------- .../workarounds/EclipseClassLoaderBridge.java | 75 ----- .../SpecificCompilerWorkarounds.java | 140 --------- .../model/source/SelectionParametersTest.java | 9 +- 43 files changed, 610 insertions(+), 770 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java rename processor/src/main/java/org/mapstruct/ap/internal/util/{workarounds/TypesDecorator.java => AbstractTypeUtilsDecorator.java} (75%) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/EclipseTypeUtilsDecorator.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/JavacTypeUtilsDecorator.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/TypeUtils.java delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseAsMemberOfWorkaround.java delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseClassLoaderBridge.java delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/SpecificCompilerWorkarounds.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java index a03f9af9a0..a6f573d4ff 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java @@ -12,7 +12,7 @@ import java.util.TreeSet; import javax.lang.model.type.TypeKind; -import javax.lang.model.util.Elements; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.ModelElement; @@ -35,7 +35,7 @@ protected abstract static class GeneratedTypeBuilder extraImportedTypes; @@ -46,7 +46,7 @@ protected abstract static class GeneratedTypeBuilder qualifiers = beanMapping.qualifiedBy().get(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java index 1b34f4698d..b94bb82efd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java @@ -9,7 +9,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.spi.BuilderInfo; @@ -84,7 +84,7 @@ public Collection getBuildMethods() { } public static BuilderType create(BuilderInfo builderInfo, Type typeToBuild, TypeFactory typeFactory, - Types typeUtils) { + TypeUtils typeUtils) { if ( builderInfo == null ) { return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index b2b12b8a25..910af60d40 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -28,22 +28,21 @@ import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; -import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor8; -import javax.lang.model.util.Types; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.util.AccessorNamingUtils; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.Executables; -import org.mapstruct.ap.internal.util.Fields; import org.mapstruct.ap.internal.util.Filters; import org.mapstruct.ap.internal.util.JavaStreamConstants; +import org.mapstruct.ap.internal.util.NativeTypes; import org.mapstruct.ap.internal.util.Nouns; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; import static org.mapstruct.ap.internal.util.Collections.first; -import org.mapstruct.ap.internal.util.NativeTypes; /** * Represents (a reference to) the type of a bean property, parameter etc. Types are managed per generated source file. @@ -57,8 +56,8 @@ */ public class Type extends ModelElement implements Comparable { - private final Types typeUtils; - private final Elements elementUtils; + private final TypeUtils typeUtils; + private final ElementUtils elementUtils; private final TypeFactory typeFactory; private final AccessorNamingUtils accessorNaming; @@ -109,7 +108,7 @@ public class Type extends ModelElement implements Comparable { private final Filters filters; //CHECKSTYLE:OFF - public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory, + public Type(TypeUtils typeUtils, ElementUtils elementUtils, TypeFactory typeFactory, AccessorNamingUtils accessorNaming, TypeMirror typeMirror, TypeElement typeElement, List typeParameters, ImplementationType implementationType, Type componentType, @@ -662,7 +661,7 @@ else if ( candidate.getAccessorType() == AccessorType.GETTER private List getAllMethods() { if ( allMethods == null ) { - allMethods = Executables.getAllEnclosedExecutableElements( elementUtils, typeElement ); + allMethods = elementUtils.getAllEnclosedExecutableElements( typeElement ); } return allMethods; @@ -670,7 +669,7 @@ private List getAllMethods() { private List getAllFields() { if ( allFields == null ) { - allFields = Fields.getAllEnclosedFields( elementUtils, typeElement ); + allFields = elementUtils.getAllEnclosedFields( typeElement ); } return allFields; @@ -861,7 +860,7 @@ private boolean isCollection(TypeMirror candidate) { private boolean isStream(TypeMirror candidate) { TypeElement streamTypeElement = elementUtils.getTypeElement( JavaStreamConstants.STREAM_FQN ); TypeMirror streamType = streamTypeElement == null ? null : typeUtils.erasure( streamTypeElement.asType() ); - return streamType != null && typeUtils.isSubtype( candidate, streamType ); + return streamType != null && typeUtils.isSubtypeErased( candidate, streamType ); } private boolean isMap(TypeMirror candidate) { @@ -871,7 +870,7 @@ private boolean isMap(TypeMirror candidate) { private boolean isSubType(TypeMirror candidate, Class clazz) { String className = clazz.getCanonicalName(); TypeMirror classType = typeUtils.erasure( elementUtils.getTypeElement( className ).asType() ); - return typeUtils.isSubtype( candidate, classType ); + return typeUtils.isSubtypeErased( candidate, classType ); } /** @@ -1141,9 +1140,9 @@ public Type resolveTypeVarToType(Type declaredType, Type parameterizedType) { private static class TypeVarMatcher extends SimpleTypeVisitor8 { private TypeVariable typeVarToMatch; - private Types types; + private TypeUtils types; - TypeVarMatcher( Types types, Type typeVarToMatch ) { + TypeVarMatcher(TypeUtils types, Type typeVarToMatch ) { super( null ); this.typeVarToMatch = (TypeVariable) typeVarToMatch.getTypeMirror(); this.types = types; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index ba1dcc1920..8d3d215c52 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -36,8 +36,8 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.util.AnnotationProcessingException; @@ -81,8 +81,8 @@ public class TypeFactory { return sb.toString(); }; - private final Elements elementUtils; - private final Types typeUtils; + private final ElementUtils elementUtils; + private final TypeUtils typeUtils; private final FormattingMessager messager; private final RoundContext roundContext; @@ -97,8 +97,8 @@ public class TypeFactory { private final boolean loggingVerbose; - public TypeFactory(Elements elementUtils, Types typeUtils, FormattingMessager messager, RoundContext roundContext, - Map notToBeImportedTypes, boolean loggingVerbose) { + public TypeFactory(ElementUtils elementUtils, TypeUtils typeUtils, FormattingMessager messager, + RoundContext roundContext, Map notToBeImportedTypes, boolean loggingVerbose) { this.elementUtils = elementUtils; this.typeUtils = typeUtils; this.messager = messager; @@ -198,10 +198,10 @@ private Type getType(TypeMirror mirror, boolean isLiteral) { ImplementationType implementationType = getImplementationType( mirror ); - boolean isIterableType = typeUtils.isSubtype( mirror, iterableType ); - boolean isCollectionType = typeUtils.isSubtype( mirror, collectionType ); - boolean isMapType = typeUtils.isSubtype( mirror, mapType ); - boolean isStreamType = streamType != null && typeUtils.isSubtype( mirror, streamType ); + boolean isIterableType = typeUtils.isSubtypeErased( mirror, iterableType ); + boolean isCollectionType = typeUtils.isSubtypeErased( mirror, collectionType ); + boolean isMapType = typeUtils.isSubtypeErased( mirror, mapType ); + boolean isStreamType = streamType != null && typeUtils.isSubtypeErased( mirror, streamType ); boolean isEnumType; boolean isInterface; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index d8fd67510e..d600ed0fc0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -11,8 +11,8 @@ import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.gem.BeanMappingGem; @@ -50,7 +50,7 @@ public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) public static BeanMappingOptions getInstanceOn(BeanMappingGem beanMapping, MapperOptions mapperOptions, ExecutableElement method, FormattingMessager messager, - Types typeUtils, TypeFactory typeFactory + TypeUtils typeUtils, TypeFactory typeFactory ) { if ( beanMapping == null || !isConsistent( beanMapping, method, messager ) ) { BeanMappingOptions options = new BeanMappingOptions( null, null, mapperOptions ); @@ -143,7 +143,7 @@ public BuilderGem getBuilder() { } @Override - public MappingControl getMappingControl(Elements elementUtils) { + public MappingControl getMappingControl(ElementUtils elementUtils) { return Optional.ofNullable( beanMapping ).map( BeanMappingGem::mappingControl ) .filter( GemValue::hasValue ) .map( GemValue::getValue ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index 81f870e25b..4d80bf57be 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -9,7 +9,7 @@ import java.util.Set; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.gem.BuilderGem; @@ -124,7 +124,7 @@ public BuilderGem getBuilder() { } @Override - public MappingControl getMappingControl(Elements elementUtils) { + public MappingControl getMappingControl(ElementUtils elementUtils) { return MappingControl.fromTypeMirror( mapper.mappingControl().getDefaultValue(), elementUtils ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java index c458681988..564358ec98 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -10,7 +10,7 @@ import java.util.Set; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; @@ -101,7 +101,7 @@ public BuilderGem getBuilder() { return next.getBuilder(); } - public MappingControl getMappingControl(Elements elementUtils) { + public MappingControl getMappingControl(ElementUtils elementUtils) { return next.getMappingControl( elementUtils ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java index 73ae1907cf..40b5b25353 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java @@ -8,8 +8,8 @@ import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.gem.IterableMappingGem; @@ -31,7 +31,7 @@ public class IterableMappingOptions extends DelegatingOptions { public static IterableMappingOptions fromGem(IterableMappingGem iterableMapping, MapperOptions mappperOptions, ExecutableElement method, - FormattingMessager messager, Types typeUtils) { + FormattingMessager messager, TypeUtils typeUtils) { if ( iterableMapping == null || !isConsistent( iterableMapping, method, messager ) ) { IterableMappingOptions options = new IterableMappingOptions( null, null, null, mappperOptions ); @@ -102,7 +102,7 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { .orElse( next().getNullValueMappingStrategy() ); } - public MappingControl getElementMappingControl(Elements elementUtils) { + public MappingControl getElementMappingControl(ElementUtils elementUtils) { return Optional.ofNullable( iterableMapping ).map( IterableMappingGem::elementMappingControl ) .filter( GemValue::hasValue ) .map( GemValue::getValue ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java index 96ff577f48..c35a8e363d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java @@ -8,8 +8,8 @@ import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.gem.MapMappingGem; @@ -32,7 +32,8 @@ public class MapMappingOptions extends DelegatingOptions { private final MapMappingGem mapMapping; public static MapMappingOptions fromGem(MapMappingGem mapMapping, MapperOptions mapperOptions, - ExecutableElement method, FormattingMessager messager, Types typeUtils) { + ExecutableElement method, FormattingMessager messager, + TypeUtils typeUtils) { if ( mapMapping == null || !isConsistent( mapMapping, method, messager ) ) { MapMappingOptions options = new MapMappingOptions( @@ -146,7 +147,7 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { .orElse( next().getNullValueMappingStrategy() ); } - public MappingControl getKeyMappingControl(Elements elementUtils) { + public MappingControl getKeyMappingControl(ElementUtils elementUtils) { return Optional.ofNullable( mapMapping ).map( MapMappingGem::keyMappingControl ) .filter( GemValue::hasValue ) .map( GemValue::getValue ) @@ -154,7 +155,7 @@ public MappingControl getKeyMappingControl(Elements elementUtils) { .orElse( next().getMappingControl( elementUtils ) ); } - public MappingControl getValueMappingControl(Elements elementUtils) { + public MappingControl getValueMappingControl(ElementUtils elementUtils) { return Optional.ofNullable( mapMapping ).map( MapMappingGem::valueMappingControl ) .filter( GemValue::hasValue ) .map( GemValue::getValue ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java index 07f668e655..7f8df7bf42 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -8,7 +8,7 @@ import java.util.Set; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; @@ -132,7 +132,7 @@ public BuilderGem getBuilder() { } @Override - public MappingControl getMappingControl(Elements elementUtils) { + public MappingControl getMappingControl(ElementUtils elementUtils) { return mapperConfig.mappingControl().hasValue() ? MappingControl.fromTypeMirror( mapperConfig.mappingControl().getValue(), elementUtils ) : next().getMappingControl( elementUtils ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index b054c628a9..b916cd2de5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -12,7 +12,7 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.gem.BuilderGem; @@ -162,7 +162,7 @@ public BuilderGem getBuilder() { } @Override - public MappingControl getMappingControl(Elements elementUtils) { + public MappingControl getMappingControl(ElementUtils elementUtils) { return mapper.mappingControl().hasValue() ? MappingControl.fromTypeMirror( mapper.mappingControl().getValue(), elementUtils ) : next().getMappingControl( elementUtils ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingControl.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingControl.java index fc896bb52e..ab2957086f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingControl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingControl.java @@ -14,7 +14,7 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.gem.MappingControlGem; import org.mapstruct.ap.internal.gem.MappingControlUseGem; @@ -32,7 +32,7 @@ public class MappingControl { private boolean allowMappingMethod = false; private boolean allow2Steps = false; - public static MappingControl fromTypeMirror(TypeMirror mirror, Elements elementUtils) { + public static MappingControl fromTypeMirror(TypeMirror mirror, ElementUtils elementUtils) { MappingControl mappingControl = new MappingControl(); if ( TypeKind.DECLARED == mirror.getKind() ) { resolveControls( mappingControl, ( (DeclaredType) mirror ).asElement(), new HashSet<>(), elementUtils ); @@ -60,7 +60,7 @@ public boolean allowBy2Steps() { } private static void resolveControls(MappingControl control, Element element, Set handledElements, - Elements elementUtils) { + ElementUtils elementUtils) { for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { Element lElement = annotationMirror.getAnnotationType().asElement(); if ( isAnnotation( lElement, MAPPING_CONTROL_FQN ) ) { @@ -102,7 +102,7 @@ private static void determineMappingControl(MappingControl in, MappingControlGem } } - private static boolean isAnnotationInPackage(Element element, String packageFQN, Elements elementUtils) { + private static boolean isAnnotationInPackage(Element element, String packageFQN, ElementUtils elementUtils) { if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { return packageFQN.equals( elementUtils.getPackageOf( element ).getQualifiedName().toString() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index b826295f75..9db1b1c04c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -18,8 +18,8 @@ import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.gem.MappingGem; import org.mapstruct.ap.internal.gem.MappingsGem; @@ -93,7 +93,7 @@ public static Set getMappingTargetNamesBy(Predicate pred public static void addInstances(MappingsGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, - FormattingMessager messager, Types typeUtils, + FormattingMessager messager, TypeUtils typeUtils, Set mappings) { for ( MappingGem mapping : gem.value().getValue() ) { @@ -102,7 +102,8 @@ public static void addInstances(MappingsGem gem, ExecutableElement method, } public static void addInstance(MappingGem mapping, ExecutableElement method, - BeanMappingOptions beanMappingOptions, FormattingMessager messager, Types typeUtils, + BeanMappingOptions beanMappingOptions, FormattingMessager messager, + TypeUtils typeUtils, Set mappings) { if ( !isConsistent( mapping, method, messager ) ) { @@ -421,7 +422,7 @@ public NullValuePropertyMappingStrategyGem getNullValuePropertyMappingStrategy() } @Override - public MappingControl getMappingControl(Elements elementUtils) { + public MappingControl getMappingControl(ElementUtils elementUtils) { return Optional.ofNullable( mapping ).map( MappingGem::mappingControl ) .filter( GemValue::hasValue ) .map( GemValue::getValue ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index 2c509924c0..ea3f720440 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -19,7 +19,7 @@ import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.SimpleTypeVisitor6; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; @@ -53,10 +53,10 @@ public class MethodMatcher { private final SourceMethod candidateMethod; - private final Types typeUtils; + private final TypeUtils typeUtils; private final TypeFactory typeFactory; - MethodMatcher(Types typeUtils, TypeFactory typeFactory, SourceMethod candidateMethod) { + MethodMatcher(TypeUtils typeUtils, TypeFactory typeFactory, SourceMethod candidateMethod) { this.typeUtils = typeUtils; this.candidateMethod = candidateMethod; this.typeFactory = typeFactory; @@ -312,8 +312,8 @@ public Boolean visitTypeVariable(TypeVariable t, TypeMirror p) { // check if types are in bound TypeMirror lowerBound = t.getLowerBound(); TypeMirror upperBound = t.getUpperBound(); - if ( ( isNullType( lowerBound ) || typeUtils.isSubtype( lowerBound, p ) ) - && ( isNullType( upperBound ) || typeUtils.isSubtype( p, upperBound ) ) ) { + if ( ( isNullType( lowerBound ) || typeUtils.isSubtypeErased( lowerBound, p ) ) + && ( isNullType( upperBound ) || typeUtils.isSubtypeErased( p, upperBound ) ) ) { genericTypesMap.put( t, p ); return Boolean.TRUE; } @@ -359,7 +359,7 @@ public Boolean visitWildcard(WildcardType t, TypeMirror p) { // for example method: String method(? super String) // to check super type, we can simply inverse the argument, but that would initially yield // a result: bounds = tpe != null ? tpe.getBounds() : null; if ( t != null && bounds != null ) { for ( TypeMirror bound : bounds ) { - if ( !( bound.getKind() == TypeKind.DECLARED && typeUtils.isSubtype( t, bound ) ) ) { + if ( !( bound.getKind() == TypeKind.DECLARED && typeUtils.isSubtypeErased( t, bound ) ) ) { return false; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java index ed6851d9a1..a78409582d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Objects; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.SourceRHS; @@ -24,7 +24,7 @@ public class SelectionParameters { private final List qualifiers; private final List qualifyingNames; private final TypeMirror resultType; - private final Types typeUtils; + private final TypeUtils typeUtils; private final SourceRHS sourceRHS; /** @@ -45,12 +45,12 @@ public static SelectionParameters forInheritance(SelectionParameters selectionPa } public SelectionParameters(List qualifiers, List qualifyingNames, TypeMirror resultType, - Types typeUtils) { + TypeUtils typeUtils) { this( qualifiers, qualifyingNames, resultType, typeUtils, null ); } private SelectionParameters(List qualifiers, List qualifyingNames, TypeMirror resultType, - Types typeUtils, SourceRHS sourceRHS) { + TypeUtils typeUtils, SourceRHS sourceRHS) { this.qualifiers = qualifiers; this.qualifyingNames = qualifyingNames; this.resultType = resultType; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 98cffb4c35..bac1346d97 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -13,7 +13,7 @@ import java.util.stream.Collectors; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.Parameter; @@ -37,7 +37,7 @@ */ public class SourceMethod implements Method { - private final Types typeUtils; + private final TypeUtils typeUtils; private final TypeFactory typeFactory; private final Type declaringMapper; @@ -82,7 +82,7 @@ public static class Builder { private IterableMappingOptions iterableMapping = null; private MapMappingOptions mapMapping = null; private BeanMappingOptions beanMapping = null; - private Types typeUtils; + private TypeUtils typeUtils; private TypeFactory typeFactory = null; private MapperOptions mapper = null; private List prototypeMethods = Collections.emptyList(); @@ -146,7 +146,7 @@ public Builder setEnumMappingOptions(EnumMappingOptions enumMappingOptions) { return this; } - public Builder setTypeUtils(Types typeUtils) { + public Builder setTypeUtils(TypeUtils typeUtils) { this.typeUtils = typeUtils; return this; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java index 5bfbe84497..74fedcb75d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java @@ -8,8 +8,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; @@ -25,7 +25,7 @@ public class MethodSelectors { private final List selectors; - public MethodSelectors(Types typeUtils, Elements elementUtils, TypeFactory typeFactory, + public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils, TypeFactory typeFactory, FormattingMessager messager) { selectors = Arrays.asList( new MethodFamilySelector(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java index 27ad9eb60d..bca471f9c0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java @@ -12,8 +12,8 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; @@ -40,10 +40,10 @@ */ public class QualifierSelector implements MethodSelector { - private final Types typeUtils; + private final TypeUtils typeUtils; private final TypeMirror namedAnnotationTypeMirror; - public QualifierSelector( Types typeUtils, Elements elementUtils ) { + public QualifierSelector(TypeUtils typeUtils, ElementUtils elementUtils ) { this.typeUtils = typeUtils; namedAnnotationTypeMirror = elementUtils.getTypeElement( "org.mapstruct.Named" ).asType(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java index 1cbcb6927e..ad1a5d47b5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java @@ -9,7 +9,7 @@ import java.util.List; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; @@ -24,9 +24,9 @@ */ public class TargetTypeSelector implements MethodSelector { - private final Types typeUtils; + private final TypeUtils typeUtils; - public TargetTypeSelector( Types typeUtils ) { + public TargetTypeSelector( TypeUtils typeUtils ) { this.typeUtils = typeUtils; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java index c3c394aefd..ecf6657991 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java @@ -11,7 +11,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.gem.XmlElementRefGem; import org.mapstruct.ap.internal.model.common.Type; @@ -37,9 +37,9 @@ */ public class XmlElementDeclSelector implements MethodSelector { - private final Types typeUtils; + private final TypeUtils typeUtils; - public XmlElementDeclSelector(Types typeUtils) { + public XmlElementDeclSelector(TypeUtils typeUtils) { this.typeUtils = typeUtils; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java index 1514f4f5e1..d6a05cec32 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java @@ -13,18 +13,17 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.processor.ModelElementProcessor.ProcessorContext; import org.mapstruct.ap.internal.util.AccessorNamingUtils; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.RoundContext; -import org.mapstruct.ap.internal.util.workarounds.TypesDecorator; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.version.VersionInformation; import org.mapstruct.ap.spi.EnumMappingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; @@ -41,7 +40,8 @@ public class DefaultModelElementProcessorContext implements ProcessorContext { private final Options options; private final TypeFactory typeFactory; private final VersionInformation versionInformation; - private final Types delegatingTypes; + private final TypeUtils delegatingTypes; + private final ElementUtils delegatingElements; private final AccessorNamingUtils accessorNaming; private final RoundContext roundContext; @@ -52,10 +52,11 @@ public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvir this.messager = new DelegatingMessager( processingEnvironment.getMessager(), options.isVerbose() ); this.accessorNaming = roundContext.getAnnotationProcessorContext().getAccessorNaming(); this.versionInformation = DefaultVersionInformation.fromProcessingEnvironment( processingEnvironment ); - this.delegatingTypes = new TypesDecorator( processingEnvironment, versionInformation ); + this.delegatingTypes = TypeUtils.create( processingEnvironment, versionInformation ); + this.delegatingElements = ElementUtils.create( processingEnvironment, versionInformation ); this.roundContext = roundContext; this.typeFactory = new TypeFactory( - processingEnvironment.getElementUtils(), + delegatingElements, delegatingTypes, messager, roundContext, @@ -71,13 +72,13 @@ public Filer getFiler() { } @Override - public Types getTypeUtils() { + public TypeUtils getTypeUtils() { return delegatingTypes; } @Override - public Elements getElementUtils() { - return processingEnvironment.getElementUtils(); + public ElementUtils getElementUtils() { + return delegatingElements; } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 25632350be..a0014f5934 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -18,8 +18,8 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.BeanMappingMethod; import org.mapstruct.ap.internal.model.ContainerMappingMethod; @@ -73,8 +73,8 @@ */ public class MapperCreationProcessor implements ModelElementProcessor, Mapper> { - private Elements elementUtils; - private Types typeUtils; + private ElementUtils elementUtils; + private TypeUtils typeUtils; private FormattingMessager messager; private Options options; private VersionInformation versionInformation; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 059a000bc5..693741bcd2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -21,9 +21,15 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.gem.BeanMappingGem; +import org.mapstruct.ap.internal.gem.IterableMappingGem; +import org.mapstruct.ap.internal.gem.MapMappingGem; +import org.mapstruct.ap.internal.gem.MappingGem; +import org.mapstruct.ap.internal.gem.MappingsGem; +import org.mapstruct.ap.internal.gem.ObjectFactoryGem; +import org.mapstruct.ap.internal.gem.ValueMappingGem; +import org.mapstruct.ap.internal.gem.ValueMappingsGem; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; @@ -36,24 +42,16 @@ import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.ValueMappingOptions; -import org.mapstruct.ap.internal.gem.BeanMappingGem; -import org.mapstruct.ap.internal.gem.IterableMappingGem; -import org.mapstruct.ap.internal.gem.MapMappingGem; -import org.mapstruct.ap.internal.gem.MappingGem; -import org.mapstruct.ap.internal.gem.MappingsGem; -import org.mapstruct.ap.internal.gem.ObjectFactoryGem; -import org.mapstruct.ap.internal.gem.ValueMappingGem; -import org.mapstruct.ap.internal.gem.ValueMappingsGem; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.AnnotationProcessingException; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.spi.EnumTransformationStrategy; -import static org.mapstruct.ap.internal.util.Executables.getAllEnclosedExecutableElements; - /** * A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s * representing all the mapping methods of the given bean mapper type as well as @@ -73,8 +71,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor enumTransformationStrategies; - private Types typeUtils; - private Elements elementUtils; + private TypeUtils typeUtils; + private ElementUtils elementUtils; private Options options; @Override @@ -119,7 +117,7 @@ private List retrievePrototypeMethods(TypeElement mapperTypeElemen TypeElement typeElement = asTypeElement( mapperAnnotation.mapperConfigType() ); List methods = new ArrayList<>(); - for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, typeElement ) ) { + for ( ExecutableElement executable : elementUtils.getAllEnclosedExecutableElements( typeElement ) ) { ExecutableType methodType = typeFactory.getMethodType( mapperAnnotation.mapperConfigType(), executable ); List parameters = typeFactory.getParameters( methodType, executable ); @@ -160,7 +158,7 @@ private List retrieveMethods(TypeElement usedMapper, TypeElement m MapperOptions mapperOptions, List prototypeMethods) { List methods = new ArrayList<>(); - for ( ExecutableElement executable : getAllEnclosedExecutableElements( elementUtils, usedMapper ) ) { + for ( ExecutableElement executable : elementUtils.getAllEnclosedExecutableElements( usedMapper ) ) { SourceMethod method = getMethod( usedMapper, executable, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java index ac0a161a28..d43b52953d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java @@ -8,8 +8,8 @@ import java.util.Map; import javax.annotation.processing.Filer; import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import javax.tools.Diagnostic.Kind; import org.mapstruct.ap.internal.model.common.TypeFactory; @@ -35,7 +35,7 @@ public interface ModelElementProcessor { /** * Context object passed to * {@link ModelElementProcessor#process(ProcessorContext, TypeElement, Object)} - * providing access to common infrastructure objects such as {@link Types} + * providing access to common infrastructure objects such as {@link TypeUtils} * etc. * * @author Gunnar Morling @@ -44,9 +44,9 @@ public interface ProcessorContext { Filer getFiler(); - Types getTypeUtils(); + TypeUtils getTypeUtils(); - Elements getElementUtils(); + ElementUtils getElementUtils(); TypeFactory getTypeFactory(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 73392a00f8..debe8f30dc 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -32,8 +32,8 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.conversion.ConversionProvider; import org.mapstruct.ap.internal.conversion.Conversions; @@ -78,7 +78,7 @@ public class MappingResolverImpl implements MappingResolver { private static final int LIMIT_REPORTING_AMBIGUOUS = 5; private final FormattingMessager messager; - private final Types typeUtils; + private final TypeUtils typeUtils; private final TypeFactory typeFactory; private final List sourceModel; @@ -98,7 +98,7 @@ public class MappingResolverImpl implements MappingResolver { */ private final Set usedSupportedMappings = new HashSet<>(); - public MappingResolverImpl(FormattingMessager messager, Elements elementUtils, Types typeUtils, + public MappingResolverImpl(FormattingMessager messager, ElementUtils elementUtils, TypeUtils typeUtils, TypeFactory typeFactory, List sourceModel, List mapperReferences, boolean verboseLogging) { this.messager = messager; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java new file mode 100644 index 0000000000..8239dcb41b --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java @@ -0,0 +1,286 @@ +/* + * 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.internal.util; + +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; + +import org.mapstruct.ap.spi.TypeHierarchyErroneousException; + +import static javax.lang.model.util.ElementFilter.fieldsIn; +import static javax.lang.model.util.ElementFilter.methodsIn; + +public abstract class AbstractElementUtilsDecorator implements ElementUtils { + + private final Elements delegate; + + AbstractElementUtilsDecorator(ProcessingEnvironment processingEnv) { + this.delegate = processingEnv.getElementUtils(); + } + + @Override + public PackageElement getPackageElement(CharSequence name) { + return delegate.getPackageElement( name ); + } + + @Override + public TypeElement getTypeElement(CharSequence name) { + return delegate.getTypeElement( name ); + } + + @Override + public Map getElementValuesWithDefaults( + AnnotationMirror a) { + return delegate.getElementValuesWithDefaults( a ); + } + + @Override + public String getDocComment(Element e) { + return delegate.getDocComment( e ); + } + + @Override + public boolean isDeprecated(Element e) { + return delegate.isDeprecated( e ); + } + + @Override + public Name getBinaryName(TypeElement type) { + return delegate.getBinaryName( type ); + } + + @Override + public PackageElement getPackageOf(Element type) { + return delegate.getPackageOf( type ); + } + + @Override + public List getAllMembers(TypeElement type) { + return delegate.getAllMembers( type ); + } + + @Override + public List getAllAnnotationMirrors(Element e) { + return delegate.getAllAnnotationMirrors( e ); + } + + @Override + public boolean hides(Element hider, Element hidden) { + return delegate.hides( hider, hidden ); + } + + @Override + public boolean overrides(ExecutableElement overrider, ExecutableElement overridden, TypeElement type) { + return delegate.overrides( overrider, overridden, type ); + } + + @Override + public String getConstantExpression(Object value) { + return delegate.getConstantExpression( value ); + } + + @Override + public void printElements(Writer w, Element... elements) { + delegate.printElements( w, elements ); + } + + @Override + public Name getName(CharSequence cs) { + return delegate.getName( cs ); + } + + @Override + public boolean isFunctionalInterface(TypeElement type) { + return delegate.isFunctionalInterface( type ); + } + + @Override + public List getAllEnclosedExecutableElements(TypeElement element) { + List enclosedElements = new ArrayList<>(); + element = replaceTypeElementIfNecessary( element ); + addEnclosedMethodsInHierarchy( enclosedElements, element, element ); + + return enclosedElements; + } + + @Override + public List getAllEnclosedFields( TypeElement element) { + List enclosedElements = new ArrayList<>(); + element = replaceTypeElementIfNecessary( element ); + addEnclosedFieldsInHierarchy( enclosedElements, element, element ); + + return enclosedElements; + } + + private void addEnclosedMethodsInHierarchy(List alreadyAdded, TypeElement element, + TypeElement parentType) { + if ( element != parentType ) { // otherwise the element was already checked for replacement + element = replaceTypeElementIfNecessary( element ); + } + + if ( element.asType().getKind() == TypeKind.ERROR ) { + throw new TypeHierarchyErroneousException( element ); + } + + addMethodNotYetOverridden( alreadyAdded, methodsIn( element.getEnclosedElements() ), parentType ); + + if ( hasNonObjectSuperclass( element ) ) { + addEnclosedMethodsInHierarchy( + alreadyAdded, + asTypeElement( element.getSuperclass() ), + parentType + ); + } + + for ( TypeMirror interfaceType : element.getInterfaces() ) { + addEnclosedMethodsInHierarchy( + alreadyAdded, + asTypeElement( interfaceType ), + parentType + ); + } + + } + + /** + * @param alreadyCollected methods that have already been collected and to which the not-yet-overridden methods will + * be added + * @param methodsToAdd methods to add to alreadyAdded, if they are not yet overridden by an element in the list + * @param parentType the type for with elements are collected + */ + private void addMethodNotYetOverridden(List alreadyCollected, + List methodsToAdd, + TypeElement parentType) { + List safeToAdd = new ArrayList<>( methodsToAdd.size() ); + for ( ExecutableElement toAdd : methodsToAdd ) { + if ( isNotPrivate( toAdd ) && isNotObjectEquals( toAdd ) + && methodWasNotYetOverridden( alreadyCollected, toAdd, parentType ) ) { + safeToAdd.add( toAdd ); + } + } + + alreadyCollected.addAll( 0, safeToAdd ); + } + + /** + * @param executable the executable to check + * @return {@code true}, iff the executable does not represent {@link java.lang.Object#equals(Object)} or an + * overridden version of it + */ + private boolean isNotObjectEquals(ExecutableElement executable) { + if ( executable.getSimpleName().contentEquals( "equals" ) && executable.getParameters().size() == 1 + && asTypeElement( executable.getParameters().get( 0 ).asType() ).getQualifiedName().contentEquals( + "java.lang.Object" + ) ) { + return false; + } + return true; + } + + /** + * @param alreadyCollected the list of already collected methods of one type hierarchy (order is from sub-types to + * super-types) + * @param executable the method to check + * @param parentType the type for which elements are collected + * @return {@code true}, iff the given executable was not yet overridden by a method in the given list. + */ + private boolean methodWasNotYetOverridden(List alreadyCollected, + ExecutableElement executable, TypeElement parentType) { + for ( ListIterator it = alreadyCollected.listIterator(); it.hasNext(); ) { + ExecutableElement executableInSubtype = it.next(); + if ( executableInSubtype == null ) { + continue; + } + if ( delegate.overrides( executableInSubtype, executable, parentType ) ) { + return false; + } + else if ( delegate.overrides( executable, executableInSubtype, parentType ) ) { + // remove the method from another interface hierarchy that is overridden by the executable to add + it.remove(); + return true; + } + } + + return true; + } + + private void addEnclosedFieldsInHierarchy( List alreadyAdded, + TypeElement element, TypeElement parentType) { + if ( element != parentType ) { // otherwise the element was already checked for replacement + element = replaceTypeElementIfNecessary( element ); + } + + if ( element.asType().getKind() == TypeKind.ERROR ) { + throw new TypeHierarchyErroneousException( element ); + } + + addFields( alreadyAdded, fieldsIn( element.getEnclosedElements() ) ); + + if ( hasNonObjectSuperclass( element ) ) { + addEnclosedFieldsInHierarchy( + alreadyAdded, + asTypeElement( element.getSuperclass() ), + parentType + ); + } + } + + private static void addFields(List alreadyCollected, List variablesToAdd) { + List safeToAdd = new ArrayList<>( variablesToAdd.size() ); + safeToAdd.addAll( variablesToAdd ); + + alreadyCollected.addAll( 0, safeToAdd ); + } + + /** + * @param element the type element to check + * @return {@code true}, iff the type has a super-class that is not java.lang.Object + */ + private boolean hasNonObjectSuperclass(TypeElement element) { + if ( element.getSuperclass().getKind() == TypeKind.ERROR ) { + throw new TypeHierarchyErroneousException( element ); + } + + return element.getSuperclass().getKind() == TypeKind.DECLARED + && !asTypeElement( element.getSuperclass() ).getQualifiedName().toString().equals( "java.lang.Object" ); + } + + /** + * @param mirror the type positionHint + * @return the corresponding type element + */ + private TypeElement asTypeElement(TypeMirror mirror) { + return (TypeElement) ( (DeclaredType) mirror ).asElement(); + } + + /** + * @param executable the executable to check + * @return {@code true}, iff the executable does not have a private modifier + */ + private boolean isNotPrivate(ExecutableElement executable) { + return !executable.getModifiers().contains( Modifier.PRIVATE ); + } + + protected abstract TypeElement replaceTypeElementIfNecessary(TypeElement element); + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/TypesDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractTypeUtilsDecorator.java similarity index 75% rename from processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/TypesDecorator.java rename to processor/src/main/java/org/mapstruct/ap/internal/util/AbstractTypeUtilsDecorator.java index 097ab52631..2f7c4d604d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/TypesDecorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractTypeUtilsDecorator.java @@ -3,10 +3,9 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.util.workarounds; +package org.mapstruct.ap.internal.util; import java.util.List; - import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; @@ -21,23 +20,18 @@ import javax.lang.model.type.WildcardType; import javax.lang.model.util.Types; -import org.mapstruct.ap.internal.version.VersionInformation; - /** - * Replaces the usage of {@link Types} within MapStruct by delegating to the original implementation or to our specific - * workarounds if necessary. + * Replaces the usage of {@link TypeUtils} within MapStruct by delegating to the original implementation or to our + * specific workarounds if necessary. * * @author Andreas Gudian */ -public class TypesDecorator implements Types { +public abstract class AbstractTypeUtilsDecorator implements TypeUtils { + private final Types delegate; - private final ProcessingEnvironment processingEnv; - private final VersionInformation versionInformation; - public TypesDecorator(ProcessingEnvironment processingEnv, VersionInformation versionInformation) { + AbstractTypeUtilsDecorator(ProcessingEnvironment processingEnv) { this.delegate = processingEnv.getTypeUtils(); - this.processingEnv = processingEnv; - this.versionInformation = versionInformation; } @Override @@ -52,12 +46,12 @@ public boolean isSameType(TypeMirror t1, TypeMirror t2) { @Override public boolean isSubtype(TypeMirror t1, TypeMirror t2) { - return SpecificCompilerWorkarounds.isSubtype( delegate, t1, t2 ); + return delegate.isSubtype( t1, t2 ); } @Override public boolean isAssignable(TypeMirror t1, TypeMirror t2) { - return SpecificCompilerWorkarounds.isAssignable( delegate, t1, t2 ); + return delegate.isAssignable( t1, t2 ); } @Override @@ -77,7 +71,7 @@ public List directSupertypes(TypeMirror t) { @Override public TypeMirror erasure(TypeMirror t) { - return SpecificCompilerWorkarounds.erasure( delegate, t ); + return delegate.erasure( t ); } @Override @@ -132,11 +126,11 @@ public DeclaredType getDeclaredType(DeclaredType containing, TypeElement typeEle @Override public TypeMirror asMemberOf(DeclaredType containing, Element element) { - return SpecificCompilerWorkarounds.asMemberOf( - delegate, - processingEnv, - versionInformation, - containing, - element ); + return delegate.asMemberOf( containing, element ); + } + + @Override + public boolean isSubtypeErased(TypeMirror t1, TypeMirror t2) { + return delegate.isSubtype( erasure( t1 ), erasure( t2 ) ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java index 889b729318..d66d81f9b0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java @@ -6,9 +6,9 @@ package org.mapstruct.ap.internal.util; /** - * Utilities for working with classes. It is mainly needed because using the {@link javax.lang.model.util.Elements} + * Utilities for working with classes. It is mainly needed because using the {@link ElementUtils} * is not always correct. For example when compiling with JDK 9 and source version 8 classes from different modules - * are available by {@link javax.lang.model.util.Elements#getTypeElement(CharSequence)} but they are actually not + * are available by {@link ElementUtils#getTypeElement(CharSequence)} but they are actually not * if those modules are not added during compilation. * * @author Filip Hrisafov diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java new file mode 100644 index 0000000000..aaf1bb7e20 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java @@ -0,0 +1,40 @@ +/* + * 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.internal.util; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; + +public class EclipseElementUtilsDecorator extends AbstractElementUtilsDecorator { + + private final Elements delegate; + + EclipseElementUtilsDecorator(ProcessingEnvironment processingEnv) { + super( processingEnv ); + this.delegate = processingEnv.getElementUtils(); + } + + /** + * When running during Eclipse Incremental Compilation, we might get a TypeElement that has an UnresolvedTypeBinding + * and which is not automatically resolved. In that case, getEnclosedElements returns an empty list. We take that as + * a hint to check if the TypeElement resolved by FQN might have any enclosed elements and, if so, return the + * resolved element. + * + * @param element the original element + * @return the element freshly resolved using the qualified name, if the original element did not return any + * enclosed elements, whereas the resolved element does return enclosed elements. + */ + protected TypeElement replaceTypeElementIfNecessary(TypeElement element) { + if ( element.getEnclosedElements().isEmpty() ) { + TypeElement resolvedByName = delegate.getTypeElement( element.getQualifiedName() ); + if ( resolvedByName != null && !resolvedByName.getEnclosedElements().isEmpty() ) { + return resolvedByName; + } + } + return element; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseTypeUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseTypeUtilsDecorator.java new file mode 100644 index 0000000000..ce01d99de8 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseTypeUtilsDecorator.java @@ -0,0 +1,15 @@ +/* + * 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.internal.util; + +import javax.annotation.processing.ProcessingEnvironment; + +public class EclipseTypeUtilsDecorator extends AbstractTypeUtilsDecorator { + + EclipseTypeUtilsDecorator(ProcessingEnvironment processingEnv) { + super( processingEnv ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java new file mode 100644 index 0000000000..fdd221c1a6 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java @@ -0,0 +1,46 @@ +/* + * 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.internal.util; + +import java.util.List; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.util.Elements; + +import org.mapstruct.ap.internal.version.VersionInformation; + +public interface ElementUtils extends Elements { + + static ElementUtils create(ProcessingEnvironment processingEnvironment, VersionInformation info ) { + if ( info.isEclipseJDTCompiler() ) { + return new EclipseElementUtilsDecorator( processingEnvironment ); + } + else { + return new JavacElementUtilsDecorator( processingEnvironment ); + } + } + + /** + * Finds all executable elements within the given type element, including executable elements defined in super + * classes and implemented interfaces. Methods defined in {@link java.lang.Object}, + * implementations of {@link java.lang.Object#equals(Object)} and private methods are ignored + * + * @param element the element to inspect + * @return the executable elements usable in the type + */ + List getAllEnclosedExecutableElements(TypeElement element); + + /** + * Finds all variable elements within the given type element, including variable + * elements defined in super classes and implemented interfaces and including the fields in the . + * + * @param element the element to inspect + * @return the executable elements usable in the type + */ + List getAllEnclosedFields(TypeElement element); +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java index b6de4662b1..1592a39fda 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java @@ -7,24 +7,12 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; import org.mapstruct.ap.internal.gem.AfterMappingGem; import org.mapstruct.ap.internal.gem.BeforeMappingGem; import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.spi.TypeHierarchyErroneousException; - -import static javax.lang.model.util.ElementFilter.methodsIn; -import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary; /** * Provides functionality around {@link ExecutableElement}s. @@ -74,151 +62,6 @@ public static boolean isDefaultMethod(ExecutableElement method) { } } - /** - * @param mirror the type positionHint - * - * @return the corresponding type element - */ - private static TypeElement asTypeElement(TypeMirror mirror) { - return (TypeElement) ( (DeclaredType) mirror ).asElement(); - } - - /** - * Finds all executable elements within the given type element, including executable elements defined in super - * classes and implemented interfaces. Methods defined in {@link java.lang.Object}, - * implementations of {@link java.lang.Object#equals(Object)} and private methods are ignored - * - * @param elementUtils element helper - * @param element the element to inspect - * - * @return the executable elements usable in the type - */ - public static List getAllEnclosedExecutableElements(Elements elementUtils, TypeElement element) { - List enclosedElements = new ArrayList<>(); - element = replaceTypeElementIfNecessary( elementUtils, element ); - addEnclosedElementsInHierarchy( elementUtils, enclosedElements, element, element ); - - return enclosedElements; - } - - private static void addEnclosedElementsInHierarchy(Elements elementUtils, List alreadyAdded, - TypeElement element, TypeElement parentType) { - if ( element != parentType ) { // otherwise the element was already checked for replacement - element = replaceTypeElementIfNecessary( elementUtils, element ); - } - - if ( element.asType().getKind() == TypeKind.ERROR ) { - throw new TypeHierarchyErroneousException( element ); - } - - addNotYetOverridden( elementUtils, alreadyAdded, methodsIn( element.getEnclosedElements() ), parentType ); - - if ( hasNonObjectSuperclass( element ) ) { - addEnclosedElementsInHierarchy( - elementUtils, - alreadyAdded, - asTypeElement( element.getSuperclass() ), - parentType - ); - } - - for ( TypeMirror interfaceType : element.getInterfaces() ) { - addEnclosedElementsInHierarchy( - elementUtils, - alreadyAdded, - asTypeElement( interfaceType ), - parentType - ); - } - - } - - /** - * @param alreadyCollected methods that have already been collected and to which the not-yet-overridden methods will - * be added - * @param methodsToAdd methods to add to alreadyAdded, if they are not yet overridden by an element in the list - * @param parentType the type for with elements are collected - */ - private static void addNotYetOverridden(Elements elementUtils, List alreadyCollected, - List methodsToAdd, TypeElement parentType) { - List safeToAdd = new ArrayList<>( methodsToAdd.size() ); - for ( ExecutableElement toAdd : methodsToAdd ) { - if ( isNotPrivate( toAdd ) && isNotObjectEquals( toAdd ) - && wasNotYetOverridden( elementUtils, alreadyCollected, toAdd, parentType ) ) { - safeToAdd.add( toAdd ); - } - } - - alreadyCollected.addAll( 0, safeToAdd ); - } - - /** - * @param executable the executable to check - * - * @return {@code true}, iff the executable does not represent {@link java.lang.Object#equals(Object)} or an - * overridden version of it - */ - private static boolean isNotObjectEquals(ExecutableElement executable) { - if ( executable.getSimpleName().contentEquals( "equals" ) && executable.getParameters().size() == 1 - && asTypeElement( executable.getParameters().get( 0 ).asType() ).getQualifiedName().contentEquals( - "java.lang.Object" - ) ) { - return false; - } - return true; - } - - /** - * @param executable the executable to check - * - * @return {@code true}, iff the executable does not have a private modifier - */ - private static boolean isNotPrivate(ExecutableElement executable) { - return !executable.getModifiers().contains( Modifier.PRIVATE ); - } - - /** - * @param elementUtils the elementUtils - * @param alreadyCollected the list of already collected methods of one type hierarchy (order is from sub-types to - * super-types) - * @param executable the method to check - * @param parentType the type for which elements are collected - * @return {@code true}, iff the given executable was not yet overridden by a method in the given list. - */ - private static boolean wasNotYetOverridden(Elements elementUtils, List alreadyCollected, - ExecutableElement executable, TypeElement parentType) { - for ( ListIterator it = alreadyCollected.listIterator(); it.hasNext(); ) { - ExecutableElement executableInSubtype = it.next(); - if ( executableInSubtype == null ) { - continue; - } - if ( elementUtils.overrides( executableInSubtype, executable, parentType ) ) { - return false; - } - else if ( elementUtils.overrides( executable, executableInSubtype, parentType ) ) { - // remove the method from another interface hierarchy that is overridden by the executable to add - it.remove(); - return true; - } - } - - return true; - } - - /** - * @param element the type element to check - * - * @return {@code true}, iff the type has a super-class that is not java.lang.Object - */ - private static boolean hasNonObjectSuperclass(TypeElement element) { - if ( element.getSuperclass().getKind() == TypeKind.ERROR ) { - throw new TypeHierarchyErroneousException( element ); - } - - return element.getSuperclass().getKind() == TypeKind.DECLARED - && !asTypeElement( element.getSuperclass() ).getQualifiedName().toString().equals( "java.lang.Object" ); - } - /** * @param executableElement the element to check * @return {@code true}, if the executable element is a method annotated with {@code @BeforeMapping} or diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java index f394f4e8f2..8e16a95274 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java @@ -5,21 +5,15 @@ */ package org.mapstruct.ap.internal.util; -import java.util.ArrayList; -import java.util.List; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; import org.mapstruct.ap.spi.TypeHierarchyErroneousException; -import static javax.lang.model.util.ElementFilter.fieldsIn; -import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary; - /** * Provides functionality around {@link VariableElement}s. * @@ -42,53 +36,6 @@ private static boolean isNotStatic(VariableElement method) { return !method.getModifiers().contains( Modifier.STATIC ); } - /** - * Finds all variable elements within the given type element, including variable - * elements defined in super classes and implemented interfaces and including the fields in the . - * - * @param elementUtils element helper - * @param element the element to inspect - * - * @return the executable elements usable in the type - */ - public static List getAllEnclosedFields(Elements elementUtils, TypeElement element) { - List enclosedElements = new ArrayList<>(); - element = replaceTypeElementIfNecessary( elementUtils, element ); - addEnclosedElementsInHierarchy( elementUtils, enclosedElements, element, element ); - - return enclosedElements; - } - - private static void addEnclosedElementsInHierarchy(Elements elementUtils, List alreadyAdded, - TypeElement element, TypeElement parentType) { - if ( element != parentType ) { // otherwise the element was already checked for replacement - element = replaceTypeElementIfNecessary( elementUtils, element ); - } - - if ( element.asType().getKind() == TypeKind.ERROR ) { - throw new TypeHierarchyErroneousException( element ); - } - - addFields( alreadyAdded, fieldsIn( element.getEnclosedElements() ) ); - - - if ( hasNonObjectSuperclass( element ) ) { - addEnclosedElementsInHierarchy( - elementUtils, - alreadyAdded, - asTypeElement( element.getSuperclass() ), - parentType - ); - } - } - - private static void addFields(List alreadyCollected, List variablesToAdd) { - List safeToAdd = new ArrayList<>( variablesToAdd.size() ); - safeToAdd.addAll( variablesToAdd ); - - alreadyCollected.addAll( 0, safeToAdd ); - } - private static TypeElement asTypeElement(TypeMirror mirror) { return (TypeElement) ( (DeclaredType) mirror ).asElement(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java index f4d52d7095..9cb923b3c8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java @@ -20,7 +20,6 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Types; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; @@ -60,10 +59,10 @@ public class Filters { } private final AccessorNamingUtils accessorNaming; - private final Types typeUtils; + private final TypeUtils typeUtils; private final TypeMirror typeMirror; - public Filters(AccessorNamingUtils accessorNaming, Types typeUtils, TypeMirror typeMirror) { + public Filters(AccessorNamingUtils accessorNaming, TypeUtils typeUtils, TypeMirror typeMirror) { this.accessorNaming = accessorNaming; this.typeUtils = typeUtils; this.typeMirror = typeMirror; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java new file mode 100644 index 0000000000..ad035743cf --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java @@ -0,0 +1,21 @@ +/* + * 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.internal.util; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.TypeElement; + +public class JavacElementUtilsDecorator extends AbstractElementUtilsDecorator { + + JavacElementUtilsDecorator(ProcessingEnvironment processingEnv) { + super( processingEnv ); + } + + @Override + protected TypeElement replaceTypeElementIfNecessary(TypeElement element) { + return element; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JavacTypeUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JavacTypeUtilsDecorator.java new file mode 100644 index 0000000000..8b3454e87e --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JavacTypeUtilsDecorator.java @@ -0,0 +1,15 @@ +/* + * 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.internal.util; + +import javax.annotation.processing.ProcessingEnvironment; + +public class JavacTypeUtilsDecorator extends AbstractTypeUtilsDecorator { + + JavacTypeUtilsDecorator(ProcessingEnvironment processingEnv) { + super( processingEnv ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/TypeUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/TypeUtils.java new file mode 100644 index 0000000000..926cd5eef8 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/TypeUtils.java @@ -0,0 +1,26 @@ +/* + * 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.internal.util; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Types; + +import org.mapstruct.ap.internal.version.VersionInformation; + +public interface TypeUtils extends Types { + + static TypeUtils create(ProcessingEnvironment processingEnvironment, VersionInformation info ) { + if ( info.isEclipseJDTCompiler() ) { + return new EclipseTypeUtilsDecorator( processingEnvironment ); + } + else { + return new JavacTypeUtilsDecorator( processingEnvironment ); + } + } + + boolean isSubtypeErased(TypeMirror t1, TypeMirror t2); +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseAsMemberOfWorkaround.java b/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseAsMemberOfWorkaround.java deleted file mode 100644 index ab0d681728..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseAsMemberOfWorkaround.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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.internal.util.workarounds; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Types; - -import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl; -import org.eclipse.jdt.internal.compiler.apt.model.ElementImpl; -import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; -import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; -import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; - -/** - * Contains the workaround for {@link Types#asMemberOf(DeclaredType, Element)} using Eclipse implementation types. - *

      - * This class may only be accessed through {@link EclipseClassLoaderBridge} when running within Eclipse - * - * @author Andreas Gudian - */ -final class EclipseAsMemberOfWorkaround { - private EclipseAsMemberOfWorkaround() { - } - - /** - * Eclipse-specific implementation of {@link Types#asMemberOf(DeclaredType, Element)}. - *

      - * Returns {@code null} if the implementation could not determine the result. - */ - static TypeMirror asMemberOf(ProcessingEnvironment environment, DeclaredType containing, - Element element) { - - ElementImpl elementImpl = tryCast( element, ElementImpl.class ); - BaseProcessingEnvImpl env = tryCast( environment, BaseProcessingEnvImpl.class ); - - if ( elementImpl == null || env == null ) { - return null; - } - - ReferenceBinding referenceBinding = - (ReferenceBinding) ( (ElementImpl) environment.getTypeUtils().asElement( containing ) )._binding; - - MethodBinding methodBinding = (MethodBinding) elementImpl._binding; - - // matches in super-classes have priority - MethodBinding inSuperclassHiearchy = findInSuperclassHierarchy( methodBinding, referenceBinding ); - - if ( inSuperclassHiearchy != null ) { - return env.getFactory().newTypeMirror( inSuperclassHiearchy ); - } - - // if nothing was found, traverse the interfaces and collect all candidate methods that match - List candidatesFromInterfaces = new ArrayList<>(); - - collectFromInterfaces( - methodBinding, - referenceBinding, - new HashSet<>(), - candidatesFromInterfaces ); - - // there can be multiple matches for the same method name from adjacent interface hierarchies. - candidatesFromInterfaces.sort( MostSpecificMethodBindingComparator.INSTANCE ); - - if ( !candidatesFromInterfaces.isEmpty() ) { - // return the most specific match - return env.getFactory().newTypeMirror( candidatesFromInterfaces.get( 0 ) ); - } - - return null; - } - - private static T tryCast(Object instance, Class type) { - if ( type.isInstance( instance ) ) { - return type.cast( instance ); - } - - return null; - } - - private static void collectFromInterfaces(MethodBinding methodBinding, ReferenceBinding typeBinding, - Set visitedTypes, List found) { - if ( typeBinding == null ) { - return; - } - - // also check the interfaces of the superclass hierarchy (the superclasses themselves don't contain a match, - // we checked that already) - collectFromInterfaces( methodBinding, typeBinding.superclass(), visitedTypes, found ); - - for ( ReferenceBinding ifc : typeBinding.superInterfaces() ) { - if ( visitedTypes.contains( ifc ) ) { - continue; - } - - visitedTypes.add( ifc ); - - // finding a match in one interface - MethodBinding f = findMatchingMethodBinding( methodBinding, ifc.methods() ); - - if ( f == null ) { - collectFromInterfaces( methodBinding, ifc, visitedTypes, found ); - } - else { - // no need for recursion if we found a candidate in this type already - found.add( f ); - } - } - } - - /** - * @param baseMethod binding to compare against - * @param methods the candidate methods - * @return The method from the list of candidates that matches the name and original/erasure of - * {@code methodBinding}, or {@code null} if none was found. - */ - private static MethodBinding findMatchingMethodBinding(MethodBinding baseMethod, MethodBinding[] methods) { - for ( MethodBinding method : methods ) { - if ( CharOperation.equals( method.selector, baseMethod.selector ) - && ( method.original() == baseMethod || method.areParameterErasuresEqual( baseMethod ) ) ) { - return method; - } - } - - return null; - } - - private static MethodBinding findInSuperclassHierarchy(MethodBinding baseMethod, ReferenceBinding typeBinding) { - while ( typeBinding != null ) { - MethodBinding matching = findMatchingMethodBinding( baseMethod, typeBinding.methods() ); - if ( matching != null ) { - return matching; - } - - typeBinding = typeBinding.superclass(); - } - - return null; - } - - /** - * Compares MethodBindings by their signature: the more specific method is considered lower. - * - * @author Andreas Gudian - */ - private static final class MostSpecificMethodBindingComparator implements Comparator { - private static final MostSpecificMethodBindingComparator INSTANCE = new MostSpecificMethodBindingComparator(); - - @Override - public int compare(MethodBinding first, MethodBinding second) { - boolean firstParamsAssignableFromSecond = - first.areParametersCompatibleWith( second.parameters ); - boolean secondParamsAssignableFromFirst = - second.areParametersCompatibleWith( first.parameters ); - - if ( firstParamsAssignableFromSecond != secondParamsAssignableFromFirst ) { - return firstParamsAssignableFromSecond ? 1 : -1; - } - - if ( TypeBinding.equalsEquals( first.returnType, second.returnType ) ) { - return 0; - } - - boolean firstReturnTypeAssignableFromSecond = - second.returnType.isCompatibleWith( first.returnType ); - - return firstReturnTypeAssignableFromSecond ? 1 : -1; - } - } -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseClassLoaderBridge.java b/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseClassLoaderBridge.java deleted file mode 100644 index d7dee52fb4..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/EclipseClassLoaderBridge.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.internal.util.workarounds; - -import java.lang.reflect.Method; -import java.net.URLClassLoader; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; - -/** - * In Eclipse 4.6, the ClassLoader of the annotation processor does not provide access to the implementation types of - * the APT-API anymore, so we need to create a new ClassLoader containing the processor class path URLs and having the - * ClassLoader of the APT-API implementations as parent. The method invocation then consequently needs to be done via - * reflection. - * - * @author Andreas Gudian - */ -class EclipseClassLoaderBridge { - private static final String ECLIPSE_AS_MEMBER_OF_WORKAROUND = - "org.mapstruct.ap.internal.util.workarounds.EclipseAsMemberOfWorkaround"; - - private static ClassLoader classLoader; - private static Method asMemberOf; - - private EclipseClassLoaderBridge() { - } - - /** - * Invokes {@link EclipseAsMemberOfWorkaround#asMemberOf(ProcessingEnvironment, DeclaredType, Element)} via - * reflection using a special ClassLoader. - */ - static TypeMirror invokeAsMemberOfWorkaround(ProcessingEnvironment env, DeclaredType containing, Element element) - throws Exception { - return (TypeMirror) getAsMemberOf( element.getClass().getClassLoader() ).invoke( - null, - env, - containing, - element ); - } - - private static ClassLoader getOrCreateClassLoader(ClassLoader parent) { - if ( classLoader == null ) { - classLoader = new URLClassLoader( - ( (URLClassLoader) EclipseClassLoaderBridge.class.getClassLoader() ).getURLs(), - parent ); - } - - return classLoader; - } - - private static Method getAsMemberOf(ClassLoader platformClassLoader) throws Exception { - if ( asMemberOf == null ) { - Class workaroundClass = - getOrCreateClassLoader( platformClassLoader ).loadClass( ECLIPSE_AS_MEMBER_OF_WORKAROUND ); - - Method found = workaroundClass.getDeclaredMethod( - "asMemberOf", - ProcessingEnvironment.class, - DeclaredType.class, - Element.class ); - - found.setAccessible( true ); - - asMemberOf = found; - } - - return asMemberOf; - } -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/SpecificCompilerWorkarounds.java b/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/SpecificCompilerWorkarounds.java deleted file mode 100644 index 69b45c1ca7..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/workarounds/SpecificCompilerWorkarounds.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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.internal.util.workarounds; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; - -import org.mapstruct.ap.internal.version.VersionInformation; - -/** - * Contains workarounds for various quirks in specific compilers. - * - * @author Sjaak Derksen - * @author Andreas Gudian - */ -public class SpecificCompilerWorkarounds { - private SpecificCompilerWorkarounds() { - } - - /** - * Tests whether one type is assignable to another, checking for VOID first. - * - * @param types the type utils - * @param t1 the first type - * @param t2 the second type - * @return {@code true} if and only if the first type is assignable to the second - * @throws IllegalArgumentException if given an executable or package type - */ - static boolean isAssignable(Types types, TypeMirror t1, TypeMirror t2) { - if ( t1.getKind() == TypeKind.VOID ) { - return false; - } - - return types.isAssignable( t1, t2 ); - } - - /** - * Tests whether one type is a subtype of another. Any type is considered to be a subtype of itself. Also see JLS section 4.10, Subtyping. - *

      - * Work-around for a bug related to sub-typing in the Eclipse JSR 269 implementation. - * - * @param types the type utils - * @param t1 the first type - * @param t2 the second type - * @return {@code true} if and only if the first type is a subtype of the second - * @throws IllegalArgumentException if given an executable or package type - */ - static boolean isSubtype(Types types, TypeMirror t1, TypeMirror t2) { - if ( t1.getKind() == TypeKind.VOID ) { - return false; - } - - return types.isSubtype( erasure( types, t1 ), erasure( types, t2 ) ); - } - - /** - * Returns the erasure of a type. - *

      - * Performs an additional test on the given type to check if it is not void. Calling - * {@link Types#erasure(TypeMirror)} with a void kind type will create a ClassCastException in Eclipse JDT. See the - * JLS, section 4.6 Type Erasure, for reference. - * - * @param types the type utils - * @param t the type to be erased - * @return the erasure of the given type - * @throws IllegalArgumentException if given a package type - */ - static TypeMirror erasure(Types types, TypeMirror t) { - if ( t.getKind() == TypeKind.VOID || t.getKind() == TypeKind.NULL ) { - return t; - } - else { - return types.erasure( t ); - } - } - - /** - * When running during Eclipse Incremental Compilation, we might get a TypeElement that has an UnresolvedTypeBinding - * and which is not automatically resolved. In that case, getEnclosedElements returns an empty list. We take that as - * a hint to check if the TypeElement resolved by FQN might have any enclosed elements and, if so, return the - * resolved element. - * - * @param elementUtils element utils - * @param element the original element - * @return the element freshly resolved using the qualified name, if the original element did not return any - * enclosed elements, whereas the resolved element does return enclosed elements. - */ - public static TypeElement replaceTypeElementIfNecessary(Elements elementUtils, TypeElement element) { - if ( element.getEnclosedElements().isEmpty() ) { - TypeElement resolvedByName = elementUtils.getTypeElement( element.getQualifiedName() ); - if ( resolvedByName != null && !resolvedByName.getEnclosedElements().isEmpty() ) { - return resolvedByName; - } - } - return element; - } - - /** - * Workaround for Bugs in the Eclipse implementation of {@link Types#asMemberOf(DeclaredType, Element)}. - * - * @see Eclipse Bug 382590 (fixed in Eclipse 4.6) - * @see Eclipse Bug 481555 - */ - static TypeMirror asMemberOf(Types typeUtils, ProcessingEnvironment env, VersionInformation versionInformation, - DeclaredType containing, Element element) { - TypeMirror result = null; - Exception lastException = null; - try { - try { - result = typeUtils.asMemberOf( containing, element ); - } - catch ( IllegalArgumentException e ) { - lastException = e; - if ( versionInformation.isEclipseJDTCompiler() ) { - result = EclipseClassLoaderBridge.invokeAsMemberOfWorkaround( env, containing, element ); - } - } - } - catch ( Exception e ) { - lastException = e; - } - - if ( null == result ) { - throw new RuntimeException( "Fallback implementation of asMemberOf didn't work for " - + element + " in " + containing, lastException ); - } - - return result; - } -} diff --git a/processor/src/test/java/org/mapstruct/ap/internal/model/source/SelectionParametersTest.java b/processor/src/test/java/org/mapstruct/ap/internal/model/source/SelectionParametersTest.java index 7f57ae75cb..19e194a764 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/model/source/SelectionParametersTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/model/source/SelectionParametersTest.java @@ -23,9 +23,9 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVisitor; import javax.lang.model.type.WildcardType; -import javax.lang.model.util.Types; import org.junit.Test; +import org.mapstruct.ap.internal.util.TypeUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -73,7 +73,12 @@ public String toString() { } } - private final Types typeUtils = new Types() { + private final TypeUtils typeUtils = new TypeUtils() { + @Override + public boolean isSubtypeErased(TypeMirror t1, TypeMirror t2) { + throw new UnsupportedOperationException( "isSubTypeErased is not supported" ); + } + @Override public Element asElement(TypeMirror t) { throw new UnsupportedOperationException( "asElement is not supported" ); From 26f62b7ef0f8592196ef342b7fad450a64ae4751 Mon Sep 17 00:00:00 2001 From: Saheb Preet Singh Date: Tue, 20 Oct 2020 21:19:21 +0800 Subject: [PATCH 117/597] #607 Mapping Iterable object to an object instead of collection --- .../chapter-6-mapping-collections.asciidoc | 4 +-- .../processor/MethodRetrievalProcessor.java | 8 +++-- .../mapstruct/ap/internal/util/Message.java | 4 +-- .../ErroneousCollectionMappingTest.java | 12 ++++--- ...oneousCollectionToNonCollectionMapper.java | 2 ++ .../iterabletononiterable/Fruit.java | 27 ++++++++++++++ .../iterabletononiterable/FruitSalad.java | 29 +++++++++++++++ .../iterabletononiterable/FruitsMapper.java | 24 +++++++++++++ .../iterabletononiterable/FruitsMenu.java | 35 +++++++++++++++++++ .../IterableToNonIterableMappingTest.java | 26 +++++++++++++- .../erroneous/ErroneousStreamMappingTest.java | 4 +-- 11 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/Fruit.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitSalad.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitsMenu.java diff --git a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc index dcce306789..785b460b12 100644 --- a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc +++ b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc @@ -90,7 +90,7 @@ carDto.getPassengers().addAll( personsToPersonDtos( car.getPassengers() ) ); [WARNING] ==== -It is not allowed to declare mapping methods with an iterable source and a non-iterable target or the other way around. An error will be raised when detecting this situation. +It is not allowed to declare mapping methods with an iterable source (from a java package) and a non-iterable target or the other way around. An error will be raised when detecting this situation. ==== [[mapping-maps]] @@ -224,4 +224,4 @@ When an iterable or map mapping method declares an interface type as return type |`ConcurrentMap`|`ConcurrentHashMap` |`ConcurrentNavigableMap`|`ConcurrentSkipListMap` -|=== \ No newline at end of file +|=== diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 693741bcd2..5985d735c2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -483,7 +483,7 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List strings); Set integerToStringSet(Integer integer); + + Set nonJavaStdlibToCollection(Source stringCollection); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/Fruit.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/Fruit.java new file mode 100644 index 0000000000..f012eb5a84 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/Fruit.java @@ -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.collection.iterabletononiterable; + +/** + * + * @author Saheb Preet Singh + */ +public class Fruit { + + private String type; + + public Fruit(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitSalad.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitSalad.java new file mode 100644 index 0000000000..a6298a6095 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitSalad.java @@ -0,0 +1,29 @@ +/* + * 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.collection.iterabletononiterable; + +import java.util.List; + +/** + * + * @author Saheb Preet Singh + */ +public class FruitSalad { + + private List fruits; + + public FruitSalad(List fruits) { + this.fruits = fruits; + } + + public List getFruits() { + return fruits; + } + + public void setFruits(List fruits) { + this.fruits = fruits; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitsMapper.java new file mode 100644 index 0000000000..3d9cc94bad --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitsMapper.java @@ -0,0 +1,24 @@ +/* + * 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.collection.iterabletononiterable; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * + * @author Saheb Preet Singh + */ +@Mapper +public interface FruitsMapper { + + FruitsMapper INSTANCE = Mappers.getMapper( + FruitsMapper.class ); + + FruitsMenu fruitSaladToMenu(FruitSalad salad); + + FruitSalad fruitsMenuToSalad(FruitsMenu menu); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitsMenu.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitsMenu.java new file mode 100644 index 0000000000..0b1ef94ded --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/FruitsMenu.java @@ -0,0 +1,35 @@ +/* + * 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.collection.iterabletononiterable; + +import java.util.Iterator; +import java.util.List; + +/** + * + * @author Saheb Preet Singh + */ +public class FruitsMenu implements Iterable { + + private List fruits; + + public FruitsMenu(List fruits) { + this.fruits = fruits; + } + + public List getFruits() { + return fruits; + } + + public void setFruits(List fruits) { + this.fruits = fruits; + } + + @Override + public Iterator iterator() { + return this.fruits.iterator(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/IterableToNonIterableMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/IterableToNonIterableMappingTest.java index b852a0a4d8..feb0948c65 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/IterableToNonIterableMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/IterableToNonIterableMappingTest.java @@ -8,6 +8,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -15,7 +16,8 @@ import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -@WithClasses({ Source.class, Target.class, SourceTargetMapper.class, StringListMapper.class }) +@WithClasses({ Source.class, Target.class, SourceTargetMapper.class, StringListMapper.class, FruitsMenu.class, + FruitSalad.class, Fruit.class, FruitsMapper.class }) @RunWith(AnnotationProcessorTestRunner.class) public class IterableToNonIterableMappingTest { @@ -45,4 +47,26 @@ public void shouldReverseMapStringListToStringUsingCustomMapper() { assertThat( source.getNames() ).isEqualTo( Arrays.asList( "Alice", "Bob", "Jim" ) ); assertThat( source.publicNames ).isEqualTo( Arrays.asList( "Alice", "Bob", "Jim" ) ); } + + @Test + @IssueKey("607") + public void shouldMapIterableToNonIterable() { + List fruits = Arrays.asList( new Fruit( "mango" ), new Fruit( "apple" ), + new Fruit( "banana" ) ); + FruitsMenu menu = new FruitsMenu(fruits); + FruitSalad salad = FruitsMapper.INSTANCE.fruitsMenuToSalad( menu ); + assertThat( salad.getFruits().get( 0 ).getType() ).isEqualTo( "mango" ); + assertThat( salad.getFruits().get( 1 ).getType() ).isEqualTo( "apple" ); + assertThat( salad.getFruits().get( 2 ).getType() ).isEqualTo( "banana" ); + } + + @Test + @IssueKey("607") + public void shouldMapNonIterableToIterable() { + List fruits = Arrays.asList( new Fruit( "mango" ), new Fruit( "apple" ), + new Fruit( "banana" ) ); + FruitSalad salad = new FruitSalad(fruits); + FruitsMenu menu = FruitsMapper.INSTANCE.fruitSaladToMenu( salad ); + assertThat( salad.getFruits() ).extracting( Fruit::getType ).containsExactly( "mango", "apple", "banana" ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java index e82ed945a6..e177b8a9a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java @@ -42,11 +42,11 @@ public class ErroneousStreamMappingTest { @Diagnostic(type = ErroneousStreamToNonStreamMapper.class, kind = Kind.ERROR, line = 15, - message = "Can't generate mapping method from iterable type to non-iterable type."), + message = "Can't generate mapping method from iterable type from java stdlib to non-iterable type."), @Diagnostic(type = ErroneousStreamToNonStreamMapper.class, kind = Kind.ERROR, line = 17, - message = "Can't generate mapping method from non-iterable type to iterable type.") + message = "Can't generate mapping method from non-iterable type to iterable type from java stdlib.") } ) public void shouldFailToGenerateImplementationBetweenStreamAndNonStreamOrIterable() { From e67daa37106fddf64e73b26924af3e7a22038728 Mon Sep 17 00:00:00 2001 From: Kemal Ozcan Date: Sat, 24 Oct 2020 17:57:12 +0300 Subject: [PATCH 118/597] mapstruct#597 Built-In conversion between String and StringBuilder --- .../internal/conversion/ConversionUtils.java | 11 ++++++++ .../ap/internal/conversion/Conversions.java | 1 + .../StringBuilderToStringConversion.java | 25 +++++++++++++++++++ .../ap/test/conversion/string/Source.java | 9 +++++++ .../string/StringConversionTest.java | 4 +++ .../ap/test/conversion/string/Target.java | 9 +++++++ .../string/SourceTargetMapperImpl.java | 10 ++++++-- 7 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/StringBuilderToStringConversion.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java index 8d2557b334..2afcf3c6ad 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java @@ -231,4 +231,15 @@ public static String dateTimeFormatter(ConversionContext conversionContext) { public static String dateTimeFormat(ConversionContext conversionContext) { return typeReferenceName( conversionContext, JodaTimeConstants.DATE_TIME_FORMAT_FQN ); } + + /** + * Name for {@link java.lang.StringBuilder}. + * + * @param conversionContext Conversion context + * + * @return Name or fully-qualified name. + */ + public static String stringBuilder(ConversionContext conversionContext) { + return typeReferenceName( conversionContext, StringBuilder.class ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index ad5fe85db3..02c478bb70 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -175,6 +175,7 @@ public Conversions(TypeFactory typeFactory) { register( Character.class, String.class, new CharWrapperToStringConversion() ); register( BigInteger.class, String.class, new BigIntegerToStringConversion() ); register( BigDecimal.class, String.class, new BigDecimalToStringConversion() ); + register( StringBuilder.class, String.class, new StringBuilderToStringConversion() ); registerJodaConversions(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/StringBuilderToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/StringBuilderToStringConversion.java new file mode 100644 index 0000000000..2f6705fab8 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/StringBuilderToStringConversion.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.ap.internal.conversion; + +import org.mapstruct.ap.internal.model.common.ConversionContext; + +/** + * Handles conversion between a target type {@link StringBuilder} and {@link String}. + * + */ +public class StringBuilderToStringConversion extends SimpleConversion { + + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toString()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return "new " + ConversionUtils.stringBuilder( conversionContext ) + "( )"; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/Source.java index ae9f226351..7f95abc797 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/Source.java @@ -23,6 +23,7 @@ public class Source { private Boolean boolBool; private char c; private Character cc; + private StringBuilder sb; private Object object; public byte getB() { @@ -160,4 +161,12 @@ public Object getObject() { public void setObject(Object object) { this.object = object; } + + public StringBuilder getSb() { + return sb; + } + + public void setSb(StringBuilder sb) { + this.sb = sb; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java index 8b7fe0f058..bc98fff256 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java @@ -48,6 +48,7 @@ public void shouldApplyStringConversions() { source.setBoolBool( Boolean.TRUE ); source.setC( 'G' ); source.setCc( 'H' ); + source.setSb( new StringBuilder( "SB" ) ); Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); @@ -68,6 +69,7 @@ public void shouldApplyStringConversions() { assertThat( target.getBoolBool() ).isEqualTo( "true" ); assertThat( target.getC() ).isEqualTo( "G" ); assertThat( target.getCc() ).isEqualTo( "H" ); + assertThat( target.getSb() ).isEqualTo( "SB" ); } @Test @@ -89,6 +91,7 @@ public void shouldApplyReverseStringConversions() { target.setBoolBool( "true" ); target.setC( "G" ); target.setCc( "H" ); + target.setSb( "SB" ); Source source = SourceTargetMapper.INSTANCE.targetToSource( target ); @@ -109,6 +112,7 @@ public void shouldApplyReverseStringConversions() { assertThat( source.getBoolBool() ).isEqualTo( true ); assertThat( source.getC() ).isEqualTo( 'G' ); assertThat( source.getCc() ).isEqualTo( 'H' ); + assertThat( source.getSb().toString() ).isEqualTo( "SB" ); } @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/Target.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/Target.java index db6c3553c2..7d58e9c12b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/Target.java @@ -23,6 +23,7 @@ public class Target { private String boolBool; private String c; private String cc; + private String sb; private String object; public String getB() { @@ -160,4 +161,12 @@ public String getObject() { public void setObject(String object) { this.object = object; } + + public String getSb() { + return sb; + } + + public void setSb(String sb) { + this.sb = sb; + } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/string/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/string/SourceTargetMapperImpl.java index 7317a6ba60..ecfa910627 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/string/SourceTargetMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/string/SourceTargetMapperImpl.java @@ -9,8 +9,8 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2016-12-30T19:22:52+0100", - comments = "version: , compiler: javac, environment: Java 1.8.0_112 (Oracle Corporation)" + date = "2020-10-24T17:41:29+0300", + comments = "version: , compiler: javac, environment: Java 1.8.0_265 (AdoptOpenJDK)" ) public class SourceTargetMapperImpl implements SourceTargetMapper { @@ -54,6 +54,9 @@ public Target sourceToTarget(Source source) { if ( source.getCc() != null ) { target.setCc( source.getCc().toString() ); } + if ( source.getSb() != null ) { + target.setSb( source.getSb().toString() ); + } return target; } @@ -115,6 +118,9 @@ public Source targetToSource(Target target) { source.setCc( target.getCc().charAt( 0 ) ); } source.setObject( target.getObject() ); + if ( target.getSb() != null ) { + source.setSb( new StringBuilder( target.getSb() ) ); + } return source; } From 85890dd442736ea79167bd099c8d68915d384f6c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 24 Oct 2020 18:24:49 +0200 Subject: [PATCH 119/597] #2245 Local variable should be created when using default value --- .../ap/internal/model/PropertyMapping.java | 9 ++- .../ap/test/bugs/_2245/Issue2245Test.java | 42 ++++++++++++++ .../ap/test/bugs/_2245/TestMapper.java | 55 +++++++++++++++++++ .../ap/test/bugs/_2245/TestMapperImpl.java | 50 +++++++++++++++++ 4 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/Issue2245Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/TestMapper.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2245/TestMapperImpl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 240343ad9b..dbda3daac3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -374,6 +374,11 @@ private Assignment getDefaultValueAssignment( Assignment rhs ) { return null; } + private boolean hasDefaultValueAssignment(Assignment rhs) { + return ( defaultValue != null || defaultJavaExpression != null ) && + ( !rhs.getSourceType().isPrimitive() || rhs.getSourcePresenceCheckerReference() != null); + } + private Assignment assignToPlain(Type targetType, AccessorType targetAccessorType, Assignment rightHandSide) { @@ -415,7 +420,9 @@ private Assignment assignToPlainViaSetter(Type targetType, Assignment rhs) { ); } else { - boolean includeSourceNullCheck = SetterWrapper.doSourceNullCheck( rhs, nvcs, nvpms, targetType ); + // If the property mapping has a default value assignment then we have to do a null value check + boolean includeSourceNullCheck = SetterWrapper.doSourceNullCheck( rhs, nvcs, nvpms, targetType ) + || hasDefaultValueAssignment( rhs ); 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. diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/Issue2245Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/Issue2245Test.java new file mode 100644 index 0000000000..5aa899d055 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/Issue2245Test.java @@ -0,0 +1,42 @@ +/* + * 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._2245; + +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; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2245") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + TestMapper.class +}) +public class Issue2245Test { + + @Rule + public final GeneratedSource generatedSource = new GeneratedSource() + .addComparisonToFixtureFor( TestMapper.class ); + + @Test + public void shouldGenerateSourceGetMethodOnce() { + + TestMapper.Tenant tenant = + TestMapper.INSTANCE.map( new TestMapper.TenantDTO( new TestMapper.Inner( "acme" ) ) ); + + assertThat( tenant ).isNotNull(); + assertThat( tenant.getId() ).isEqualTo( "acme" ); + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/TestMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/TestMapper.java new file mode 100644 index 0000000000..750ee12fa5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/TestMapper.java @@ -0,0 +1,55 @@ +/* + * 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._2245; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface TestMapper { + + TestMapper INSTANCE = Mappers.getMapper( TestMapper.class ); + + class Tenant { + private String id; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + } + + class Inner { + private final String id; + + public Inner(String id) { + this.id = id; + } + + public String getId() { + return id; + } + } + + class TenantDTO { + private final Inner inner; + + public TenantDTO(Inner inner) { + this.inner = inner; + } + + public Inner getInner() { + return inner; + } + } + + @Mapping(target = "id", source = "inner.id", defaultValue = "test") + Tenant map(TenantDTO tenant); +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2245/TestMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2245/TestMapperImpl.java new file mode 100644 index 0000000000..c3ecc02911 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2245/TestMapperImpl.java @@ -0,0 +1,50 @@ +/* + * 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._2245; + +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2020-10-24T17:57:23+0200", + comments = "version: , compiler: javac, environment: Java 1.8.0_202 (AdoptOpenJdk)" +) +public class TestMapperImpl implements TestMapper { + + @Override + public Tenant map(TenantDTO tenant) { + if ( tenant == null ) { + return null; + } + + Tenant tenant1 = new Tenant(); + + String id = tenantInnerId( tenant ); + if ( id != null ) { + tenant1.setId( id ); + } + else { + tenant1.setId( "test" ); + } + + return tenant1; + } + + private String tenantInnerId(TenantDTO tenantDTO) { + if ( tenantDTO == null ) { + return null; + } + Inner inner = tenantDTO.getInner(); + if ( inner == null ) { + return null; + } + String id = inner.getId(); + if ( id == null ) { + return null; + } + return id; + } +} From 3256abb79ccee1727c0e84f1dc9847aa695ed918 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 24 Oct 2020 17:46:20 +0200 Subject: [PATCH 120/597] #2244 Mark mapstruct-processor jar as Spring-Boot-Jar-Type: annotation-processor Doing this would make sure that starting from Spring Boot 2.4 the mapstruct-processor will not be included in the fat jar produced by the Spring Boot maven plugin if people have it as a maven provided dependency. This is an alternative if people are not using the maven-compiler-plugin annotationProcessorPaths --- processor/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/processor/pom.xml b/processor/pom.xml index bbec83bf29..2ff5d2d186 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -123,6 +123,7 @@ org.mapstruct.processor + annotation-processor From 749ded96c17114cec676b313d888f8bde464eb84 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 31 Oct 2020 12:56:25 +0100 Subject: [PATCH 121/597] #2251 Fix incorrect code generated for constructor mapping from implicit source parameter matching --- .../ap/internal/model/BeanMappingMethod.java | 1 + .../ap/test/bugs/_2251/Issue2251Mapper.java | 20 ++++++++++ .../ap/test/bugs/_2251/Issue2251Test.java | 37 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2251/Source.java | 22 +++++++++++ .../mapstruct/ap/test/bugs/_2251/Target.java | 24 ++++++++++++ 5 files changed, 104 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Target.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 4d086b5126..b17e060848 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1315,6 +1315,7 @@ private void applyParameterNameBasedMapping() { sourceParameters.remove(); unprocessedDefinedTargets.remove( targetProperty.getKey() ); unprocessedSourceProperties.remove( targetProperty.getKey() ); + unprocessedConstructorProperties.remove( targetProperty.getKey() ); } } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Mapper.java new file mode 100644 index 0000000000..d09f3440fe --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Mapper.java @@ -0,0 +1,20 @@ +/* + * 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._2251; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2251Mapper { + + Issue2251Mapper INSTANCE = Mappers.getMapper( Issue2251Mapper.class ); + + @Mapping(target = "value1", source = "source.value") + Target map(Source source, String value2); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Test.java new file mode 100644 index 0000000000..d08a5f7d74 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Test.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.bugs._2251; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2251") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2251Mapper.class, + Source.class, + Target.class, +}) +public class Issue2251Test { + + @Test + public void shouldGenerateCorrectCode() { + + Target target = Issue2251Mapper.INSTANCE.map( new Source( "source" ), "test" ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue1() ).isEqualTo( "source" ); + assertThat( target.getValue2() ).isEqualTo( "test" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Source.java new file mode 100644 index 0000000000..cbd882a8f8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Source.java @@ -0,0 +1,22 @@ +/* + * 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._2251; + +/** + * @author Filip Hrisafov + */ +public class Source { + + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Target.java new file mode 100644 index 0000000000..363129797f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Target.java @@ -0,0 +1,24 @@ +/* + * 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._2251; + +public class Target { + private final String value1; + private final String value2; + + public Target(String value1, String value2) { + this.value1 = value1; + this.value2 = value2; + } + + public String getValue1() { + return value1; + } + + public String getValue2() { + return value2; + } +} From 8f9df5b69b6adbc789243a76a5b72b61ea7422fe Mon Sep 17 00:00:00 2001 From: Nikolas Charalambidis Date: Wed, 4 Nov 2020 21:39:06 +0100 Subject: [PATCH 122/597] #2258 Fixes vague description of @Default and @ConstructorProperties annotations --- ...er-14-third-party-api-integration.asciidoc | 41 +++++++++++++++++++ .../chapter-3-defining-a-mapper.asciidoc | 6 +-- .../mapstruct-reference-guide.asciidoc | 4 +- 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc diff --git a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc new file mode 100644 index 0000000000..03e18167e3 --- /dev/null +++ b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc @@ -0,0 +1,41 @@ +[[third-party-api-integration]] +== Third-party API integration + +[[non-shipped-annotations]] +=== Non-shipped annotations + +There are various use-cases you must resolve ambiguity for MapStruct to use a correct piece of code. +However, the primary goal of MapStruct is to focus on bean mapping without polluting the entity code. +For that reason, MapStruct is flexible enough to interact with already defined annotations from third-party libraries. +The requirement to enable this behavior is to match the _name_ of such annotation. +Hence, we say that annotation can be _from any package_. + +The annotations _named_ `@ConstructorProperties` and `@Default` are currently examples of this kind of annotation. + +[WARNING] +==== +If such named third-party annotation exists, it does not guarantee its `@Target` matches with the intended placement. +Be aware of placing a third-party annotation just for sake of mapping is not recommended as long as it might lead to unwanted side effects caused by that library. +==== + +A very common case is that no third-party dependency imported to your project provides such annotation or is inappropriate for use as already described. +In such cases create your own annotation, for example: + +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +package foo.support.mapstruct; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.CONSTRUCTOR) +@Retention(RetentionPolicy.CLASS) +public @interface Default { + +} +---- +==== \ No newline at end of file diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 54a0dbd709..acdfd98174 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -537,10 +537,10 @@ When doing a mapping MapStruct checks if there is a builder for the type being m If there is no builder, then MapStruct looks for a single accessible constructor. When there are multiple constructors then the following is done to pick the one which should be used: -* If a constructor is annotated with an annotation named `@Default` (from any package) it will be used. +* If a constructor is annotated with an annotation _named_ `@Default` (from any package, see <>) it will be used. * If a single public constructor exists then it will be used to construct the object, and the other non public constructors will be ignored. * If a parameterless constructor exists then it will be used to construct the object, and the other constructors will be ignored. -* If there are multiple eligible constructors then there will be a compilation error due to ambiguous constructors. In order to break the ambiguity an annotation named `@Default` (from any package) can used. +* If there are multiple eligible constructors then there will be a compilation error due to ambiguous constructors. In order to break the ambiguity an annotation _named_ `@Default` (from any package, see <>) can used. .Deciding which constructor to use ==== @@ -585,7 +585,7 @@ public class Van { ==== When using a constructor then the names of the parameters of the constructor will be used and matched to the target properties. -When the constructor has an annotation named `@ConstructorProperties` (from any package) then this annotation will be used to get the names of the parameters. +When the constructor has an annotation _named_ `@ConstructorProperties` (from any package, see <>) then this annotation will be used to get the names of the parameters. [NOTE] ==== diff --git a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc index 1aa08b17b8..b83c874c5e 100644 --- a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc +++ b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc @@ -44,4 +44,6 @@ include::chapter-11-reusing-mapping-configurations.asciidoc[] include::chapter-12-customizing-mapping.asciidoc[] -include::chapter-13-using-mapstruct-spi.asciidoc[] \ No newline at end of file +include::chapter-13-using-mapstruct-spi.asciidoc[] + +include::chapter-14-third-party-api-integration.asciidoc[] \ No newline at end of file From 75f963adf66461b246f3fad8b3bc46f94f42d7d0 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 2 Nov 2020 22:26:17 +0100 Subject: [PATCH 123/597] #2263 Fix IndexOutOfBoundsException when resolving TypeVar to a Type --- .../ap/internal/model/common/Type.java | 7 +++- .../test/bugs/_2263/Erroneous2263Mapper.java | 17 ++++++++ .../ap/test/bugs/_2263/Issue2263Test.java | 41 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2263/Source.java | 22 ++++++++++ .../mapstruct/ap/test/bugs/_2263/Target.java | 22 ++++++++++ 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Erroneous2263Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Issue2263Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Target.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 910af60d40..b3d01f4131 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -1159,7 +1159,12 @@ public Type visitTypeVariable(TypeVariable t, Type parameterized) { @Override public Type visitDeclared(DeclaredType t, Type parameterized) { if ( types.isAssignable( types.erasure( t ), types.erasure( parameterized.getTypeMirror() ) ) ) { - // if same type, we can cast en assume number of type args are also the same + // We can't assume that the type args are the same + // e.g. List is assignable to Object + if ( t.getTypeArguments().size() != parameterized.getTypeParameters().size() ) { + return super.visitDeclared( t, parameterized ); + } + for ( int i = 0; i < t.getTypeArguments().size(); i++ ) { Type result = visit( t.getTypeArguments().get( i ), parameterized.getTypeParameters().get( i ) ); if ( result != null ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Erroneous2263Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Erroneous2263Mapper.java new file mode 100644 index 0000000000..59a0a42ddb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Erroneous2263Mapper.java @@ -0,0 +1,17 @@ +/* + * 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._2263; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Erroneous2263Mapper { + + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Issue2263Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Issue2263Test.java new file mode 100644 index 0000000000..03e545b210 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Issue2263Test.java @@ -0,0 +1,41 @@ +/* + * 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._2263; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2263") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Erroneous2263Mapper.class, + Source.class, + Target.class, +}) +public class Issue2263Test { + + @Test + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = Erroneous2263Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 16, + message = "Can't map property \"Object value\" to \"String value\". " + + "Consider to declare/implement a mapping method: \"String map(Object value)\".") + }) + public void shouldCorrectlyReportUnmappableTargetObject() { + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Source.java new file mode 100644 index 0000000000..54e5b7fd38 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Source.java @@ -0,0 +1,22 @@ +/* + * 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._2263; + +/** + * @author Filip Hrisafov + */ +public class Source { + + private final Object value; + + public Source(Object value) { + this.value = value; + } + + public Object getValue() { + return value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Target.java new file mode 100644 index 0000000000..943a51264c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Target.java @@ -0,0 +1,22 @@ +/* + * 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._2263; + +/** + * @author Filip Hrisafov + */ +public class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} From 6df9243d92f66490ceccdc6df40ffe41def2e217 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 6 Nov 2020 21:24:52 +0100 Subject: [PATCH 124/597] #2253 remove unmapped source properties when source parameter is directly mapped --- .../ap/internal/model/BeanMappingMethod.java | 10 ++++ .../ap/test/bugs/_2253/Issue2253Test.java | 34 +++++++++++++ .../ap/test/bugs/_2253/TestMapper.java | 48 +++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/Issue2253Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/TestMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index b17e060848..d19fb4f398 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1315,6 +1315,16 @@ private void applyParameterNameBasedMapping() { sourceParameters.remove(); unprocessedDefinedTargets.remove( targetProperty.getKey() ); unprocessedSourceProperties.remove( targetProperty.getKey() ); + + // The source parameter was directly mapped so ignore all of its source properties completely + if ( !sourceParameter.getType().isPrimitive() && !sourceParameter.getType().isArrayType() ) { + // We explicitly ignore source properties from primitives or array types + Map readAccessors = sourceParameter.getType().getPropertyReadAccessors(); + for ( String sourceProperty : readAccessors.keySet() ) { + unprocessedSourceProperties.remove( sourceProperty ); + } + } + unprocessedConstructorProperties.remove( targetProperty.getKey() ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/Issue2253Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/Issue2253Test.java new file mode 100644 index 0000000000..fc0e007daa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/Issue2253Test.java @@ -0,0 +1,34 @@ +/* + * 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._2253; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2253") +@RunWith( AnnotationProcessorTestRunner.class ) +@WithClasses( { + TestMapper.class, +} ) +public class Issue2253Test { + + @Test + public void shouldNotTreatMatchedSourceParameterAsBean() { + + TestMapper.Person person = TestMapper.INSTANCE.map( new TestMapper.PersonDto( 20 ), "Tester" ); + + assertThat( person.getAge() ).isEqualTo( 20 ); + assertThat( person.getName() ).isEqualTo( "Tester" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/TestMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/TestMapper.java new file mode 100644 index 0000000000..1e05e444a2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/TestMapper.java @@ -0,0 +1,48 @@ +/* + * 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._2253; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface TestMapper { + + TestMapper INSTANCE = Mappers.getMapper( TestMapper.class ); + + Person map(PersonDto personDto, String name); + + class Person { + private final int age; + private final String name; + + public Person(int age, String name) { + this.age = age; + this.name = name; + } + + public int getAge() { + return age; + } + + public String getName() { + return name; + } + } + + class PersonDto { + private final int age; + + public PersonDto(int age) { + this.age = age; + } + + public int getAge() { + return age; + } + } +} From 6daea86a1b367e061f1968ece4329cc4aa8bd67d Mon Sep 17 00:00:00 2001 From: Nikolas Charalambidis Date: Wed, 11 Nov 2020 22:28:53 +0100 Subject: [PATCH 125/597] Add Lombok subsection in the documentation (#2266) --- ...er-14-third-party-api-integration.asciidoc | 151 +++++++++++++++++- .../chapter-3-defining-a-mapper.asciidoc | 3 +- 2 files changed, 152 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc index 03e18167e3..c31424a0de 100644 --- a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc +++ b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc @@ -38,4 +38,153 @@ public @interface Default { } ---- -==== \ No newline at end of file +==== + +[[lombok]] +=== Lombok + +MapStruct works together with https://projectlombok.org/[Project Lombok] as of MapStruct 1.2.0.Beta1 and Lombok 1.16.14. + +MapStruct takes advantage of generated getters, setters, and constructors and uses them to generate the mapper implementations. + +[NOTE] +==== +Lombok 1.18.16 introduces a breaking change (https://projectlombok.org/changelog[changelog]). +The additional annotation processor `lombok-mapstruct-binding` (https://mvnrepository.com/artifact/org.projectlombok/lombok-mapstruct-binding[Maven]) must be added otherwise MapStruct stops working with Lombok. +This resolves the compilation issues of Lombok and MapStruct modules. + +[source, xml] +---- + + org.projectlombok + lombok-mapstruct-binding + 0.1.0 + +---- +==== + +==== Set up + +The set up using Maven or Gradle does not differ from what is described in <>. Additionally, you need to provide Lombok dependencies. + +.Maven configuration +==== +[source, xml, linenums] +[subs="verbatim,attributes"] +---- + + + {mapstructVersion} + 1.18.16 + 1.8 + 1.8 + + + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + + + org.projectlombok + lombok + ${org.projectlombok.version} + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + org.projectlombok + lombok + ${org.projectlombok.version} + + + + + org.projectlombok + lombok-mapstruct-binding + 0.1.0 + + + + + + +---- +==== + +.Gradle configuration (3.4 and later) +==== +[source, groovy, linenums] +[subs="verbatim,attributes"] +---- + +dependencies { + + implementation "org.mapstruct:mapstruct:${mapstructVersion}" + implementation "org.projectlombok:lombok:1.18.16" + annotationProcessor "org.projectlombok:lombok-mapstruct-binding:0.1.0" + annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" + annotationProcessor "org.projectlombok:lombok:1.18.16" +} + +---- +==== + +The usage combines what you already know from <> and Lombok. + +.Usage of MapStruct with Lombok +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Data +public class Source { + + private String test; +} + +public class Target { + + private Long testing; + + public Long getTesting() { + return testing; + } + + public void setTesting( Long testing ) { + this.testing = testing; + } +} + +@Mapper +public interface SourceTargetMapper { + + SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class ); + + @Mapping( source = "test", target = "testing" ) + Target toTarget( Source s ); +} + +---- +==== + +A working example can be found on the GitHub project https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-lombok[mapstruct-lombok]. diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index acdfd98174..be3435b541 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -516,7 +516,8 @@ public class PersonMapperImpl implements PersonMapper { Supported builder frameworks: -* https://projectlombok.org/[Lombok] - requires having the Lombok classes in a separate module. See for more information https://github.com/rzwitserloot/lombok/issues/1538[rzwitserloot/lombok#1538] +* https://projectlombok.org/[Lombok] - It is required to have the Lombok classes in a separate module. +See for more information at https://github.com/rzwitserloot/lombok/issues/1538[rzwitserloot/lombok#1538] and to set up Lombok with MapStruct, refer to <>. * https://github.com/google/auto/blob/master/value/userguide/index.md[AutoValue] * https://immutables.github.io/[Immutables] - When Immutables are present on the annotation processor path then the `ImmutablesAccessorNamingStrategy` and `ImmutablesBuilderProvider` would be used by default * https://github.com/google/FreeBuilder[FreeBuilder] - When FreeBuilder is present on the annotation processor path then the `FreeBuilderAccessorNamingStrategy` would be used by default. From 84c3bda5a23e48956ad5de4955792ef0a278638e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 22 Nov 2020 13:04:26 +0100 Subject: [PATCH 126/597] #2274, #2023 Fix problems with property mapping using source parameters Fixes problems when property mapping is using source parameter and has default value / expression or is doing an update --- .../ap/internal/model/BeanMappingMethod.java | 39 ++++++++++++- .../ap/internal/model/PropertyMapping.java | 53 +++++++++++++++--- .../model/assignment/SetterWrapper.java | 30 ---------- .../ap/internal/model/BeanMappingMethod.ftl | 8 +-- .../ap/test/bugs/_2023/Issue2023Mapper.java | 24 ++++++++ .../ap/test/bugs/_2023/Issue2023Test.java | 51 +++++++++++++++++ .../ap/test/bugs/_2023/NewPersonRequest.java | 42 ++++++++++++++ .../ap/test/bugs/_2023/PersonDto.java | 31 ++++++++++ .../ManySourceArgumentsConstructorMapper.java | 25 +++++++++ ...SourceArgumentsConstructorMappingTest.java | 40 +++++++++++++ .../manysourcearguments/ExampleMapper.java | 19 +++++++ .../manysourcearguments/ExampleMember.java | 19 +++++++ .../manysourcearguments/ExampleSource.java | 21 +++++++ .../manysourcearguments/ExampleTarget.java | 31 ++++++++++ .../ManyArgumentsUpdateMappingTest.java | 56 +++++++++++++++++++ 15 files changed, 446 insertions(+), 43 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/NewPersonRequest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/PersonDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMappingTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleMember.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleTarget.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ManyArgumentsUpdateMappingTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index d19fb4f398..7ea17bf27b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1619,12 +1619,47 @@ public List getSourceParametersExcludingPrimitives() { .collect( Collectors.toList() ); } - public List getSourcePrimitiveParameters() { + public List getSourceParametersNeedingNullCheck() { return getSourceParameters().stream() - .filter( parameter -> parameter.getType().isPrimitive() ) + .filter( this::needsNullCheck ) .collect( Collectors.toList() ); } + public List getSourceParametersNotNeedingNullCheck() { + return getSourceParameters().stream() + .filter( parameter -> !needsNullCheck( parameter ) ) + .collect( Collectors.toList() ); + } + + private boolean needsNullCheck(Parameter parameter) { + if ( parameter.getType().isPrimitive() ) { + return false; + } + + List mappings = propertyMappingsByParameter( parameter ); + if ( mappings.size() == 1 && doesNotNeedNullCheckForSourceParameter( mappings.get( 0 ) ) ) { + return false; + } + + mappings = constructorPropertyMappingsByParameter( parameter ); + + if ( mappings.size() == 1 && doesNotNeedNullCheckForSourceParameter( mappings.get( 0 ) ) ) { + return false; + } + + return true; + } + + private boolean doesNotNeedNullCheckForSourceParameter(PropertyMapping mapping) { + if ( mapping.getAssignment().isCallingUpdateMethod() ) { + // If the mapping assignment is calling an update method then we should do a null check + // in the bean mapping + return false; + } + + return mapping.getAssignment().isSourceReferenceParameter(); + } + @Override public int hashCode() { //Needed for Checkstyle, otherwise it fails due to EqualsHashCode rule diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index dbda3daac3..61e1fca864 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -46,6 +46,8 @@ import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; +import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping; import static org.mapstruct.ap.internal.model.ForgedMethod.forParameterMapping; import static org.mapstruct.ap.internal.model.ForgedMethod.forPropertyMapping; @@ -374,11 +376,6 @@ private Assignment getDefaultValueAssignment( Assignment rhs ) { return null; } - private boolean hasDefaultValueAssignment(Assignment rhs) { - return ( defaultValue != null || defaultJavaExpression != null ) && - ( !rhs.getSourceType().isPrimitive() || rhs.getSourcePresenceCheckerReference() != null); - } - private Assignment assignToPlain(Type targetType, AccessorType targetAccessorType, Assignment rightHandSide) { @@ -421,8 +418,7 @@ private Assignment assignToPlainViaSetter(Type targetType, Assignment rhs) { } else { // If the property mapping has a default value assignment then we have to do a null value check - boolean includeSourceNullCheck = SetterWrapper.doSourceNullCheck( rhs, nvcs, nvpms, targetType ) - || hasDefaultValueAssignment( rhs ); + boolean includeSourceNullCheck = setterWrapperNeedsSourceNullCheck( rhs, 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. @@ -438,6 +434,49 @@ private Assignment assignToPlainViaSetter(Type targetType, Assignment rhs) { } } + /** + * Checks whether the setter wrapper should include a null / presence check or not + * + * @param rhs the source right hand side + * @param targetType the target type + * + * @return whether to include a null / presence check or not + */ + private boolean setterWrapperNeedsSourceNullCheck(Assignment rhs, Type targetType) { + if ( rhs.getSourceType().isPrimitive() && rhs.getSourcePresenceCheckerReference() == null ) { + // If the source type is primitive or it doesn't have a presence checker then + // we shouldn't do a null check + return false; + } + + if ( nvcs == ALWAYS ) { + // NullValueCheckStrategy is ALWAYS -> do a null check + return true; + } + + if ( nvpms == SET_TO_DEFAULT || nvpms == IGNORE ) { + // NullValuePropertyMapping is SET_TO_DEFAULT or IGNORE -> do a null check + return true; + } + + if ( rhs.getType().isConverted() ) { + // A type conversion is applied, so a null check is required + return true; + } + + if ( rhs.getType().isDirect() && targetType.isPrimitive() ) { + // If the type is direct and the target type is primitive (i.e. we are unboxing) then check is needed + return true; + } + + if ( defaultValue != null || defaultJavaExpression != null ) { + // If there is default value defined then a check is needed + return true; + } + + return false; + } + private Assignment assignToPlainViaAdder( Assignment rightHandSide) { Assignment result = rightHandSide; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java index e9fc38741a..7be948c857 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java @@ -10,12 +10,6 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; -import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; - -import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; -import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; -import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; /** * Wraps the assignment in a target setter. @@ -77,28 +71,4 @@ 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 - * - * @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, NullValueCheckStrategyGem nvcs, - NullValuePropertyMappingStrategyGem nvpms, Type targetType) { - return !rhs.isSourceReferenceParameter() - && !rhs.getSourceType().isPrimitive() - && (ALWAYS == nvcs - || SET_TO_DEFAULT == nvpms || IGNORE == nvpms - || rhs.getType().isConverted() - || (rhs.getType().isDirect() && targetType.isPrimitive())); - } } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index c58f9e7581..f5d6e799e1 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -27,7 +27,7 @@ <#if !existingInstanceMapping> <#if hasConstructorMappings()> <#if (sourceParameters?size > 1)> - <#list sourceParametersExcludingPrimitives as sourceParam> + <#list sourceParametersNeedingNullCheck as sourceParam> <#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)> <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; @@ -39,7 +39,7 @@ } - <#list sourcePrimitiveParameters as sourceParam> + <#list sourceParametersNotNeedingNullCheck as sourceParam> <#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)> <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; @@ -80,7 +80,7 @@ <#if (sourceParameters?size > 1)> - <#list sourceParametersExcludingPrimitives as sourceParam> + <#list sourceParametersNeedingNullCheck as sourceParam> <#if (propertyMappingsByParameter(sourceParam)?size > 0)> if ( ${sourceParam.name} != null ) { <#list propertyMappingsByParameter(sourceParam) as propertyMapping> @@ -89,7 +89,7 @@ } - <#list sourcePrimitiveParameters as sourceParam> + <#list sourceParametersNotNeedingNullCheck as sourceParam> <#if (propertyMappingsByParameter(sourceParam)?size > 0)> <#list propertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping targetBeanName=resultName existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Mapper.java new file mode 100644 index 0000000000..a2fafb7a2d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Mapper.java @@ -0,0 +1,24 @@ +/* + * 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._2023; + +import java.util.UUID; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2023Mapper { + + Issue2023Mapper INSTANCE = Mappers.getMapper( Issue2023Mapper.class ); + + @Mapping(target = "correlationId", source = "correlationId", defaultExpression = "java(UUID.randomUUID())") + NewPersonRequest createRequest(PersonDto person, UUID correlationId); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Test.java new file mode 100644 index 0000000000..ffc3df5e07 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Test.java @@ -0,0 +1,51 @@ +/* + * 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._2023; + +import java.util.UUID; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2023") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2023Mapper.class, + NewPersonRequest.class, + PersonDto.class, +}) +public class Issue2023Test { + + @Test + public void shouldUseDefaultExpressionCorrectly() { + PersonDto person = new PersonDto(); + person.setName( "John" ); + person.setEmail( "john@doe.com" ); + + NewPersonRequest request = Issue2023Mapper.INSTANCE.createRequest( person, null ); + + assertThat( request ).isNotNull(); + assertThat( request.getName() ).isEqualTo( "John" ); + assertThat( request.getEmail() ).isEqualTo( "john@doe.com" ); + assertThat( request.getCorrelationId() ).isNotNull(); + + UUID correlationId = UUID.randomUUID(); + request = Issue2023Mapper.INSTANCE.createRequest( person, correlationId ); + + assertThat( request ).isNotNull(); + assertThat( request.getName() ).isEqualTo( "John" ); + assertThat( request.getEmail() ).isEqualTo( "john@doe.com" ); + assertThat( request.getCorrelationId() ).isEqualTo( correlationId ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/NewPersonRequest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/NewPersonRequest.java new file mode 100644 index 0000000000..98f3d0b5b6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/NewPersonRequest.java @@ -0,0 +1,42 @@ +/* + * 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._2023; + +import java.util.UUID; + +/** + * @author Filip Hrisafov + */ +public class NewPersonRequest { + + private String name; + private String email; + private UUID correlationId; + + 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 UUID getCorrelationId() { + return correlationId; + } + + public void setCorrelationId(UUID correlationId) { + this.correlationId = correlationId; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/PersonDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/PersonDto.java new file mode 100644 index 0000000000..d2551c50c6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/PersonDto.java @@ -0,0 +1,31 @@ +/* + * 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._2023; + +/** + * @author Filip Hrisafov + */ +public class PersonDto { + + private String name; + private String email; + + 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; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMapper.java new file mode 100644 index 0000000000..dfa3b4be5c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMapper.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.ap.test.constructor.manysourcearguments; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.ap.test.constructor.Person; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface ManySourceArgumentsConstructorMapper { + + ManySourceArgumentsConstructorMapper INSTANCE = Mappers.getMapper( ManySourceArgumentsConstructorMapper.class ); + + @Mapping(target = "name", defaultValue = "Tester") + @Mapping(target = "city", defaultExpression = "java(\"Zurich\")") + Person map(String name, String city); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMappingTest.java new file mode 100644 index 0000000000..dde307adf7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMappingTest.java @@ -0,0 +1,40 @@ +/* + * 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.constructor.manysourcearguments; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.constructor.Person; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@RunWith( AnnotationProcessorTestRunner.class ) +@WithClasses( { + ManySourceArgumentsConstructorMapper.class, + Person.class, +} ) +public class ManySourceArgumentsConstructorMappingTest { + + @Test + public void shouldCorrectlyUseDefaultValueForSourceParameters() { + Person person = ManySourceArgumentsConstructorMapper.INSTANCE.map( null, "Test Valley" ); + + assertThat( person ).isNotNull(); + assertThat( person.getName() ).isEqualTo( "Tester" ); + assertThat( person.getCity() ).isEqualTo( "Test Valley" ); + + person = ManySourceArgumentsConstructorMapper.INSTANCE.map( "Other Tester", null ); + + assertThat( person ).isNotNull(); + assertThat( person.getName() ).isEqualTo( "Other Tester" ); + assertThat( person.getCity() ).isEqualTo( "Zurich" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleMapper.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleMapper.java new file mode 100644 index 0000000000..dbbceeae6e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleMapper.java @@ -0,0 +1,19 @@ +/* + * 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.updatemethods.manysourcearguments; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ExampleMapper { + + ExampleMapper INSTANCE = Mappers.getMapper( ExampleMapper.class ); + + void update(@MappingTarget ExampleTarget target, ExampleSource source, ExampleMember member); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleMember.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleMember.java new file mode 100644 index 0000000000..7a4e1a9916 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleMember.java @@ -0,0 +1,19 @@ +/* + * 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.updatemethods.manysourcearguments; + +public class ExampleMember { + + private final String name; + + public ExampleMember(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleSource.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleSource.java new file mode 100644 index 0000000000..2e23ed4add --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleSource.java @@ -0,0 +1,21 @@ +/* + * 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.updatemethods.manysourcearguments; + +import java.time.LocalDate; + +public class ExampleSource { + + private final LocalDate birthday; + + public ExampleSource(LocalDate birthday) { + this.birthday = birthday; + } + + public LocalDate getBirthday() { + return birthday; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleTarget.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleTarget.java new file mode 100644 index 0000000000..142503b6de --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ExampleTarget.java @@ -0,0 +1,31 @@ +/* + * 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.updatemethods.manysourcearguments; + +import java.time.LocalDate; + +public class ExampleTarget { + + private LocalDate birthday; + + private ExampleMember member; + + public LocalDate getBirthday() { + return birthday; + } + + public void setBirthday(LocalDate birthday) { + this.birthday = birthday; + } + + public ExampleMember getMember() { + return member; + } + + public void setMember(ExampleMember member) { + this.member = member; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ManyArgumentsUpdateMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ManyArgumentsUpdateMappingTest.java new file mode 100644 index 0000000000..8a6ac225a6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ManyArgumentsUpdateMappingTest.java @@ -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.updatemethods.manysourcearguments; + +import java.time.LocalDate; +import java.time.Month; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2274") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + ExampleMapper.class, + ExampleMember.class, + ExampleSource.class, + ExampleTarget.class, +}) +public class ManyArgumentsUpdateMappingTest { + + @Test + public void shouldUpdateToNullIfSourceParametersAreNull() { + ExampleTarget target = new ExampleTarget(); + target.setBirthday( LocalDate.of( 2020, Month.NOVEMBER, 15 ) ); + target.setMember( new ExampleMember( "test" ) ); + ExampleMapper.INSTANCE.update( + target, + new ExampleSource( LocalDate.of( 2020, Month.AUGUST, 30 ) ), + null + ); + + assertThat( target.getBirthday() ).isEqualTo( LocalDate.of( 2020, Month.AUGUST, 30 ) ); + assertThat( target.getMember() ).isNull(); + + ExampleMapper.INSTANCE.update( + target, + new ExampleSource( null ), + new ExampleMember( "second test" ) + ); + + assertThat( target.getBirthday() ).isNull(); + assertThat( target.getMember() ).isNotNull(); + assertThat( target.getMember().getName() ).isEqualTo( "second test" ); + } +} From e73dd1b485ecc535ae338492eb052d15758563cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20D=C3=BCsterhus?= Date: Mon, 14 Dec 2020 11:44:04 +0100 Subject: [PATCH 127/597] Update chapter-2-set-up.asciidoc See official maven doc on this: https://maven.apache.org/plugins/maven-compiler-plugin/examples/pass-compiler-arguments.html Otherwise for example intellij doesn't recognize the compiler options on maven import --- .../src/main/asciidoc/chapter-2-set-up.asciidoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index fb954ac58f..cb0067f2f3 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -187,15 +187,15 @@ When invoking javac directly, these options are passed to the compiler in the fo true - + -Amapstruct.suppressGeneratorTimestamp=true - - + + -Amapstruct.suppressGeneratorVersionInfoComment=true - - + + -Amapstruct.verbose=true - + From f84f756a4cc142c4cb4bdce540f009b79da03f23 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 5 Dec 2020 10:29:48 +0100 Subject: [PATCH 128/597] #2293 Use MapperConfig instead of MappingConfig in the documentation --- .../chapter-10-advanced-mapping-options.asciidoc | 12 ++++++------ .../chapter-5-data-type-conversions.asciidoc | 2 +- .../ap/internal/model/source/DefaultOptions.java | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index ddf7fbcaed..4ccad69f2c 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -161,13 +161,13 @@ The mechanism is also present on iterable mapping and map mapping. `@IterableMap MapStruct offers control over the object to create when the source argument of the mapping method equals `null`. By default `null` will be returned. -However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT` on `@BeanMapping`, `@IterableMapping`, `@MapMapping`, or globally on `@Mapper` or `@MappingConfig`, the mapping result can be altered to return empty *default* values. This means for: +However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT` on `@BeanMapping`, `@IterableMapping`, `@MapMapping`, or globally on `@Mapper` or `@MapperConfig`, the mapping result can be altered to return empty *default* values. This means for: * *Bean mappings*: an 'empty' target bean will be returned, with the exception of constants and expressions, they will be populated when present. * *Iterables / Arrays*: an empty iterable will be returned. * *Maps*: an empty map will be returned. -The strategy works in a hierarchical fashion. Setting `nullValueMappingStrategy` on mapping method level will override `@Mapper#nullValueMappingStrategy`, and `@Mapper#nullValueMappingStrategy` will override `@MappingConfig#nullValueMappingStrategy`. +The strategy works in a hierarchical fashion. Setting `nullValueMappingStrategy` on mapping method level will override `@Mapper#nullValueMappingStrategy`, and `@Mapper#nullValueMappingStrategy` will override `@MapperConfig#nullValueMappingStrategy`. [[mapping-result-for-null-properties]] @@ -179,13 +179,13 @@ By default the target 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. +1. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MapperConfig`, the mapping result can be altered to return *default* values. For `List` MapStruct generates an `ArrayList`, for `Map` a `HashMap`, for arrays an empty array, for `String` `""` and for primitive / boxed types a representation of `false` or `0`. For all other objects an new instance is created. Please note that a default constructor is required. If not available, use the `@Mapping#defaultValue`. -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. +2. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MapperConfig`, the mapping result will be equal to the original value of the `@MappingTarget` annotated target. -The strategy works in a hierarchical fashion. Setting `nullValuePropertyMappingStrategy` on mapping method level will override `@Mapper#nullValuePropertyMappingStrategy`, and `@Mapper#nullValuePropertyMappingStrategy` will override `@MappingConfig#nullValuePropertyMappingStrategy`. +The strategy works in a hierarchical fashion. Setting `nullValuePropertyMappingStrategy` on mapping method level will override `@Mapper#nullValuePropertyMappingStrategy`, and `@Mapper#nullValuePropertyMappingStrategy` will override `@MapperConfig#nullValuePropertyMappingStrategy`. [NOTE] ==== @@ -213,7 +213,7 @@ First calling a mapping method on the source property is not protected by a null The option `nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS` will always include a null check when source is non primitive, unless a source presence checker is defined on the source bean. -The strategy works in a hierarchical fashion. `@Mapping#nullValueCheckStrategy` will override `@BeanMapping#nullValueCheckStrategy`, `@BeanMapping#nullValueCheckStrategy` will override `@Mapper#nullValueCheckStrategy` and `@Mapper#nullValueCheckStrategy` will override `@MappingConfig#nullValueCheckStrategy`. +The strategy works in a hierarchical fashion. `@Mapping#nullValueCheckStrategy` will override `@BeanMapping#nullValueCheckStrategy`, `@BeanMapping#nullValueCheckStrategy` will override `@Mapper#nullValueCheckStrategy` and `@Mapper#nullValueCheckStrategy` will override `@MaperConfig#nullValueCheckStrategy`. [[source-presence-check]] === Source presence checking diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 725c69bad0..9619d0fe26 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -155,7 +155,7 @@ When generating the implementation of a mapping method, MapStruct will apply the . If no such method was found MapStruct will try to generate an automatic sub-mapping method that will do the mapping between the source and target attributes. . If MapStruct could not create a name based mapping method an error will be raised at build time, indicating the non-mappable attribute and its path. -A mapping control (`MappingControl`) can be defined on all levels (`@MappingConfig`, `@Mapper`, `@BeanMapping`, `@Mapping`), the latter taking precedence over the former. For example: `@Mapper( mappingControl = NoComplexMapping.class )` takes precedence over `@MapperConfig( mappingControl = DeepClone.class )`. `@IterableMapping` and `@MapMapping` work similar as `@Mapping`. MappingControl is experimental from MapStruct 1.4. +A mapping control (`MappingControl`) can be defined on all levels (`@MapperConfig`, `@Mapper`, `@BeanMapping`, `@Mapping`), the latter taking precedence over the former. For example: `@Mapper( mappingControl = NoComplexMapping.class )` takes precedence over `@MapperConfig( mappingControl = DeepClone.class )`. `@IterableMapping` and `@MapMapping` work similar as `@Mapping`. MappingControl is experimental from MapStruct 1.4. `MappingControl` has an enum that corresponds to the first 4 options above: `MappingControl.Use#DIRECT`, `MappingControl.Use#MAPPING_METHOD`, `MappingControl.Use#BUILT_IN_CONVERSION` and `MappingControl.Use#COMPLEX_MAPPING` the presence of which allows the user to switch *on* a option. The absence of an enum switches *off* a mapping option. Default they are all present enabling all mapping options. [NOTE] diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index 4d80bf57be..58f03ad55d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -119,7 +119,7 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { public BuilderGem getBuilder() { // TODO: I realized this is not correct, however it needs to be null in order to keep downward compatibility // but assuming a default @Builder will make testcases fail. Not having a default means that you need to - // specify this mandatory on @MappingConfig and @Mapper. + // specify this mandatory on @MapperConfig and @Mapper. return null; } From 700293f0898fb5e9014d0e241bc9674695430ebe Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 19 Dec 2020 19:50:32 +0100 Subject: [PATCH 129/597] #2301 Implicitly ignore forward inherited mappings from different method types --- .../ap/internal/model/BeanMappingMethod.java | 9 +++ .../ap/test/bugs/_2301/Artifact.java | 66 +++++++++++++++++++ .../ap/test/bugs/_2301/ArtifactDto.java | 22 +++++++ .../ap/test/bugs/_2301/Issue2301Mapper.java | 28 ++++++++ .../ap/test/bugs/_2301/Issue2301Test.java | 40 +++++++++++ 5 files changed, 165 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Artifact.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/ArtifactDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 7ea17bf27b..459a4f82d9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -938,6 +938,15 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTyp if ( targetWriteAccessor == null ) { if ( targetReadAccessor == null ) { + if ( mapping.getInheritContext() != null && mapping.getInheritContext().isForwarded() && + mapping.getInheritContext().getTemplateMethod().isUpdateMethod() != method.isUpdateMethod() ) { + // When a configuration is inherited and the template method is not same type as the current + // method then we can safely ignore this mapping. + // This means that a property which is inherited might be present for a direct mapping + // via the Builder, but not for an update mapping (directly on the object itself), + // or vice versa + return false; + } Set readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet(); String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Artifact.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Artifact.java new file mode 100644 index 0000000000..316959f48b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Artifact.java @@ -0,0 +1,66 @@ +/* + * 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._2301; + +import java.util.Set; + +/** + * @author Filip Hrisafov + */ +public class Artifact { + + private String name; + private Set dependantBuildRecords; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getDependantBuildRecords() { + return dependantBuildRecords; + } + + public void setDependantBuildRecords(Set dependantBuildRecords) { + this.dependantBuildRecords = dependantBuildRecords; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String name; + private Set dependantBuildRecords; + + public Artifact build() { + Artifact artifact = new Artifact(); + artifact.setName( name ); + artifact.setDependantBuildRecords( dependantBuildRecords ); + + return artifact; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder dependantBuildRecord(String dependantBuildRecord) { + this.dependantBuildRecords.add( dependantBuildRecord ); + return this; + } + + public Builder dependantBuildRecords(Set dependantBuildRecords) { + this.dependantBuildRecords = dependantBuildRecords; + return this; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/ArtifactDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/ArtifactDto.java new file mode 100644 index 0000000000..68b9280930 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/ArtifactDto.java @@ -0,0 +1,22 @@ +/* + * 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._2301; + +/** + * @author Filip Hrisafov + */ +public class ArtifactDto { + + private final String name; + + public ArtifactDto(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Mapper.java new file mode 100644 index 0000000000..cd3cca3744 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Mapper.java @@ -0,0 +1,28 @@ +/* + * 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._2301; + +import org.mapstruct.InheritConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2301Mapper { + + Issue2301Mapper INSTANCE = Mappers.getMapper( Issue2301Mapper.class ); + + @Mapping(target = "dependantBuildRecords", ignore = true) + @Mapping(target = "dependantBuildRecord", ignore = true) + Artifact map(ArtifactDto dto); + + @InheritConfiguration + void update(@MappingTarget Artifact artifact, ArtifactDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Test.java new file mode 100644 index 0000000000..8c237a2d30 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Test.java @@ -0,0 +1,40 @@ +/* + * 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._2301; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2301") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Artifact.class, + ArtifactDto.class, + Issue2301Mapper.class +}) +public class Issue2301Test { + + @Test + public void shouldCorrectlyIgnoreProperties() { + Artifact artifact = Issue2301Mapper.INSTANCE.map( new ArtifactDto( "mapstruct" ) ); + + assertThat( artifact ).isNotNull(); + assertThat( artifact.getName() ).isEqualTo( "mapstruct" ); + assertThat( artifact.getDependantBuildRecords() ).isNull(); + + Issue2301Mapper.INSTANCE.update( artifact, new ArtifactDto( "mapstruct-processor" ) ); + + assertThat( artifact.getName() ).isEqualTo( "mapstruct-processor" ); + } +} From 4223e3ab812a496aeb31b67a63737fb648bd2ddb Mon Sep 17 00:00:00 2001 From: Tomas Poledny Date: Thu, 5 Nov 2020 14:09:30 +0100 Subject: [PATCH 130/597] #2255 Add constants for componentModel --- core/src/main/java/org/mapstruct/Mapper.java | 6 +-- .../main/java/org/mapstruct/MapperConfig.java | 2 +- .../java/org/mapstruct/MappingConstants.java | 45 +++++++++++++++++++ .../chapter-4-retrieving-a-mapper.asciidoc | 6 +-- .../chapter-5-data-type-conversions.asciidoc | 2 +- .../cdi/DecoratedSourceTargetMapper.java | 3 +- .../itest/cdi/SourceTargetMapper.java | 3 +- .../jsr330/DecoratedSourceTargetMapper.java | 3 +- .../SecondDecoratedSourceTargetMapper.java | 3 +- .../itest/jsr330/SourceTargetMapper.java | 3 +- .../spring/DecoratedSourceTargetMapper.java | 3 +- .../SecondDecoratedSourceTargetMapper.java | 3 +- .../itest/spring/SourceTargetMapper.java | 3 +- .../ap/internal/gem/MappingConstantsGem.java | 19 ++++++++ .../processor/CdiComponentProcessor.java | 3 +- .../processor/Jsr330ComponentProcessor.java | 3 +- .../processor/MapperServiceProcessor.java | 3 +- .../processor/SpringComponentProcessor.java | 3 +- .../ap/test/bugs/_1395/Issue1395Mapper.java | 4 +- .../ap/test/bugs/_880/Issue880Test.java | 5 ++- .../_880/UsesConfigFromAnnotationMapper.java | 3 +- .../test/decorator/jsr330/PersonMapper.java | 3 +- .../spring/constructor/PersonMapper.java | 3 +- .../decorator/spring/field/PersonMapper.java | 3 +- .../DestinationClassNameWithJsr330Mapper.java | 3 +- .../mapstruct/ap/test/gem/ConstantTest.java | 9 ++++ .../ap/test/imports/SourceTargetMapper.java | 3 +- ...Jsr330DefaultCompileOptionFieldMapper.java | 4 +- ...Jsr330DefaultCompileOptionFieldMapper.java | 3 +- ...rJsr330CompileOptionConstructorMapper.java | 3 +- ...rJsr330CompileOptionConstructorMapper.java | 3 +- .../constructor/ConstructorJsr330Config.java | 4 +- .../CustomerJsr330ConstructorMapper.java | 3 +- .../field/CustomerJsr330FieldMapper.java | 4 +- .../jsr330/field/FieldJsr330Config.java | 3 +- .../_default/CustomerSpringDefaultMapper.java | 3 +- .../_default/GenderSpringDefaultMapper.java | 3 +- ...dSpringCompileOptionConstructorMapper.java | 3 +- ...rSpringCompileOptionConstructorMapper.java | 3 +- ...rSpringCompileOptionConstructorMapper.java | 3 +- .../constructor/ConstructorSpringConfig.java | 4 +- ...CustomerRecordSpringConstructorMapper.java | 3 +- .../CustomerSpringConstructorMapper.java | 3 +- .../field/CustomerSpringFieldMapper.java | 4 +- .../spring/field/FieldSpringConfig.java | 3 +- .../samename/Jsr330SourceTargetMapper.java | 3 +- 46 files changed, 166 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 233907621a..5ed9eab17f 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -35,7 +35,7 @@ *

      *
      
        * // we have MarkMapper (map field "mark" to field "name" to upper case)
      - * @Mapper(componentModel = "spring")
      + * @Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
        * public class MarkMapper {
        *     public String mapMark(String mark) {
        *         return mark.toUpperCase();
      @@ -43,7 +43,7 @@
        * }
        * // we have CarMapper
        * @Mapper(
      - *      componentModel = "spring",
      + *      componentModel = MappingConstants.ComponentModel.SPRING,
        *      uses = MarkMapper.class,
        *      injectionStrategy = InjectionStrategy.CONSTRUCTOR)
        * public interface CarMapper {
      @@ -148,7 +148,7 @@
            *
            * @return The component model for the generated mapper.
            */
      -    String componentModel() default "default";
      +    String componentModel() default MappingConstants.ComponentModel.DEFAULT;
       
           /**
            * Specifies the name of the implementation class. The {@code } will be replaced by the
      diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java
      index d251ff3798..ecfd888ca2 100644
      --- a/core/src/main/java/org/mapstruct/MapperConfig.java
      +++ b/core/src/main/java/org/mapstruct/MapperConfig.java
      @@ -135,7 +135,7 @@
            *
            * @return The component model for the generated mapper.
            */
      -    String componentModel() default "default";
      +    String componentModel() default MappingConstants.ComponentModel.DEFAULT;
       
           /**
            * Specifies the name of the implementation class. The {@code } will be replaced by the
      diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java
      index 2c5757038f..6e30f08c21 100644
      --- a/core/src/main/java/org/mapstruct/MappingConstants.java
      +++ b/core/src/main/java/org/mapstruct/MappingConstants.java
      @@ -66,4 +66,49 @@ private MappingConstants() {
            */
           public static final String STRIP_PREFIX_TRANSFORMATION = "stripPrefix";
       
      +    /**
      +    * Specifies the component model constants to which the generated mapper should adhere.
      +    * It can be used with the annotation {@link Mapper#componentModel()} or {@link MapperConfig#componentModel()}
      +    *
      +    * 

      + * Example: + *

      + *
      
      +    * // Spring component model
      +    * @Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
      +    * 
      + * + * @since 1.5.0 + */ + public static final class ComponentModel { + + private ComponentModel() { + } + + /** + * The mapper uses no component model, instances are typically retrieved + * via {@link org.mapstruct.factory.Mappers#getMapper(java.lang.Class)} + * + */ + public static final String DEFAULT = "default"; + + /** + * The generated mapper is an application-scoped CDI bean and can be retrieved via @Inject + */ + public static final String CDI = "cdi"; + + /** + * The generated mapper is a Spring bean and can be retrieved via @Autowired + * + */ + public static final String SPRING = "spring"; + + /** + * The generated mapper is annotated with @javax.inject.Named and @Singleton, and can be retrieved via @Inject + * + */ + public static final String JSR330 = "jsr330"; + + } + } diff --git a/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc index 75b5b10294..724b42fbb7 100644 --- a/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc @@ -69,14 +69,14 @@ Note that mappers generated by MapStruct are stateless and thread-safe and thus If you're working with a dependency injection framework such as http://jcp.org/en/jsr/detail?id=346[CDI] (Contexts and Dependency Injection for Java^TM^ EE) or the http://www.springsource.org/spring-framework[Spring Framework], it is recommended to obtain mapper objects via dependency injection and *not* via the `Mappers` class as described above. For that purpose you can specify the component model which generated mapper classes should be based on either via `@Mapper#componentModel` or using a processor option as described in <>. -Currently there is support for CDI and Spring (the latter either via its custom annotations or using the JSR 330 annotations). See <> for the allowed values of the `componentModel` attribute which are the same as for the `mapstruct.defaultComponentModel` processor option. In both cases the required annotations will be added to the generated mapper implementations classes in order to make the same subject to dependency injection. The following shows an example using CDI: +Currently there is support for CDI and Spring (the latter either via its custom annotations or using the JSR 330 annotations). See <> for the allowed values of the `componentModel` attribute which are the same as for the `mapstruct.defaultComponentModel` processor option and constants are defined in a class `MappingConstants.ComponentModel`. In both cases the required annotations will be added to the generated mapper implementations classes in order to make the same subject to dependency injection. The following shows an example using CDI: .A mapper using the CDI component model ==== [source, java, linenums] [subs="verbatim,attributes"] ---- -@Mapper(componentModel = "cdi") +@Mapper(componentModel = MappingConstants.ComponentModel.CDI) public interface CarMapper { CarDto carToCarDto(Car car); @@ -110,7 +110,7 @@ This can be done by either providing the injection strategy via `@Mapper` or `@M [source, java, linenums] [subs="verbatim,attributes"] ---- -@Mapper(componentModel = "cdi", uses = EngineMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR) +@Mapper(componentModel = MappingConstants.ComponentModel.CDI, uses = EngineMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR) public interface CarMapper { CarDto carToCarDto(Car car); } diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 9619d0fe26..9978cd3f25 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -405,7 +405,7 @@ public class ReferenceMapper { } } -@Mapper(componentModel = "cdi", uses = ReferenceMapper.class ) +@Mapper(componentModel = MappingConstants.ComponentModel.CDI, uses = ReferenceMapper.class ) public interface CarMapper { Car carDtoToCar(CarDto carDto); diff --git a/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/DecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/DecoratedSourceTargetMapper.java index 1787fd3c90..18a57ce25e 100644 --- a/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/DecoratedSourceTargetMapper.java +++ b/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/DecoratedSourceTargetMapper.java @@ -6,10 +6,11 @@ package org.mapstruct.itest.cdi; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.DecoratedWith; import org.mapstruct.itest.cdi.other.DateMapper; -@Mapper( componentModel = "cdi", uses = DateMapper.class ) +@Mapper( componentModel = MappingConstants.ComponentModel.CDI, uses = DateMapper.class ) public interface DecoratedSourceTargetMapper { Target sourceToTarget(Source source); diff --git a/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/SourceTargetMapper.java b/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/SourceTargetMapper.java index 3a46a20889..eb895f5e5a 100644 --- a/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/SourceTargetMapper.java +++ b/integrationtest/src/test/resources/cdiTest/src/main/java/org/mapstruct/itest/cdi/SourceTargetMapper.java @@ -6,9 +6,10 @@ package org.mapstruct.itest.cdi; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.itest.cdi.other.DateMapper; -@Mapper(componentModel = "cdi", uses = DateMapper.class) +@Mapper(componentModel = MappingConstants.ComponentModel.CDI, uses = DateMapper.class) public interface SourceTargetMapper { Target sourceToTarget(Source source); diff --git a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/DecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/DecoratedSourceTargetMapper.java index 137f234c26..77f5ec558a 100644 --- a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/DecoratedSourceTargetMapper.java +++ b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/DecoratedSourceTargetMapper.java @@ -6,10 +6,11 @@ package org.mapstruct.itest.jsr330; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.DecoratedWith; import org.mapstruct.itest.jsr330.other.DateMapper; -@Mapper(componentModel = "jsr330", uses = DateMapper.class) +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330, uses = DateMapper.class) @DecoratedWith(SourceTargetMapperDecorator.class) public interface DecoratedSourceTargetMapper { diff --git a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondDecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondDecoratedSourceTargetMapper.java index cdec6dc19d..f088e21de4 100644 --- a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondDecoratedSourceTargetMapper.java +++ b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SecondDecoratedSourceTargetMapper.java @@ -6,10 +6,11 @@ package org.mapstruct.itest.jsr330; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.DecoratedWith; import org.mapstruct.itest.jsr330.other.DateMapper; -@Mapper(componentModel = "jsr330", uses = DateMapper.class) +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330, uses = DateMapper.class) @DecoratedWith(SecondSourceTargetMapperDecorator.class) public interface SecondDecoratedSourceTargetMapper { diff --git a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SourceTargetMapper.java b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SourceTargetMapper.java index c5ba3670fb..a96a860da5 100644 --- a/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SourceTargetMapper.java +++ b/integrationtest/src/test/resources/jsr330Test/src/main/java/org/mapstruct/itest/jsr330/SourceTargetMapper.java @@ -6,9 +6,10 @@ package org.mapstruct.itest.jsr330; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.itest.jsr330.other.DateMapper; -@Mapper(componentModel = "jsr330", uses = DateMapper.class) +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330, uses = DateMapper.class) public interface SourceTargetMapper { Target sourceToTarget(Source source); diff --git a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/DecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/DecoratedSourceTargetMapper.java index f1bbf87d3a..a020ab2b27 100644 --- a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/DecoratedSourceTargetMapper.java +++ b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/DecoratedSourceTargetMapper.java @@ -6,10 +6,11 @@ package org.mapstruct.itest.spring; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.DecoratedWith; import org.mapstruct.itest.spring.other.DateMapper; -@Mapper( componentModel = "spring", uses = DateMapper.class ) +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING, uses = DateMapper.class ) @DecoratedWith( SourceTargetMapperDecorator.class ) public interface DecoratedSourceTargetMapper { diff --git a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SecondDecoratedSourceTargetMapper.java b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SecondDecoratedSourceTargetMapper.java index 88e2906300..7c04262595 100644 --- a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SecondDecoratedSourceTargetMapper.java +++ b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SecondDecoratedSourceTargetMapper.java @@ -6,10 +6,11 @@ package org.mapstruct.itest.spring; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.DecoratedWith; import org.mapstruct.itest.spring.other.DateMapper; -@Mapper( componentModel = "spring", uses = DateMapper.class ) +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING, uses = DateMapper.class ) @DecoratedWith( SecondSourceTargetMapperDecorator.class ) public interface SecondDecoratedSourceTargetMapper { diff --git a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SourceTargetMapper.java b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SourceTargetMapper.java index 06e4be4d22..e6d6b46339 100644 --- a/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SourceTargetMapper.java +++ b/integrationtest/src/test/resources/springTest/src/main/java/org/mapstruct/itest/spring/SourceTargetMapper.java @@ -6,9 +6,10 @@ package org.mapstruct.itest.spring; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.itest.spring.other.DateMapper; -@Mapper(componentModel = "spring", uses = DateMapper.class) +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = DateMapper.class) public interface SourceTargetMapper { Target sourceToTarget(Source source); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java index 685f5a543c..5df7fd3f92 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java @@ -28,4 +28,23 @@ private MappingConstantsGem() { public static final String PREFIX_TRANSFORMATION = "prefix"; public static final String STRIP_PREFIX_TRANSFORMATION = "stripPrefix"; + + /** + * Gem for the class {@link org.mapstruct.MappingConstants.ComponentModel} + * + */ + public final class ComponentModelGem { + + private ComponentModelGem() { + } + + public static final String DEFAULT = "default"; + + public static final String CDI = "cdi"; + + public static final String SPRING = "spring"; + + public static final String JSR330 = "jsr330"; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/CdiComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/CdiComponentProcessor.java index a5e3458681..74ff2b118b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/CdiComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/CdiComponentProcessor.java @@ -9,6 +9,7 @@ import java.util.Collections; import java.util.List; +import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Mapper; @@ -23,7 +24,7 @@ public class CdiComponentProcessor extends AnnotationBasedComponentModelProcesso @Override protected String getComponentModelIdentifier() { - return "cdi"; + return MappingConstantsGem.ComponentModelGem.CDI; } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java index 4f7b46d643..18a6e4f69d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java @@ -9,6 +9,7 @@ import java.util.Collections; import java.util.List; +import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Mapper; @@ -23,7 +24,7 @@ public class Jsr330ComponentProcessor extends AnnotationBasedComponentModelProcessor { @Override protected String getComponentModelIdentifier() { - return "jsr330"; + return MappingConstantsGem.ComponentModelGem.JSR330; } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java index f74a84c491..c9e37596f7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java @@ -11,6 +11,7 @@ import javax.tools.FileObject; import javax.tools.StandardLocation; +import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.GeneratedType; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.ServicesEntry; @@ -38,7 +39,7 @@ public Void process(ProcessorContext context, TypeElement mapperTypeElement, Map String componentModel = MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() ).componentModel( ); - spiGenerationNeeded = "default".equals( componentModel ); + spiGenerationNeeded = MappingConstantsGem.ComponentModelGem.DEFAULT.equals( componentModel ); } if ( !context.isErroneous() && spiGenerationNeeded && mapper.hasCustomImplementation() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java index 7780501a78..f7aa9b4fea 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java @@ -10,6 +10,7 @@ import java.util.Collections; import java.util.List; +import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Mapper; @@ -24,7 +25,7 @@ public class SpringComponentProcessor extends AnnotationBasedComponentModelProcessor { @Override protected String getComponentModelIdentifier() { - return "spring"; + return MappingConstantsGem.ComponentModelGem.SPRING; } @Override diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Mapper.java index 559e39512e..ebc9ae27e0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Mapper.java @@ -7,11 +7,13 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; /** * @author Filip Hrisafov */ -@Mapper(injectionStrategy = InjectionStrategy.CONSTRUCTOR, componentModel = "spring", uses = NotUsedService.class) +@Mapper(injectionStrategy = InjectionStrategy.CONSTRUCTOR, componentModel = MappingConstants.ComponentModel.SPRING, + uses = NotUsedService.class) public interface Issue1395Mapper { Target map(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java index 2a80961bc5..4d491b1b2c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java @@ -10,6 +10,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; @@ -30,7 +31,7 @@ Poodle.class, Config.class }) @ProcessorOptions({ - @ProcessorOption(name = "mapstruct.defaultComponentModel", value = "spring"), + @ProcessorOption(name = "mapstruct.defaultComponentModel", value = MappingConstants.ComponentModel.SPRING), @ProcessorOption(name = "mapstruct.unmappedTargetPolicy", value = "ignore") }) public class Issue880Test { @Rule @@ -40,7 +41,7 @@ public class Issue880Test { @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, diagnostics = @Diagnostic(kind = Kind.WARNING, - type = UsesConfigFromAnnotationMapper.class, line = 16, + type = UsesConfigFromAnnotationMapper.class, line = 17, message = "Unmapped target property: \"core\".")) public void compilationSucceedsAndAppliesCorrectComponentModel() { generatedSource.forMapper( UsesConfigFromAnnotationMapper.class ).containsNoImportFor( Component.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/UsesConfigFromAnnotationMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/UsesConfigFromAnnotationMapper.java index ed4267cf12..d9a1efe33e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/UsesConfigFromAnnotationMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/UsesConfigFromAnnotationMapper.java @@ -6,12 +6,13 @@ package org.mapstruct.ap.test.bugs._880; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; /** * @author Andreas Gudian * */ -@Mapper(componentModel = "default", config = Config.class) +@Mapper(componentModel = MappingConstants.ComponentModel.DEFAULT, config = Config.class) public interface UsesConfigFromAnnotationMapper { Poodle metamorph(Object essence); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/PersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/PersonMapper.java index b37557bae1..770393f817 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/PersonMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/PersonMapper.java @@ -8,12 +8,13 @@ import org.mapstruct.DecoratedWith; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.decorator.Address; import org.mapstruct.ap.test.decorator.AddressDto; import org.mapstruct.ap.test.decorator.Person; import org.mapstruct.ap.test.decorator.PersonDto; -@Mapper(componentModel = "jsr330") +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330) @DecoratedWith(PersonMapperDecorator.class) public interface PersonMapper { diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/PersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/PersonMapper.java index e1e57eb6f3..4ada1124c0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/PersonMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/PersonMapper.java @@ -9,12 +9,13 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.decorator.Address; import org.mapstruct.ap.test.decorator.AddressDto; import org.mapstruct.ap.test.decorator.Person; import org.mapstruct.ap.test.decorator.PersonDto; -@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR) +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, injectionStrategy = InjectionStrategy.CONSTRUCTOR) @DecoratedWith(PersonMapperDecorator.class) public interface PersonMapper { diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/PersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/PersonMapper.java index fc03b6ce1a..5cbe867e7a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/PersonMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/PersonMapper.java @@ -9,12 +9,13 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.decorator.Address; import org.mapstruct.ap.test.decorator.AddressDto; import org.mapstruct.ap.test.decorator.Person; import org.mapstruct.ap.test.decorator.PersonDto; -@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.FIELD) +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, injectionStrategy = InjectionStrategy.FIELD) @DecoratedWith(PersonMapperDecorator.class) public interface PersonMapper { diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameWithJsr330Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameWithJsr330Mapper.java index 48d5547ff2..92c72a877d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameWithJsr330Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameWithJsr330Mapper.java @@ -6,11 +6,12 @@ package org.mapstruct.ap.test.destination; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; /** * @author Christophe Labouisse on 27/05/2015. */ -@Mapper(implementationName = "Jsr330Impl", componentModel = "jsr330") +@Mapper(implementationName = "Jsr330Impl", componentModel = MappingConstants.ComponentModel.JSR330) public interface DestinationClassNameWithJsr330Mapper { String intToString(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java index a2a6afefde..69fa56a310 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java @@ -30,4 +30,13 @@ public void constantsShouldBeEqual() { assertThat( MappingConstants.STRIP_PREFIX_TRANSFORMATION ) .isEqualTo( MappingConstantsGem.STRIP_PREFIX_TRANSFORMATION ); } + + @Test + public void componentModelContantsShouldBeEqual() { + assertThat( MappingConstants.ComponentModel.DEFAULT ) + .isEqualTo( MappingConstantsGem.ComponentModelGem.DEFAULT ); + assertThat( MappingConstants.ComponentModel.CDI ).isEqualTo( MappingConstantsGem.ComponentModelGem.CDI ); + assertThat( MappingConstants.ComponentModel.SPRING ).isEqualTo( MappingConstantsGem.ComponentModelGem.SPRING ); + assertThat( MappingConstants.ComponentModel.JSR330 ).isEqualTo( MappingConstantsGem.ComponentModelGem.JSR330 ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/imports/SourceTargetMapper.java index 8e5e7b0de2..1380ca400a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/imports/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/SourceTargetMapper.java @@ -8,6 +8,7 @@ import java.util.Date; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.imports.referenced.Source; import org.mapstruct.ap.test.imports.referenced.Target; import org.mapstruct.ap.test.imports.to.Foo; @@ -16,7 +17,7 @@ /** * @author Gunnar Morling */ -@Mapper(componentModel = "jsr330") +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330) public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/CustomerJsr330DefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/CustomerJsr330DefaultCompileOptionFieldMapper.java index 9ce6b1215e..7fcbe97d5a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/CustomerJsr330DefaultCompileOptionFieldMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/CustomerJsr330DefaultCompileOptionFieldMapper.java @@ -6,13 +6,15 @@ package org.mapstruct.ap.test.injectionstrategy.jsr330._default; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; /** * @author Andrei Arlou */ -@Mapper(componentModel = "jsr330", uses = GenderJsr330DefaultCompileOptionFieldMapper.class) +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330, + uses = GenderJsr330DefaultCompileOptionFieldMapper.class) public interface CustomerJsr330DefaultCompileOptionFieldMapper { CustomerDto asTarget(CustomerEntity customerEntity); diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/GenderJsr330DefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/GenderJsr330DefaultCompileOptionFieldMapper.java index 6701ce42fa..1ecf720cbf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/GenderJsr330DefaultCompileOptionFieldMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/GenderJsr330DefaultCompileOptionFieldMapper.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.injectionstrategy.jsr330._default; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; @@ -14,7 +15,7 @@ /** * @author Andrei Arlou */ -@Mapper(componentModel = "jsr330") +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330) public interface GenderJsr330DefaultCompileOptionFieldMapper { @ValueMappings({ diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/CustomerJsr330CompileOptionConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/CustomerJsr330CompileOptionConstructorMapper.java index 0e89ec8943..143c452886 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/CustomerJsr330CompileOptionConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/CustomerJsr330CompileOptionConstructorMapper.java @@ -7,13 +7,14 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; /** * @author Andrei Arlou */ -@Mapper( componentModel = "jsr330", +@Mapper( componentModel = MappingConstants.ComponentModel.JSR330, uses = GenderJsr330CompileOptionConstructorMapper.class ) public interface CustomerJsr330CompileOptionConstructorMapper { diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/GenderJsr330CompileOptionConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/GenderJsr330CompileOptionConstructorMapper.java index da3caed5be..bffcc06bb9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/GenderJsr330CompileOptionConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/GenderJsr330CompileOptionConstructorMapper.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.injectionstrategy.jsr330.compileoptionconstructor; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; @@ -14,7 +15,7 @@ /** * @author Andrei Arlou */ -@Mapper(componentModel = "jsr330") +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330) public interface GenderJsr330CompileOptionConstructorMapper { @ValueMappings({ diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/ConstructorJsr330Config.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/ConstructorJsr330Config.java index 3bfc7d23ae..177b70d1dc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/ConstructorJsr330Config.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/ConstructorJsr330Config.java @@ -7,10 +7,12 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.MapperConfig; +import org.mapstruct.MappingConstants; /** * @author Filip Hrisafov */ -@MapperConfig(componentModel = "jsr330", injectionStrategy = InjectionStrategy.CONSTRUCTOR) +@MapperConfig(componentModel = MappingConstants.ComponentModel.JSR330, + injectionStrategy = InjectionStrategy.CONSTRUCTOR) public interface ConstructorJsr330Config { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/CustomerJsr330ConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/CustomerJsr330ConstructorMapper.java index 001cb9f736..638e71a3aa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/CustomerJsr330ConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/CustomerJsr330ConstructorMapper.java @@ -8,13 +8,14 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; /** * @author Kevin Grüneberg */ -@Mapper( componentModel = "jsr330", +@Mapper( componentModel = MappingConstants.ComponentModel.JSR330, uses = GenderJsr330ConstructorMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR ) public interface CustomerJsr330ConstructorMapper { diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/CustomerJsr330FieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/CustomerJsr330FieldMapper.java index 5c53a91c0f..a9e02280a6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/CustomerJsr330FieldMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/CustomerJsr330FieldMapper.java @@ -7,13 +7,15 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; /** * @author Kevin Grüneberg */ -@Mapper(componentModel = "jsr330", uses = GenderJsr330FieldMapper.class, injectionStrategy = InjectionStrategy.FIELD) +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330, uses = GenderJsr330FieldMapper.class, + injectionStrategy = InjectionStrategy.FIELD) public interface CustomerJsr330FieldMapper { CustomerDto asTarget(CustomerEntity customerEntity); diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/FieldJsr330Config.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/FieldJsr330Config.java index 33b5bbe8dc..ba3938ae48 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/FieldJsr330Config.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/FieldJsr330Config.java @@ -7,10 +7,11 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.MapperConfig; +import org.mapstruct.MappingConstants; /** * @author Filip Hrisafov */ -@MapperConfig(componentModel = "jsr330", injectionStrategy = InjectionStrategy.FIELD) +@MapperConfig(componentModel = MappingConstants.ComponentModel.JSR330, injectionStrategy = InjectionStrategy.FIELD) public interface FieldJsr330Config { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/CustomerSpringDefaultMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/CustomerSpringDefaultMapper.java index 34b06fc788..c7c2243d59 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/CustomerSpringDefaultMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/CustomerSpringDefaultMapper.java @@ -6,13 +6,14 @@ package org.mapstruct.ap.test.injectionstrategy.spring._default; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; /** * @author Filip Hrisafov */ -@Mapper(componentModel = "spring", uses = GenderSpringDefaultMapper.class ) +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = GenderSpringDefaultMapper.class ) public interface CustomerSpringDefaultMapper { CustomerDto asTarget(CustomerEntity customerEntity); diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/GenderSpringDefaultMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/GenderSpringDefaultMapper.java index 106b93519e..e49a820994 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/GenderSpringDefaultMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/GenderSpringDefaultMapper.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.injectionstrategy.spring._default; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; @@ -14,7 +15,7 @@ /** * @author Filip Hrisafov */ -@Mapper(componentModel = "spring") +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) public interface GenderSpringDefaultMapper { @ValueMappings({ diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerRecordSpringCompileOptionConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerRecordSpringCompileOptionConstructorMapper.java index d7debfb96c..f411ba506f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerRecordSpringCompileOptionConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerRecordSpringCompileOptionConstructorMapper.java @@ -6,13 +6,14 @@ package org.mapstruct.ap.test.injectionstrategy.spring.compileoptionconstructor; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordEntity; /** * @author Andrei Arlou */ -@Mapper(componentModel = "spring", +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = { CustomerSpringCompileOptionConstructorMapper.class, GenderSpringCompileOptionConstructorMapper.class }, disableSubMappingMethodsGeneration = true) public interface CustomerRecordSpringCompileOptionConstructorMapper { diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerSpringCompileOptionConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerSpringCompileOptionConstructorMapper.java index db11c1b7ad..47060104f7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerSpringCompileOptionConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerSpringCompileOptionConstructorMapper.java @@ -7,13 +7,14 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; /** * @author Andrei Arlou */ -@Mapper( componentModel = "spring", +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING, uses = GenderSpringCompileOptionConstructorMapper.class) public interface CustomerSpringCompileOptionConstructorMapper { diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/GenderSpringCompileOptionConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/GenderSpringCompileOptionConstructorMapper.java index c909f1dc5c..096fe91b96 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/GenderSpringCompileOptionConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/GenderSpringCompileOptionConstructorMapper.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.injectionstrategy.spring.compileoptionconstructor; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; @@ -14,7 +15,7 @@ /** * @author Andrei Arlou */ -@Mapper(componentModel = "spring") +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) public interface GenderSpringCompileOptionConstructorMapper { @ValueMappings({ diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/ConstructorSpringConfig.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/ConstructorSpringConfig.java index f4b46e35c3..0bb89895cb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/ConstructorSpringConfig.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/ConstructorSpringConfig.java @@ -7,10 +7,12 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.MapperConfig; +import org.mapstruct.MappingConstants; /** * @author Filip Hrisafov */ -@MapperConfig(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR) +@MapperConfig(componentModel = MappingConstants.ComponentModel.SPRING, + injectionStrategy = InjectionStrategy.CONSTRUCTOR) public interface ConstructorSpringConfig { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerRecordSpringConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerRecordSpringConstructorMapper.java index a7f9e8a8b2..c2a5862978 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerRecordSpringConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerRecordSpringConstructorMapper.java @@ -7,13 +7,14 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordEntity; /** * @author Kevin Grüneberg */ -@Mapper(componentModel = "spring", +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = { CustomerSpringConstructorMapper.class, GenderSpringConstructorMapper.class }, injectionStrategy = InjectionStrategy.CONSTRUCTOR, disableSubMappingMethodsGeneration = true) diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerSpringConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerSpringConstructorMapper.java index 9a9ab0119a..4ea869ec5f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerSpringConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerSpringConstructorMapper.java @@ -8,13 +8,14 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; /** * @author Kevin Grüneberg */ -@Mapper( componentModel = "spring", +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING, uses = GenderSpringConstructorMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR ) public interface CustomerSpringConstructorMapper { diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/CustomerSpringFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/CustomerSpringFieldMapper.java index 449cc5e435..fb8a316c59 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/CustomerSpringFieldMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/CustomerSpringFieldMapper.java @@ -7,13 +7,15 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; /** * @author Kevin Grüneberg */ -@Mapper(componentModel = "spring", uses = GenderSpringFieldMapper.class, injectionStrategy = InjectionStrategy.FIELD) +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = GenderSpringFieldMapper.class, + injectionStrategy = InjectionStrategy.FIELD) public interface CustomerSpringFieldMapper { CustomerDto asTarget(CustomerEntity customerEntity); diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/FieldSpringConfig.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/FieldSpringConfig.java index d44a54c935..c9e3c3f0f7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/FieldSpringConfig.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/FieldSpringConfig.java @@ -7,10 +7,11 @@ import org.mapstruct.InjectionStrategy; import org.mapstruct.MapperConfig; +import org.mapstruct.MappingConstants; /** * @author Filip Hrisafov */ -@MapperConfig(componentModel = "spring", injectionStrategy = InjectionStrategy.FIELD) +@MapperConfig(componentModel = MappingConstants.ComponentModel.SPRING, injectionStrategy = InjectionStrategy.FIELD) public interface FieldSpringConfig { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/samename/Jsr330SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/references/samename/Jsr330SourceTargetMapper.java index 6d613b0f91..f63b49eef5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/references/samename/Jsr330SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/references/samename/Jsr330SourceTargetMapper.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.references.samename; import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.test.references.samename.a.CustomMapper; import org.mapstruct.ap.test.references.samename.model.Source; import org.mapstruct.ap.test.references.samename.model.Target; @@ -15,7 +16,7 @@ * @author Gunnar Morling */ @Mapper( - componentModel = "jsr330", + componentModel = MappingConstants.ComponentModel.JSR330, uses = { CustomMapper.class, org.mapstruct.ap.test.references.samename.b.CustomMapper.class From dfc752809604dfcff8bec9d5c1c8f679c9dcae61 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 17 Jan 2021 11:39:57 +0100 Subject: [PATCH 131/597] Small fixes for Java 16 * Upgrade japicmp-maven-plugin to 0.15.2 * Do not use deprecated for removal Long constructor * Update Spring to 5.3.3 * Upgrade Lombok to 1.18.16 + add lombok-mapstruct-binding --- integrationtest/pom.xml | 1 - .../src/test/resources/lombokBuilderTest/pom.xml | 6 ++++++ parent/pom.xml | 8 ++++---- .../mapstruct/ap/test/exceptions/ExceptionTestMapper.java | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index f19ecfe343..17a6a4a9a9 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -65,7 +65,6 @@ commons-io commons-io - 2.6 test diff --git a/integrationtest/src/test/resources/lombokBuilderTest/pom.xml b/integrationtest/src/test/resources/lombokBuilderTest/pom.xml index 92ece809fe..5d43efb1d3 100644 --- a/integrationtest/src/test/resources/lombokBuilderTest/pom.xml +++ b/integrationtest/src/test/resources/lombokBuilderTest/pom.xml @@ -25,5 +25,11 @@ lombok compile + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + compile + diff --git a/parent/pom.xml b/parent/pom.xml index 9e5c04f27a..b6a619a90b 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -25,7 +25,7 @@ 3.0.0-M3 3.0.0-M5 3.1.0 - 4.0.3.RELEASE + 5.3.3 1.6.0 8.36.1 5.7.0 @@ -202,7 +202,7 @@ org.projectlombok lombok - 1.18.10 + 1.18.16 org.immutables @@ -261,7 +261,7 @@ commons-io commons-io - 2.5 + 2.6 @@ -553,7 +553,7 @@ com.github.siom79.japicmp japicmp-maven-plugin - 0.13.1 + 0.15.2 verify diff --git a/processor/src/test/java/org/mapstruct/ap/test/exceptions/ExceptionTestMapper.java b/processor/src/test/java/org/mapstruct/ap/test/exceptions/ExceptionTestMapper.java index c856c0d3bf..13d0d1f5cf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/exceptions/ExceptionTestMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/exceptions/ExceptionTestMapper.java @@ -20,6 +20,6 @@ public Long toLong(Integer size) throws TestException1, TestException2 { else if ( size == 2 ) { throw new TestException2(); } - return new Long(size); + return Long.valueOf( size ); } } From 8478a5455b6dec8da99c1978ac00f0f2605a51c3 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 23 Jan 2021 09:15:46 +0100 Subject: [PATCH 132/597] #2278 inherited property ignored due to ignore on nested level (#2332) Co-authored-by: sjaakd --- .../model/source/MappingMethodOptions.java | 10 +- .../ap/test/bugs/_2278/Issue2278MapperA.java | 62 ++++++++++ .../ap/test/bugs/_2278/Issue2278MapperB.java | 65 ++++++++++ .../bugs/_2278/Issue2278ReferenceMapper.java | 54 ++++++++ .../ap/test/bugs/_2278/Issue2278Test.java | 95 +++++++++++++++ .../ap/test/bugs/_2318/Issue2318Mapper.java | 115 ++++++++++++++++++ .../ap/test/bugs/_2318/Issue2318Test.java | 38 ++++++ 7 files changed, 435 insertions(+), 4 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278MapperA.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278MapperB.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278ReferenceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index b7fd7f00d0..859a4c6f5f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -230,9 +230,11 @@ private void addAllNonRedefined(Set inheritedMappings) { } private boolean isRedefined(Set redefinedNames, String inheritedName ) { - for ( String redefinedName : redefinedNames ) { - if ( elementsAreContainedIn( redefinedName, inheritedName ) ) { - return true; + if ( inheritedName != null ) { + for ( String redefinedName : redefinedNames ) { + if ( elementsAreContainedIn( inheritedName, redefinedName ) ) { + return true; + } } } return false; @@ -241,7 +243,7 @@ private boolean isRedefined(Set redefinedNames, String inheritedName ) { private boolean elementsAreContainedIn( String redefinedName, String inheritedName ) { if ( inheritedName != null && redefinedName.startsWith( inheritedName ) ) { // it is possible to redefine an exact matching source name, because the same source can be mapped to - // multiple targets. It is not possible for target, but caught by the Set and equals methoded in + // multiple targets. It is not possible for target, but caught by the Set and equals method in // MappingOptions. SourceName == null also could hint at redefinition if ( redefinedName.length() > inheritedName.length() ) { // redefined.lenght() > inherited.length(), first following character should be separator diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278MapperA.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278MapperA.java new file mode 100644 index 0000000000..8527be3f89 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278MapperA.java @@ -0,0 +1,62 @@ +/* + * 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._2278; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * ReproducerA + * + */ +@Mapper +public interface Issue2278MapperA { + + Issue2278MapperA INSTANCE = Mappers.getMapper( Issue2278MapperA.class ); + + @Mapping( target = "detailsDTO", source = "details" ) + @Mapping( target = "detailsDTO.fuelType", ignore = true ) + CarDTO map(Car in); + + // checkout the Issue2278ReferenceMapper, the @InheritInverseConfiguration + // is de-facto @Mapping( target = "details", source = "detailsDTO" ) + @InheritInverseConfiguration + @Mapping( target = "details.model", ignore = true ) + @Mapping( target = "details.type", constant = "gto") + @Mapping( target = "details.fuel", source = "detailsDTO.fuelType") + Car map(CarDTO in); + + class Car { + //CHECKSTYLE:OFF + public Details details; + //CHECKSTYLE:ON + } + + class CarDTO { + //CHECKSTYLE:OFF + public DetailsDTO detailsDTO; + //CHECKSTYLE:ON + } + + class Details { + //CHECKSTYLE:OFF + public String brand; + public String model; + public String type; + public String fuel; + //CHECKSTYLE:ON + } + + class DetailsDTO { + //CHECKSTYLE:OFF + public String brand; + public String fuelType; + //CHECKSTYLE:ON + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278MapperB.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278MapperB.java new file mode 100644 index 0000000000..b9d0abd523 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278MapperB.java @@ -0,0 +1,65 @@ +/* + * 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._2278; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * ReproducerB + * + */ +@Mapper +public interface Issue2278MapperB { + + Issue2278MapperB INSTANCE = Mappers.getMapper( Issue2278MapperB.class ); + + // id mapping is cros-linked + @Mapping( target = "amount", source = "price" ) + @Mapping( target = "detailsDTO.brand", source = "details.type" ) + @Mapping( target = "detailsDTO.id1", source = "details.id2" ) + @Mapping( target = "detailsDTO.id2", source = "details.id1" ) + CarDTO map(Car in); + + // inherit inverse, but undo cross-link in one sweep + @InheritInverseConfiguration // inherits all + @Mapping( target = "details", source = "detailsDTO" ) // resets everything on details <> detailsDto + @Mapping( target = "details.type", source = "detailsDTO.brand" ) // so this needs to be redone + Car map2(CarDTO in); + + class Car { + //CHECKSTYLE:OFF + public float price; + public Details details; + //CHECKSTYLE:ON + } + + class CarDTO { + //CHECKSTYLE:OFF + public float amount; + public DetailsDTO detailsDTO; + //CHECKSTYLE:ON + } + + class Details { + //CHECKSTYLE:OFF + public String type; + public String id1; + public String id2; + //CHECKSTYLE:ON + } + + class DetailsDTO { + //CHECKSTYLE:OFF + public String brand; + public String id1; + public String id2; + //CHECKSTYLE:ON + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278ReferenceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278ReferenceMapper.java new file mode 100644 index 0000000000..48b457f84c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278ReferenceMapper.java @@ -0,0 +1,54 @@ +/* + * 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._2278; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * A reference mapper, that checks how a regular forward merge works + */ +@Mapper +public interface Issue2278ReferenceMapper { + + Issue2278ReferenceMapper INSTANCE = Mappers.getMapper( Issue2278ReferenceMapper.class ); + + @Mapping( target = "details", source = "detailsDTO" ) + @Mapping( target = "details.model", ignore = true ) + @Mapping( target = "details.type", constant = "gto") + @Mapping( target = "details.fuel", source = "detailsDTO.fuelType") + Car map(CarDTO in); + + class Car { + //CHECKSTYLE:OFF + public Details details; + //CHECKSTYLE:ON + } + + class CarDTO { + //CHECKSTYLE:OFF + public DetailsDTO detailsDTO; + //CHECKSTYLE:ON + } + + class Details { + //CHECKSTYLE:OFF + public String brand; + public String model; + public String type; + public String fuel; + //CHECKSTYLE:ON + } + + class DetailsDTO { + //CHECKSTYLE:OFF + public String brand; + public String fuelType; + //CHECKSTYLE:ON + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278Test.java new file mode 100644 index 0000000000..eefebfa870 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278Test.java @@ -0,0 +1,95 @@ +/* + * 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._2278; + +import static org.assertj.core.api.Assertions.assertThat; + +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; + +@IssueKey("2278") +@RunWith( AnnotationProcessorTestRunner.class) +public class Issue2278Test { + + @Test + @WithClasses( Issue2278ReferenceMapper.class ) + public void testReferenceMergeBehaviour() { + + Issue2278ReferenceMapper.CarDTO dto = new Issue2278ReferenceMapper.CarDTO(); + dto.detailsDTO = new Issue2278ReferenceMapper.DetailsDTO(); + dto.detailsDTO.brand = "Ford"; + dto.detailsDTO.fuelType = "petrol"; + + Issue2278ReferenceMapper.Car target = Issue2278ReferenceMapper.INSTANCE.map( dto ); + + assertThat( target ).isNotNull(); + assertThat( target.details ).isNotNull(); + assertThat( target.details.brand ).isEqualTo( "Ford" ); + assertThat( target.details.model ).isNull(); + assertThat( target.details.type ).isEqualTo( "gto" ); + assertThat( target.details.fuel ).isEqualTo( "petrol" ); + + } + + @Test + @WithClasses( Issue2278MapperA.class ) + public void shouldBehaveJustAsTestReferenceMergeBehaviour() { + + Issue2278MapperA.CarDTO dto = new Issue2278MapperA.CarDTO(); + dto.detailsDTO = new Issue2278MapperA.DetailsDTO(); + dto.detailsDTO.brand = "Ford"; + dto.detailsDTO.fuelType = "petrol"; + + Issue2278MapperA.Car target = Issue2278MapperA.INSTANCE.map( dto ); + + assertThat( target ).isNotNull(); + assertThat( target.details ).isNotNull(); + assertThat( target.details.brand ).isEqualTo( "Ford" ); + assertThat( target.details.model ).isNull(); + assertThat( target.details.type ).isEqualTo( "gto" ); + assertThat( target.details.fuel ).isEqualTo( "petrol" ); + + } + + @Test + @WithClasses( Issue2278MapperB.class ) + public void shouldOverrideDetailsMappingWithRedefined() { + + Issue2278MapperB.Car source = new Issue2278MapperB.Car(); + source.details = new Issue2278MapperB.Details(); + source.details.type = "Ford"; + source.details.id1 = "id1"; + source.details.id2 = "id2"; + source.price = 20000f; + + Issue2278MapperB.CarDTO target1 = Issue2278MapperB.INSTANCE.map( source ); + + assertThat( target1 ).isNotNull(); + assertThat( target1.amount ).isEqualTo( 20000f ); + assertThat( target1.detailsDTO ).isNotNull(); + assertThat( target1.detailsDTO.brand ).isEqualTo( "Ford" ); + assertThat( target1.detailsDTO.id1 ).isEqualTo( "id2" ); + assertThat( target1.detailsDTO.id2 ).isEqualTo( "id1" ); + + // restore the mappings, just to make it logical again + target1.detailsDTO.id1 = "id1"; + target1.detailsDTO.id2 = "id2"; + + // now check the reverse inheritance + Issue2278MapperB.Car target2 = Issue2278MapperB.INSTANCE.map2( target1 ); + + assertThat( target2 ).isNotNull(); + assertThat( target2.price ).isEqualTo( 20000f ); + assertThat( target2.details ).isNotNull(); + assertThat( target2.details.type ).isEqualTo( "Ford" ); // should inherit + assertThat( target2.details.id1 ).isEqualTo( "id1" ); // should be undone + assertThat( target2.details.id2 ).isEqualTo( "id2" ); // should be undone + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Mapper.java new file mode 100644 index 0000000000..20621e4d01 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Mapper.java @@ -0,0 +1,115 @@ +/* + * 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._2318; + +import org.mapstruct.InheritConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MapperConfig; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper(config = Issue2318Mapper.Config.class) +public interface Issue2318Mapper { + + Issue2318Mapper INSTANCE = Mappers.getMapper( Issue2318Mapper.class ); + + @MapperConfig + interface Config { + + @Mapping(target = "parentValue1", source = "holder") + TargetParent mapParent(SourceParent parent); + + @InheritConfiguration(name = "mapParent") + @Mapping(target = "childValue", source = "value") + @Mapping(target = "parentValue2", source = "holder.parentValue2") + TargetChild mapChild(SourceChild child); + } + + @InheritConfiguration(name = "mapChild") + TargetChild mapChild(SourceChild child); + + default String parentValue1(SourceParent.Holder holder) { + return holder.getParentValue1(); + } + + class SourceParent { + private Holder holder; + + public Holder getHolder() { + return holder; + } + + public void setHolder(Holder holder) { + this.holder = holder; + } + + public static class Holder { + private String parentValue1; + private Integer parentValue2; + + public String getParentValue1() { + return parentValue1; + } + + public void setParentValue1(String parentValue1) { + this.parentValue1 = parentValue1; + } + + public Integer getParentValue2() { + return parentValue2; + } + + public void setParentValue2(Integer parentValue2) { + this.parentValue2 = parentValue2; + } + } + } + + class SourceChild extends SourceParent { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class TargetParent { + private String parentValue1; + private Integer parentValue2; + + public String getParentValue1() { + return parentValue1; + } + + public void setParentValue1(String parentValue1) { + this.parentValue1 = parentValue1; + } + + public Integer getParentValue2() { + return parentValue2; + } + + public void setParentValue2(Integer parentValue2) { + this.parentValue2 = parentValue2; + } + } + + class TargetChild extends TargetParent { + private String childValue; + + public String getChildValue() { + return childValue; + } + + public void setChildValue(String childValue) { + this.childValue = childValue; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Test.java new file mode 100644 index 0000000000..a1f1ad250b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Test.java @@ -0,0 +1,38 @@ +/* + * 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._2318; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.bugs._2318.Issue2318Mapper.SourceChild; +import org.mapstruct.ap.test.bugs._2318.Issue2318Mapper.TargetChild; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("2318") +@RunWith(AnnotationProcessorTestRunner.class) +public class Issue2318Test { + + @Test + @WithClasses( Issue2318Mapper.class ) + public void shouldMap() { + + SourceChild source = new SourceChild(); + source.setValue( "From child" ); + source.setHolder( new Issue2318Mapper.SourceParent.Holder() ); + source.getHolder().setParentValue1( "From parent" ); + source.getHolder().setParentValue2( 12 ); + + TargetChild target = Issue2318Mapper.INSTANCE.mapChild( source ); + + assertThat( target.getParentValue1() ).isEqualTo( "From parent" ); + assertThat( target.getParentValue2() ).isEqualTo( 12 ); + assertThat( target.getChildValue() ).isEqualTo( "From child" ); + } +} From 0d8bbacc53bfd101015a5d8860d67a3c2d71ea1c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 16 Jan 2021 19:43:22 +0100 Subject: [PATCH 133/597] #2250, #2324 Do not throw error when qualifier is missing on iterable mapping With this change we are relaxing the error handling when qualifiers are used in `@Mapping` and we allow defining qualifiers for collections / array mapping to mean the same as if it was defined on `@IterableMapping` i.e. apply the qualifier on the element mapping --- .../model/ContainerMappingMethodBuilder.java | 10 +++- .../ap/internal/model/PropertyMapping.java | 1 + .../creation/MappingResolverImpl.java | 8 +++- ...neousMessageByNamedWithIterableMapper.java | 31 ++++++++++++ .../qualifier/errors/QualfierMessageTest.java | 23 +++++++++ .../selection/qualifier/iterable/Cities.java | 23 +++++++++ .../iterable/IterableAndQualifiersTest.java | 40 +++++++++++++++- .../selection/qualifier/iterable/Rivers.java | 23 +++++++++ .../qualifier/iterable/TopologyMapper.java | 17 ------- .../TopologyWithoutIterableMappingMapper.java | 47 +++++++++++++++++++ 10 files changed, 203 insertions(+), 20 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByNamedWithIterableMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/Cities.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/Rivers.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/TopologyWithoutIterableMappingMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java index b9bbf6d094..be08ac1ca4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java @@ -22,6 +22,8 @@ import static org.mapstruct.ap.internal.util.Collections.first; +import javax.lang.model.element.AnnotationMirror; + /** * Builder that can be used to build {@link ContainerMappingMethod}(s). * @@ -37,6 +39,7 @@ public abstract class ContainerMappingMethodBuilder selfType, String errorMessagePart) { super( selfType ); @@ -58,6 +61,11 @@ public B callingContextTargetPropertyName(String callingContextTargetPropertyNam return myself; } + public B positionHint(AnnotationMirror positionHint) { + this.positionHint = positionHint; + return myself; + } + @Override public final M build() { Type sourceParameterType = first( method.getSourceParameters() ).getType(); @@ -88,7 +96,7 @@ public final M build() { formattingParameters, criteria, sourceRHS, - null, + positionHint, () -> forge( sourceRHS, sourceElementType, targetElementType ) ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 61e1fca864..48ebc34d29 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -656,6 +656,7 @@ private Assignment forgeWithElementMapping(Type sourceType, Type targetType, Sou .method( methodRef ) .selectionParameters( selectionParameters ) .callingContextTargetPropertyName( targetPropertyName ) + .positionHint( positionHint ) .build(); return createForgedAssignment( source, methodRef, iterableMappingMethod ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index debe8f30dc..5f9121082c 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -298,7 +298,13 @@ && allowDirect( sourceType, targetType ) ) { } if ( hasQualfiers() ) { - printQualifierMessage( selectionCriteria ); + if ((sourceType.isCollectionType() || sourceType.isArrayType()) && targetType.isIterableType()) { + // Allow forging iterable mapping when no iterable mapping already found + return forger.get(); + } + else { + printQualifierMessage( selectionCriteria ); + } } else if ( allowMappingMethod() ) { // only forge if we would allow mapping method diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByNamedWithIterableMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByNamedWithIterableMapper.java new file mode 100644 index 0000000000..088207c610 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/ErroneousMessageByNamedWithIterableMapper.java @@ -0,0 +1,31 @@ +/* + * 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.selection.qualifier.errors; + +import java.util.Collection; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper +public interface ErroneousMessageByNamedWithIterableMapper { + + @Mapping(target = "elements", qualifiedByName = "SelectMe") + Target map(Source source); + + // CHECKSTYLE:OFF + class Source { + + public Collection elements; + } + + class Target { + + public Collection elements; + } + // CHECKSTYLE ON + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java index 4c8edc64dd..48295ed4fb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java @@ -87,4 +87,27 @@ public void testNoQualifyingMethodByNamedFound() { ) public void testNoQualifyingMethodByAnnotationAndNamedFound() { } + + @Test + @WithClasses(ErroneousMessageByNamedWithIterableMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = ErroneousMessageByNamedWithIterableMapper.class, + kind = ERROR, + line = 16, + message = "Qualifier error. No method found annotated with @Named#value: [ SelectMe ]. " + + "See https://mapstruct.org/faq/#qualifier for more info."), + @Diagnostic( + type = ErroneousMessageByNamedWithIterableMapper.class, + kind = ERROR, + line = 16, + messageRegExp = "Can't map property.*"), + + } + ) + public void testNoQualifyingMethodByNamedForForgedIterableFound() { + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/Cities.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/Cities.java new file mode 100644 index 0000000000..828750399a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/Cities.java @@ -0,0 +1,23 @@ +/* + * 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.selection.qualifier.iterable; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mapstruct.Qualifier; + +/** + * @author Filip Hrisafov + */ +@Qualifier +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface Cities { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/IterableAndQualifiersTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/IterableAndQualifiersTest.java index db8de9496e..c2465b255e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/IterableAndQualifiersTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/IterableAndQualifiersTest.java @@ -15,6 +15,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import org.mapstruct.factory.Mappers; /** * @@ -22,20 +23,22 @@ */ @IssueKey( "707" ) @WithClasses( { + Cities.class, CityDto.class, CityEntity.class, RiverDto.class, RiverEntity.class, + Rivers.class, TopologyDto.class, TopologyEntity.class, TopologyFeatureDto.class, TopologyFeatureEntity.class, - TopologyMapper.class } ) @RunWith( AnnotationProcessorTestRunner.class ) public class IterableAndQualifiersTest { @Test + @WithClasses(TopologyMapper.class) public void testGenerationBasedOnQualifier() { TopologyDto topologyDto1 = new TopologyDto(); @@ -67,4 +70,39 @@ public void testGenerationBasedOnQualifier() { assertThat( ( (CityEntity) result2.getTopologyFeatures().get( 0 ) ).getPopulation() ).isEqualTo( 800000 ); } + + @Test + @WithClasses(TopologyWithoutIterableMappingMapper.class) + public void testIterableGeneratorBasedOnQualifier() { + + TopologyWithoutIterableMappingMapper mapper = Mappers.getMapper( TopologyWithoutIterableMappingMapper.class ); + + TopologyDto riverTopologyDto = new TopologyDto(); + List topologyFeatures1 = new ArrayList<>(); + RiverDto riverDto = new RiverDto(); + riverDto.setName( "Rhine" ); + riverDto.setLength( 5 ); + topologyFeatures1.add( riverDto ); + riverTopologyDto.setTopologyFeatures( topologyFeatures1 ); + + TopologyEntity riverTopology = mapper.mapTopologyAsRiver( riverTopologyDto ); + assertThat( riverTopology.getTopologyFeatures() ).hasSize( 1 ); + assertThat( riverTopology.getTopologyFeatures().get( 0 ).getName() ).isEqualTo( "Rhine" ); + assertThat( riverTopology.getTopologyFeatures().get( 0 ) ).isInstanceOf( RiverEntity.class ); + assertThat( ( (RiverEntity) riverTopology.getTopologyFeatures().get( 0 ) ).getLength() ).isEqualTo( 5 ); + + TopologyDto cityTopologyDto = new TopologyDto(); + List topologyFeatures2 = new ArrayList<>(); + CityDto cityDto = new CityDto(); + cityDto.setName( "Amsterdam" ); + cityDto.setPopulation( 800000 ); + topologyFeatures2.add( cityDto ); + cityTopologyDto.setTopologyFeatures( topologyFeatures2 ); + + TopologyEntity cityTopology = mapper.mapTopologyAsCity( cityTopologyDto ); + assertThat( cityTopology.getTopologyFeatures() ).hasSize( 1 ); + assertThat( cityTopology.getTopologyFeatures().get( 0 ).getName() ).isEqualTo( "Amsterdam" ); + assertThat( cityTopology.getTopologyFeatures().get( 0 ) ).isInstanceOf( CityEntity.class ); + assertThat( ( (CityEntity) cityTopology.getTopologyFeatures().get( 0 ) ).getPopulation() ).isEqualTo( 800000 ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/Rivers.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/Rivers.java new file mode 100644 index 0000000000..f6a56951a1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/Rivers.java @@ -0,0 +1,23 @@ +/* + * 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.selection.qualifier.iterable; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mapstruct.Qualifier; + +/** + * @author Filip Hrisafov + */ +@Qualifier +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface Rivers { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/TopologyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/TopologyMapper.java index 22e87dd084..84c4bc58a1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/TopologyMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/TopologyMapper.java @@ -5,16 +5,11 @@ */ package org.mapstruct.ap.test.selection.qualifier.iterable; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; import java.util.List; import org.mapstruct.IterableMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.Qualifier; import org.mapstruct.factory.Mappers; /** @@ -64,16 +59,4 @@ protected TopologyFeatureEntity mapCity( TopologyFeatureDto dto ) { return topologyFeatureEntity; } - @Qualifier - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.CLASS) - public @interface Rivers { - } - - @Qualifier - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.CLASS) - public @interface Cities { - } - } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/TopologyWithoutIterableMappingMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/TopologyWithoutIterableMappingMapper.java new file mode 100644 index 0000000000..4b72b34663 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/TopologyWithoutIterableMappingMapper.java @@ -0,0 +1,47 @@ +/* + * 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.selection.qualifier.iterable; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface TopologyWithoutIterableMappingMapper { + + @Mapping( target = "topologyFeatures", qualifiedBy = Rivers.class ) + TopologyEntity mapTopologyAsRiver(TopologyDto dto); + + @Mapping( target = "topologyFeatures", qualifiedBy = Cities.class ) + TopologyEntity mapTopologyAsCity(TopologyDto dto); + + @Rivers + default TopologyFeatureEntity mapRiver( TopologyFeatureDto dto ) { + TopologyFeatureEntity topologyFeatureEntity = null; + if ( dto instanceof RiverDto ) { + RiverEntity riverEntity = new RiverEntity(); + riverEntity.setLength( ( (RiverDto) dto ).getLength() ); + topologyFeatureEntity = riverEntity; + topologyFeatureEntity.setName( dto.getName() ); + } + return topologyFeatureEntity; + } + + @Cities + default TopologyFeatureEntity mapCity( TopologyFeatureDto dto ) { + TopologyFeatureEntity topologyFeatureEntity = null; + if ( dto instanceof CityDto ) { + CityEntity cityEntity = new CityEntity(); + cityEntity.setPopulation( ( (CityDto) dto ).getPopulation() ); + topologyFeatureEntity = cityEntity; + topologyFeatureEntity.setName( dto.getName() ); + } + return topologyFeatureEntity; + } + +} From 08af258533c6f949de8b0e1a71fb79beab895b5a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 30 Jan 2021 10:35:04 +0100 Subject: [PATCH 134/597] #2346 Update documentation and readme about MapStruct and Gradle Remove obsolete net.ltgt.apt Use com.diffplug.eclipse.apt for Eclipse IntelliJ works transparently --- .../main/asciidoc/chapter-2-set-up.asciidoc | 36 ++----------------- readme.md | 7 +--- 2 files changed, 3 insertions(+), 40 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index cb0067f2f3..76626f44ce 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -76,7 +76,7 @@ It will not work with older versions. Add the following to your Gradle build file in order to enable MapStruct: -.Gradle configuration (3.4 and later) +.Gradle configuration ==== [source, groovy, linenums] [subs="verbatim,attributes"] @@ -84,14 +84,9 @@ Add the following to your Gradle build file in order to enable MapStruct: ... plugins { ... - id 'net.ltgt.apt' version '0.20' + id "com.diffplug.eclipse.apt" version "3.26.0" // Only for Eclipse } -// You can integrate with your IDEs. -// See more details: https://github.com/tbroyer/gradle-apt-plugin#usage-with-ides -apply plugin: 'net.ltgt.apt-idea' -apply plugin: 'net.ltgt.apt-eclipse' - dependencies { ... implementation "org.mapstruct:mapstruct:${mapstructVersion}" @@ -103,33 +98,6 @@ dependencies { ... ---- ==== -.Gradle (3.3 and older) -==== -[source, groovy, linenums] -[subs="verbatim,attributes"] ----- -... -plugins { - ... - id 'net.ltgt.apt' version '0.20' -} - -// You can integrate with your IDEs. -// See more details: https://github.com/tbroyer/gradle-apt-plugin#usage-with-ides -apply plugin: 'net.ltgt.apt-idea' -apply plugin: 'net.ltgt.apt-eclipse' - -dependencies { - ... - compile "org.mapstruct:mapstruct:${mapstructVersion}" - annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" - - // If you are using mapstruct in test code - testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" -} -... ----- -==== You can find a complete example in the https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-on-gradle[mapstruct-examples] project on GitHub. diff --git a/readme.md b/readme.md index c39a408ac2..45eeda27e6 100644 --- a/readme.md +++ b/readme.md @@ -109,14 +109,9 @@ For Gradle, you need something along the following lines: ```groovy plugins { ... - id 'net.ltgt.apt' version '0.15' + id "com.diffplug.eclipse.apt" version "3.26.0" // Only for Eclipse } -// You can integrate with your IDEs. -// See more details: https://github.com/tbroyer/gradle-apt-plugin#usage-with-ides -apply plugin: 'net.ltgt.apt-idea' -apply plugin: 'net.ltgt.apt-eclipse' - dependencies { ... compile 'org.mapstruct:mapstruct:1.4.1.Final' From aeadf8cb77978334619e40801faed25e5eaa54b3 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 31 Jan 2021 14:52:11 +0100 Subject: [PATCH 135/597] Update readme with latest released 1.4.2.Final release --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index 45eeda27e6..5fc8399748 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.4.1.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.4.2.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.4.1.Final + 1.4.2.Final ... @@ -114,10 +114,10 @@ plugins { dependencies { ... - compile 'org.mapstruct:mapstruct:1.4.1.Final' + compile 'org.mapstruct:mapstruct:1.4.2.Final' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.1.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.1.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' // if you are using mapstruct in test code } ... ``` From c59ca79e7f992d051661fa2d39eb1cbee014b116 Mon Sep 17 00:00:00 2001 From: dmngb <26900086+dmngb@users.noreply.github.com> Date: Sat, 6 Feb 2021 16:10:32 +0100 Subject: [PATCH 136/597] #2277 default component model: mapper reference use singleton INSTANCE if it exists (#2280) This allows to easily avoid the runtime dependency on mapstruct.jar: we can avoid Mappers.getMapper(...) for instantiating used mappers if the code follows the conventional pattern for creating mapper singletons. Co-authored-by: GIBOU Damien --- .../model/DefaultMapperReference.java | 18 +++++++++---- .../processor/MapperCreationProcessor.java | 26 +++++++++++++++++++ .../internal/model/DefaultMapperReference.ftl | 2 +- .../selection/OrganizationMapper1Impl.java | 3 +-- .../selection/OrganizationMapper3Impl.java | 3 +-- 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/DefaultMapperReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/DefaultMapperReference.java index 924648368a..6ac0c1c74d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/DefaultMapperReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/DefaultMapperReference.java @@ -21,19 +21,22 @@ */ public class DefaultMapperReference extends MapperReference { + private final boolean isSingleton; private final boolean isAnnotatedMapper; private final Set importTypes; - private DefaultMapperReference(Type type, boolean isAnnotatedMapper, Set importTypes, String variableName) { + private DefaultMapperReference(Type type, boolean isAnnotatedMapper, boolean isSingleton, + Set importTypes, String variableName) { super( type, variableName ); this.isAnnotatedMapper = isAnnotatedMapper; this.importTypes = importTypes; + this.isSingleton = isSingleton; } - public static DefaultMapperReference getInstance(Type type, boolean isAnnotatedMapper, TypeFactory typeFactory, - List otherMapperReferences) { + public static DefaultMapperReference getInstance(Type type, boolean isAnnotatedMapper, boolean isSingleton, + TypeFactory typeFactory, List otherMapperReferences) { Set importTypes = Collections.asSet( type ); - if ( isAnnotatedMapper ) { + if ( isAnnotatedMapper && !isSingleton) { importTypes.add( typeFactory.getType( "org.mapstruct.factory.Mappers" ) ); } @@ -42,7 +45,7 @@ public static DefaultMapperReference getInstance(Type type, boolean isAnnotatedM otherMapperReferences ); - return new DefaultMapperReference( type, isAnnotatedMapper, importTypes, variableName ); + return new DefaultMapperReference( type, isAnnotatedMapper, isSingleton, importTypes, variableName ); } @Override @@ -53,4 +56,9 @@ public Set getImportTypes() { public boolean isAnnotatedMapper() { return isAnnotatedMapper; } + + public boolean isSingleton() { + return isSingleton; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index a0014f5934..ad6d84b9e9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.processor; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -13,7 +14,9 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -60,6 +63,9 @@ import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.version.VersionInformation; +import static javax.lang.model.element.Modifier.FINAL; +import static javax.lang.model.element.Modifier.PUBLIC; +import static javax.lang.model.element.Modifier.STATIC; import static org.mapstruct.ap.internal.model.SupportingConstructorFragment.addAllFragmentsIn; import static org.mapstruct.ap.internal.model.SupportingField.addAllFieldsIn; import static org.mapstruct.ap.internal.util.Collections.first; @@ -73,6 +79,9 @@ */ public class MapperCreationProcessor implements ModelElementProcessor, Mapper> { + /** Modifiers for public "constant" e.g. "public static final" */ + private static final List PUBLIC_CONSTANT_MODIFIERS = Arrays.asList( PUBLIC, STATIC, FINAL ); + private ElementUtils elementUtils; private TypeUtils typeUtils; private FormattingMessager messager; @@ -136,6 +145,7 @@ private List initReferencedMappers(TypeElement element, MapperO DefaultMapperReference mapperReference = DefaultMapperReference.getInstance( typeFactory.getType( usedMapper ), MapperGem.instanceOn( typeUtils.asElement( usedMapper ) ) != null, + hasSingletonInstance( usedMapper ), typeFactory, variableNames ); @@ -147,6 +157,22 @@ private List initReferencedMappers(TypeElement element, MapperO return result; } + private boolean hasSingletonInstance(TypeMirror mapper) { + return typeUtils.asElement( mapper ).getEnclosedElements().stream() + .anyMatch( a -> isPublicConstantOfType( a, "INSTANCE", mapper ) ); + } + + /** + * @return true if the element is a "public static final" field (e.g. a constant) + * named fieldName of type "fieldType" + */ + private boolean isPublicConstantOfType(Element element, String fieldName, TypeMirror fieldType) { + return element.getKind().isField() && + element.getModifiers().containsAll( PUBLIC_CONSTANT_MODIFIERS ) && + element.getSimpleName().contentEquals( fieldName ) && + typeUtils.isSameType( element.asType(), fieldType ); + } + private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List methods) { List mappingMethods = getMappingMethods( mapperOptions, methods ); diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/DefaultMapperReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/DefaultMapperReference.ftl index e4eeddb4b5..b2ab4cb6f0 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/DefaultMapperReference.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/DefaultMapperReference.ftl @@ -6,4 +6,4 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.DefaultMapperReference" --> -private final <@includeModel object=type/> ${variableName} = <#if annotatedMapper>Mappers.getMapper( <@includeModel object=type/>.class );<#else>new <@includeModel object=type/>(); \ No newline at end of file +private final <@includeModel object=type/> ${variableName} = <#if singleton><@includeModel object=type/>.INSTANCE;<#else><#if annotatedMapper>Mappers.getMapper( <@includeModel object=type/>.class );<#else>new <@includeModel object=type/>(); \ No newline at end of file diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/OrganizationMapper1Impl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/OrganizationMapper1Impl.java index 7e53df87c4..98f603d365 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/OrganizationMapper1Impl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/OrganizationMapper1Impl.java @@ -9,7 +9,6 @@ import org.mapstruct.ap.test.updatemethods.CompanyDto; import org.mapstruct.ap.test.updatemethods.CompanyEntity; import org.mapstruct.ap.test.updatemethods.DepartmentEntityFactory; -import org.mapstruct.factory.Mappers; @Generated( value = "org.mapstruct.ap.MappingProcessor", @@ -18,7 +17,7 @@ ) public class OrganizationMapper1Impl implements OrganizationMapper1 { - private final ExternalMapper externalMapper = Mappers.getMapper( ExternalMapper.class ); + private final ExternalMapper externalMapper = ExternalMapper.INSTANCE; private final DepartmentEntityFactory departmentEntityFactory = new DepartmentEntityFactory(); @Override diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/OrganizationMapper3Impl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/OrganizationMapper3Impl.java index 11c3bd0df6..76b2d3d9e8 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/OrganizationMapper3Impl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/OrganizationMapper3Impl.java @@ -9,7 +9,6 @@ import org.mapstruct.ap.test.updatemethods.BossDto; import org.mapstruct.ap.test.updatemethods.BossEntity; import org.mapstruct.ap.test.updatemethods.ConstructableDepartmentEntity; -import org.mapstruct.factory.Mappers; @Generated( value = "org.mapstruct.ap.MappingProcessor", @@ -18,7 +17,7 @@ ) public class OrganizationMapper3Impl implements OrganizationMapper3 { - private final ExternalMapper externalMapper = Mappers.getMapper( ExternalMapper.class ); + private final ExternalMapper externalMapper = ExternalMapper.INSTANCE; @Override public void toBossEntity(BossDto dto, BossEntity entity) { From b643061b57f39dbf75d7142ec7b9144ed184331b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 6 Feb 2021 16:27:24 +0100 Subject: [PATCH 137/597] #2277 Add tests with fixtures with testing the generated source code --- .../DefaultComponentModelMapperTest.java | 46 +++++++++++++++++++ .../InstanceIterableMapper.java | 19 ++++++++ .../defaultcomponentmodel/InstanceMapper.java | 20 ++++++++ .../NonInstanceIterableMapper.java | 19 ++++++++ .../NonInstanceMapper.java | 20 ++++++++ .../NonPublicIterableMapper.java | 19 ++++++++ .../NonPublicMapper.java | 20 ++++++++ .../ap/test/defaultcomponentmodel/Source.java | 22 +++++++++ .../ap/test/defaultcomponentmodel/Target.java | 22 +++++++++ .../InstanceIterableMapperImpl.java | 34 ++++++++++++++ .../NonInstanceIterableMapperImpl.java | 35 ++++++++++++++ .../NonPublicIterableMapperImpl.java | 35 ++++++++++++++ 12 files changed, 311 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/DefaultComponentModelMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/InstanceIterableMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/InstanceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceIterableMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicIterableMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/Target.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/InstanceIterableMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceIterableMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicIterableMapperImpl.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/DefaultComponentModelMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/DefaultComponentModelMapperTest.java new file mode 100644 index 0000000000..b6b19ea88d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/DefaultComponentModelMapperTest.java @@ -0,0 +1,46 @@ +/* + * 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.defaultcomponentmodel; + +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; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2277") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Source.class, + Target.class, +}) +public class DefaultComponentModelMapperTest { + + @Rule + public final GeneratedSource generatedSource = new GeneratedSource(); + + @Test + @WithClasses({ + InstanceIterableMapper.class, + InstanceMapper.class, + NonInstanceIterableMapper.class, + NonInstanceMapper.class, + NonPublicIterableMapper.class, + NonPublicMapper.class, + }) + public void shouldGenerateCorrectMapperInstantiation() { + generatedSource.addComparisonToFixtureFor( + InstanceIterableMapper.class, + NonInstanceIterableMapper.class, + NonPublicIterableMapper.class + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/InstanceIterableMapper.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/InstanceIterableMapper.java new file mode 100644 index 0000000000..b5b0506b62 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/InstanceIterableMapper.java @@ -0,0 +1,19 @@ +/* + * 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.defaultcomponentmodel; + +import java.util.List; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = InstanceMapper.class) +public interface InstanceIterableMapper { + + List map(List list); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/InstanceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/InstanceMapper.java new file mode 100644 index 0000000000..902ed551cd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/InstanceMapper.java @@ -0,0 +1,20 @@ +/* + * 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.defaultcomponentmodel; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface InstanceMapper { + + InstanceMapper INSTANCE = Mappers.getMapper( InstanceMapper.class ); + + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceIterableMapper.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceIterableMapper.java new file mode 100644 index 0000000000..6cdbe53006 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceIterableMapper.java @@ -0,0 +1,19 @@ +/* + * 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.defaultcomponentmodel; + +import java.util.List; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = NonInstanceMapper.class) +public interface NonInstanceIterableMapper { + + List map(List list); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceMapper.java new file mode 100644 index 0000000000..0d7f68392c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceMapper.java @@ -0,0 +1,20 @@ +/* + * 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.defaultcomponentmodel; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface NonInstanceMapper { + + NonInstanceMapper MAPPER = Mappers.getMapper( NonInstanceMapper.class ); + + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicIterableMapper.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicIterableMapper.java new file mode 100644 index 0000000000..20d6dbc1e7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicIterableMapper.java @@ -0,0 +1,19 @@ +/* + * 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.defaultcomponentmodel; + +import java.util.List; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = NonPublicMapper.class) +public interface NonPublicIterableMapper { + + List map(List list); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicMapper.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicMapper.java new file mode 100644 index 0000000000..36f9b978f2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicMapper.java @@ -0,0 +1,20 @@ +/* + * 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.defaultcomponentmodel; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public abstract class NonPublicMapper { + + static final NonPublicMapper INSTANCE = Mappers.getMapper( NonPublicMapper.class ); + + public abstract Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/Source.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/Source.java new file mode 100644 index 0000000000..a6aa152e20 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/Source.java @@ -0,0 +1,22 @@ +/* + * 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.defaultcomponentmodel; + +/** + * @author Filip Hrisafov + */ +public class Source { + + private final String id; + + public Source(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/Target.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/Target.java new file mode 100644 index 0000000000..da7bfdbed0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/Target.java @@ -0,0 +1,22 @@ +/* + * 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.defaultcomponentmodel; + +/** + * @author Filip Hrisafov + */ +public class Target { + + private final String id; + + public Target(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/InstanceIterableMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/InstanceIterableMapperImpl.java new file mode 100644 index 0000000000..7dab330793 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/InstanceIterableMapperImpl.java @@ -0,0 +1,34 @@ +/* + * 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.defaultcomponentmodel; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-02-06T16:20:45+0100", + comments = "version: , compiler: javac, environment: Java 11.0.9.1 (AdoptOpenJDK)" +) +public class InstanceIterableMapperImpl implements InstanceIterableMapper { + + private final InstanceMapper instanceMapper = InstanceMapper.INSTANCE; + + @Override + public List map(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( Source source : list ) { + list1.add( instanceMapper.map( source ) ); + } + + return list1; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceIterableMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceIterableMapperImpl.java new file mode 100644 index 0000000000..3854376bad --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/NonInstanceIterableMapperImpl.java @@ -0,0 +1,35 @@ +/* + * 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.defaultcomponentmodel; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.processing.Generated; +import org.mapstruct.factory.Mappers; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-02-06T16:20:45+0100", + comments = "version: , compiler: javac, environment: Java 11.0.9.1 (AdoptOpenJDK)" +) +public class NonInstanceIterableMapperImpl implements NonInstanceIterableMapper { + + private final NonInstanceMapper nonInstanceMapper = Mappers.getMapper( NonInstanceMapper.class ); + + @Override + public List map(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( Source source : list ) { + list1.add( nonInstanceMapper.map( source ) ); + } + + return list1; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicIterableMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicIterableMapperImpl.java new file mode 100644 index 0000000000..4cca04617a --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/defaultcomponentmodel/NonPublicIterableMapperImpl.java @@ -0,0 +1,35 @@ +/* + * 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.defaultcomponentmodel; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.processing.Generated; +import org.mapstruct.factory.Mappers; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-02-06T16:20:45+0100", + comments = "version: , compiler: javac, environment: Java 11.0.9.1 (AdoptOpenJDK)" +) +public class NonPublicIterableMapperImpl implements NonPublicIterableMapper { + + private final NonPublicMapper nonPublicMapper = Mappers.getMapper( NonPublicMapper.class ); + + @Override + public List map(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( Source source : list ) { + list1.add( nonPublicMapper.map( source ) ); + } + + return list1; + } +} From 630a8da904d4669507fdf31f86ef96ac0deb1954 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 6 Feb 2021 16:44:07 +0100 Subject: [PATCH 138/597] Fix a typo in the Mapper#componentModel --- core/src/main/java/org/mapstruct/Mapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 5ed9eab17f..92d393f6c8 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -143,7 +143,7 @@ * {@code jsr330}: the generated mapper is annotated with {@code @javax.inject.Named} and * {@code @Singleton}, and can be retrieved via {@code @Inject} *
    - * The method overrides an unmappedTargetPolicy set in a central configuration set + * The method overrides a componentModel set in a central configuration set * by {@link #config() } * * @return The component model for the generated mapper. From f4b62ded8910c0bc6d09fc677f887c3775ed1827 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 5 Feb 2021 23:01:41 +0100 Subject: [PATCH 139/597] #2352 Add source element type for Iterable mappings When the Iterable type we are mapping is not generic (i.e. it is a custom type extending an Iterable) then the source element type which is included in the loop was not imported. --- .../internal/model/IterableMappingMethod.java | 9 ++++ .../ap/test/bugs/_2352/Issue2352Test.java | 49 +++++++++++++++++++ .../ap/test/bugs/_2352/dto/TheDto.java | 22 +++++++++ .../ap/test/bugs/_2352/dto/TheModel.java | 22 +++++++++ .../ap/test/bugs/_2352/dto/TheModels.java | 15 ++++++ .../bugs/_2352/mapper/TheModelMapper.java | 19 +++++++ .../bugs/_2352/mapper/TheModelsMapper.java | 24 +++++++++ 7 files changed, 160 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/Issue2352Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheModel.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheModels.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/mapper/TheModelMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/mapper/TheModelsMapper.java 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 5310fa382d..38ec44f1e1 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 @@ -9,6 +9,7 @@ import java.util.Collection; import java.util.List; +import java.util.Set; import org.mapstruct.ap.internal.model.assignment.LocalVarWrapper; import org.mapstruct.ap.internal.model.assignment.SetterWrapper; @@ -86,6 +87,14 @@ private IterableMappingMethod(Method method, Collection existingVariable ); } + @Override + public Set getImportTypes() { + Set types = super.getImportTypes(); + + types.add( getSourceElementType() ); + return types; + } + public Type getSourceElementType() { Type sourceParameterType = getSourceParameter().getType(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/Issue2352Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/Issue2352Test.java new file mode 100644 index 0000000000..ebf6ac1e1c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/Issue2352Test.java @@ -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._2352; + +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.bugs._2352.dto.TheDto; +import org.mapstruct.ap.test.bugs._2352.dto.TheModel; +import org.mapstruct.ap.test.bugs._2352.dto.TheModels; +import org.mapstruct.ap.test.bugs._2352.mapper.TheModelMapper; +import org.mapstruct.ap.test.bugs._2352.mapper.TheModelsMapper; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2352") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + TheDto.class, + TheModel.class, + TheModels.class, + TheModelMapper.class, + TheModelsMapper.class, +}) +public class Issue2352Test { + + @Test + public void shouldGenerateValidCode() { + TheModels theModels = new TheModels(); + theModels.add( new TheModel( "1" ) ); + theModels.add( new TheModel( "2" ) ); + + List theDtos = TheModelsMapper.INSTANCE.convert( theModels ); + + assertThat( theDtos ) + .extracting( TheDto::getId ) + .containsExactly( "1", "2" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheDto.java new file mode 100644 index 0000000000..a07c8bd093 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheDto.java @@ -0,0 +1,22 @@ +/* + * 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._2352.dto; + +/** + * @author Filip Hrisafov + */ +public class TheDto { + + private String id; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheModel.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheModel.java new file mode 100644 index 0000000000..fc7b1e89be --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheModel.java @@ -0,0 +1,22 @@ +/* + * 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._2352.dto; + +/** + * @author Filip Hrisafov + */ +public class TheModel { + + private final String id; + + public TheModel(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheModels.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheModels.java new file mode 100644 index 0000000000..37359ce779 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/dto/TheModels.java @@ -0,0 +1,15 @@ +/* + * 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._2352.dto; + +import java.util.ArrayList; + +/** + * @author Filip Hrisafov + */ +public class TheModels extends ArrayList { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/mapper/TheModelMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/mapper/TheModelMapper.java new file mode 100644 index 0000000000..2e9b5be478 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/mapper/TheModelMapper.java @@ -0,0 +1,19 @@ +/* + * 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._2352.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.bugs._2352.dto.TheDto; +import org.mapstruct.ap.test.bugs._2352.dto.TheModel; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface TheModelMapper { + + TheDto convert(TheModel theModel); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/mapper/TheModelsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/mapper/TheModelsMapper.java new file mode 100644 index 0000000000..cf6c30409c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/mapper/TheModelsMapper.java @@ -0,0 +1,24 @@ +/* + * 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._2352.mapper; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.bugs._2352.dto.TheDto; +import org.mapstruct.ap.test.bugs._2352.dto.TheModels; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = TheModelMapper.class) +public interface TheModelsMapper { + + TheModelsMapper INSTANCE = Mappers.getMapper( TheModelsMapper.class ); + + List convert(TheModels theModels); +} From 07f5189a722396c17e1df8a2debbaefb5a337e32 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 6 Feb 2021 10:26:08 +0100 Subject: [PATCH 140/597] #2347 Do not generate mapper implementation for private mappers Provide a compiler error message instead of generating code that will not compile --- .../processor/MapperCreationProcessor.java | 12 +++++ .../mapstruct/ap/internal/util/Message.java | 1 + .../ErroneousClassWithPrivateMapper.java | 51 +++++++++++++++++++ .../ap/test/bugs/_2347/Issue2347Test.java | 49 ++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/ErroneousClassWithPrivateMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/Issue2347Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index ad6d84b9e9..c887140723 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -15,6 +15,7 @@ import java.util.SortedSet; import java.util.TreeSet; import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; @@ -210,6 +211,17 @@ private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List< mappingContext.getForgedMethodsUnderCreation().keySet() ); } + if ( element.getModifiers().contains( Modifier.PRIVATE ) ) { + // If the mapper element is private then we should report an error + // we can't generate an implementation for a private mapper + mappingContext.getMessager() + .printMessage( element, + Message.GENERAL_CANNOT_IMPLEMENT_PRIVATE_MAPPER, + element.getSimpleName().toString(), + element.getKind() == ElementKind.INTERFACE ? "interface" : "class" + ); + } + return mapper; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index d88791fda1..378395d4da 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -115,6 +115,7 @@ public enum Message { DECORATOR_NO_SUBTYPE( "Specified decorator type is no subtype of the annotated mapper type." ), DECORATOR_CONSTRUCTOR( "Specified decorator type has no default constructor nor a constructor with a single parameter accepting the decorated mapper type." ), + GENERAL_CANNOT_IMPLEMENT_PRIVATE_MAPPER("Cannot create an implementation for mapper %s, because it is a private %s."), GENERAL_NO_IMPLEMENTATION( "No implementation type is registered for return type %s." ), GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ), GENERAL_AMBIGUOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/ErroneousClassWithPrivateMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/ErroneousClassWithPrivateMapper.java new file mode 100644 index 0000000000..c18620887e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/ErroneousClassWithPrivateMapper.java @@ -0,0 +1,51 @@ +/* + * 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._2347; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +public class ErroneousClassWithPrivateMapper { + + public static class Target { + private final String id; + + public Target(String id) { + this.id = id; + } + + public String getId() { + return id; + } + } + + public static class Source { + + private final String id; + + public Source(String id) { + this.id = id; + } + + public String getId() { + return id; + } + } + + @Mapper + private interface PrivateInterfaceMapper { + + Target map(Source source); + } + + @Mapper + private abstract class PrivateClassMapper { + + public abstract Target map(Source source); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/Issue2347Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/Issue2347Test.java new file mode 100644 index 0000000000..f5e04fc776 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/Issue2347Test.java @@ -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._2347; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2347") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + ErroneousClassWithPrivateMapper.class +}) +public class Issue2347Test { + + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = ErroneousClassWithPrivateMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 41, + message = "Cannot create an implementation for mapper PrivateInterfaceMapper," + + " because it is a private interface." + ), + @Diagnostic( + type = ErroneousClassWithPrivateMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 47, + message = "Cannot create an implementation for mapper PrivateClassMapper," + + " because it is a private class." + ) + } + ) + @Test + public void shouldGenerateCompileErrorWhenMapperIsPrivate() { + + } +} From 85af901ea7d7bc6cbdc52952211df76fc290f426 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 6 Feb 2021 09:05:40 +0100 Subject: [PATCH 141/597] #2350 Generate core string to enum mapping when AnyRemaining or AnyUnmapped is not used --- .../ap/internal/model/ValueMappingMethod.java | 9 +++------ .../string2enum/StringToEnumMappingTest.java | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index c26dd5df6b..627a4b7288 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -262,8 +262,8 @@ private List stringToEnumMapping(Method method, Type targetType ) List mappings = new ArrayList<>(); List unmappedSourceConstants = new ArrayList<>( targetType.getEnumConstants() ); boolean sourceErrorOccurred = !reportErrorIfMappedTargetEnumConstantsDontExist( method, targetType ); - boolean mandatoryMissing = !reportErrorIfAnyRemainingOrAnyUnMappedMissing( method ); - if ( sourceErrorOccurred || mandatoryMissing ) { + reportWarningIfAnyRemainingOrAnyUnMappedMissing( method ); + if ( sourceErrorOccurred ) { return mappings; } Set mappedSources = new LinkedHashSet<>(); @@ -344,17 +344,14 @@ private boolean reportErrorIfSourceEnumConstantsContainsAnyRemaining(Method meth return !foundIncorrectMapping; } - private boolean reportErrorIfAnyRemainingOrAnyUnMappedMissing(Method method) { - boolean foundIncorrectMapping = false; + private void reportWarningIfAnyRemainingOrAnyUnMappedMissing(Method method) { if ( !( valueMappings.hasMapAnyUnmapped || valueMappings.hasMapAnyRemaining ) ) { ctx.getMessager().printMessage( method.getExecutable(), Message.VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING ); - foundIncorrectMapping = true; } - return !foundIncorrectMapping; } private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, Type targetType) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java index 7bf06ab860..fea8efac12 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java @@ -16,6 +16,7 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; @IssueKey( "1557" ) @WithClasses({ OrderType.class, OrderMapper.class }) @@ -53,7 +54,20 @@ public void testRemainingAndNull() { "type String to an enum type." ) } ) - public void shouldRaiseErrorWhenUsingAnyRemaining() { + public void shouldRaiseWarningWhenNotUsingAnyRemainingOrAnyUnmapped() { + + assertThatThrownBy( () -> ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.INSTANCE.map( "unknown" ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Unexpected enum constant: unknown" ); + + assertThat( ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.INSTANCE.map( null ) ) + .isEqualTo( OrderType.STANDARD ); + + assertThat( ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.INSTANCE.map( "STANDARD" ) ) + .isNull(); + + assertThat( ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.INSTANCE.map( "RETAIL" ) ) + .isEqualTo( OrderType.RETAIL ); } } From d5703d3ee8ecb8bd12ce80c6cd7a477119b2bfb8 Mon Sep 17 00:00:00 2001 From: Jeroen van Wilgenburg Date: Mon, 1 Mar 2021 07:27:47 +0100 Subject: [PATCH 142/597] #2368 fix order of target parameter in documentation --- core/src/main/java/org/mapstruct/Mapper.java | 2 +- core/src/main/java/org/mapstruct/Mappings.java | 8 ++++---- .../main/java/org/mapstruct/ValueMapping.java | 6 +++--- .../main/java/org/mapstruct/ValueMappings.java | 8 ++++---- ...er-11-reusing-mapping-configurations.asciidoc | 2 +- .../chapter-3-defining-a-mapper.asciidoc | 16 ++++++++-------- .../asciidoc/chapter-8-mapping-values.asciidoc | 6 +++--- .../model/beanmapping/SourceReference.java | 2 +- .../model/beanmapping/TargetReference.java | 2 +- readme.md | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 92d393f6c8..98022f1a6c 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -47,7 +47,7 @@ * uses = MarkMapper.class, * injectionStrategy = InjectionStrategy.CONSTRUCTOR) * public interface CarMapper { - * @Mapping(source = "mark", target = "name") + * @Mapping(target = "name", source = "mark") * CarDto convertMap(CarEntity carEntity); * } * diff --git a/core/src/main/java/org/mapstruct/Mappings.java b/core/src/main/java/org/mapstruct/Mappings.java index 039ec7b3c7..1578a648a9 100644 --- a/core/src/main/java/org/mapstruct/Mappings.java +++ b/core/src/main/java/org/mapstruct/Mappings.java @@ -23,8 +23,8 @@ * @Mapper * public interface MyMapper { * @Mappings({ - * @Mapping(source = "first", target = "firstProperty"), - * @Mapping(source = "second", target = "secondProperty") + * @Mapping(target = "firstProperty", source = "first"), + * @Mapping(target = "secondProperty", source = "second") * }) * HumanDto toHumanDto(Human human); * } @@ -33,8 +33,8 @@ * // Java 8 and later * @Mapper * public interface MyMapper { - * @Mapping(source = "first", target = "firstProperty"), - * @Mapping(source = "second", target = "secondProperty") + * @Mapping(target = "firstProperty", source = "first"), + * @Mapping(target = "secondProperty", source = "second") * HumanDto toHumanDto(Human human); * } * diff --git a/core/src/main/java/org/mapstruct/ValueMapping.java b/core/src/main/java/org/mapstruct/ValueMapping.java index 26ed661dce..f8f2d3b18e 100644 --- a/core/src/main/java/org/mapstruct/ValueMapping.java +++ b/core/src/main/java/org/mapstruct/ValueMapping.java @@ -27,9 +27,9 @@ * * public enum ExternalOrderType { RETAIL, B2B, SPECIAL, DEFAULT } * - * @ValueMapping(source = "EXTRA", target = "SPECIAL"), - * @ValueMapping(source = "STANDARD", target = "DEFAULT"), - * @ValueMapping(source = "NORMAL", target = "DEFAULT") + * @ValueMapping(target = "SPECIAL", source = "EXTRA"), + * @ValueMapping(target = "DEFAULT", source = "STANDARD"), + * @ValueMapping(target = "DEFAULT", source = "NORMAL") * ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); * * Mapping result: diff --git a/core/src/main/java/org/mapstruct/ValueMappings.java b/core/src/main/java/org/mapstruct/ValueMappings.java index a1b4666178..faefbf7105 100644 --- a/core/src/main/java/org/mapstruct/ValueMappings.java +++ b/core/src/main/java/org/mapstruct/ValueMappings.java @@ -22,8 +22,8 @@ * @Mapper * public interface GenderMapper { * @ValueMappings({ - * @ValueMapping(source = "MALE", target = "M"), - * @ValueMapping(source = "FEMALE", target = "F") + * @ValueMapping(target = "M", source = "MALE"), + * @ValueMapping(target = "F", source = "FEMALE") * }) * GenderDto mapToDto(Gender gender); * } @@ -32,8 +32,8 @@ * //Java 8 and later * @Mapper * public interface GenderMapper { - * @ValueMapping(source = "MALE", target = "M"), - * @ValueMapping(source = "FEMALE", target = "F") + * @ValueMapping(target = "M", source = "MALE"), + * @ValueMapping(target = "F", source = "FEMALE") * GenderDto mapToDto(Gender gender); * } * diff --git a/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc b/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc index 104b0a9b2f..fb8c71a622 100644 --- a/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc +++ b/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc @@ -54,7 +54,7 @@ Use the annotation `@InheritInverseConfiguration` to indicate that a method shal @Mapper public interface CarMapper { - @Mapping(source = "numberOfSeats", target = "seatCount") + @Mapping(target = "seatCount", source = "numberOfSeats") CarDto carToDto(Car car); @InheritInverseConfiguration diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index be3435b541..f679d4494a 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -16,11 +16,11 @@ To create a mapper simply define a Java interface with the required mapping meth @Mapper public interface CarMapper { - @Mapping(source = "make", target = "manufacturer") - @Mapping(source = "numberOfSeats", target = "seatCount") + @Mapping(target = "manufacturer", source = "make") + @Mapping(target = "seatCount", source = "numberOfSeats") CarDto carToCarDto(Car car); - @Mapping(source = "name", target = "fullName") + @Mapping(target = "fullName", source = "name") PersonDto personToPersonDto(Person person); } ---- @@ -236,8 +236,8 @@ MapStruct also supports mapping methods with several source parameters. This is @Mapper public interface AddressMapper { - @Mapping(source = "person.description", target = "description") - @Mapping(source = "address.houseNo", target = "houseNumber") + @Mapping(target = "description", source = "person.description") + @Mapping(target = "houseNumber", source = "address.houseNo") DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address); } ---- @@ -267,8 +267,8 @@ MapStruct also offers the possibility to directly refer to a source parameter. @Mapper public interface AddressMapper { - @Mapping(source = "person.description", target = "description") - @Mapping(source = "hn", target = "houseNumber") + @Mapping(target = "description", source = "person.description") + @Mapping(target = "houseNumber", source = "hn") DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn); } ---- @@ -368,7 +368,7 @@ public interface CustomerMapper { CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class ); - @Mapping(source = "customerName", target = "name") + @Mapping(target = "name", source = "customerName") Customer toCustomer(CustomerDto customerDto); @InheritInverseConfiguration diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index e38cee215b..57b9433a20 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -20,9 +20,9 @@ public interface OrderMapper { OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); @ValueMappings({ - @ValueMapping(source = "EXTRA", target = "SPECIAL"), - @ValueMapping(source = "STANDARD", target = "DEFAULT"), - @ValueMapping(source = "NORMAL", target = "DEFAULT") + @ValueMapping(target = "SPECIAL", source = "EXTRA"), + @ValueMapping(target = "DEFAULT", source = "STANDARD"), + @ValueMapping(target = "DEFAULT", source = "NORMAL") }) ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index 1d747c190e..d43a242278 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -34,7 +34,7 @@ * mapping method: * *
    - * @Mapping(source = "in.propA.propB" target = "propC")
    + * @Mapping(target = "propC", source = "in.propA.propB")
      * TypeB mappingMethod(TypeA in);
      * 
    * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java index fee97f840d..22bfd6343b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java @@ -32,7 +32,7 @@ * method: * *
    - * @Mapping(source = "in.propA.propB" target = "propC")
    + * @Mapping(target = "propC", source = "in.propA.propB")
      * TypeB mappingMethod(TypeA in);
      * 
    * diff --git a/readme.md b/readme.md index 5fc8399748..645f3ee992 100644 --- a/readme.md +++ b/readme.md @@ -42,7 +42,7 @@ public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); - @Mapping(source = "numberOfSeats", target = "seatCount") + @Mapping(target = "seatCount", source = "numberOfSeats") CarDto carToCarDto(Car car); } ``` From d9fdd86b94585ba59e918924ecbbcd33e4048573 Mon Sep 17 00:00:00 2001 From: Jeroen van Wilgenburg Date: Mon, 1 Mar 2021 08:10:31 +0100 Subject: [PATCH 143/597] #2368 fix order of target parameter in tests (and removed some spaces) --- .../itest/gradle/lib/TestMapper.java | 2 +- .../ap/test/bugs/_636/SourceTargetMapper.java | 4 +-- .../mapstruct/itest/java8/Java8Mapper.java | 4 +-- .../simple/SourceTargetAbstractMapper.java | 6 ++-- .../itest/simple/SourceTargetMapper.java | 6 ++-- .../AbstractSourceTargetMapperPrivate.java | 2 +- .../AbstractSourceTargetMapperProtected.java | 2 +- .../SourceTargetMapperDefaultOther.java | 2 +- .../SourceTargetMapperDefaultSame.java | 2 +- .../referenced/SourceTargetMapperPrivate.java | 2 +- .../SourceTargetMapperProtected.java | 2 +- .../ap/test/bugs/_1124/Issue1124Mapper.java | 2 +- .../ap/test/bugs/_1148/Issue1148Mapper.java | 32 +++++++++---------- .../ap/test/bugs/_1155/Issue1155Mapper.java | 2 +- .../mapstruct/ap/test/bugs/_1650/AMapper.java | 4 +-- .../ap/test/bugs/_1685/UserMapper.java | 10 +++--- .../ap/test/bugs/_1714/Issue1714Mapper.java | 2 +- .../ap/test/bugs/_1881/VehicleDtoMapper.java | 6 ++-- .../SameNameForSourceAndTargetCarsMapper.java | 2 +- .../ap/test/bugs/_405/PersonMapper.java | 2 +- .../ap/test/bugs/_846/Mapper846.java | 2 +- .../ap/test/bugs/_849/Issue849Mapper.java | 2 +- .../ap/test/bugs/_891/BuggyMapper.java | 2 +- .../test/collection/SourceTargetMapper.java | 14 ++++---- .../collection/adder/SourceTargetMapper.java | 2 +- .../SourceTargetMapper.java | 2 +- .../mapstruct/ap/test/complex/CarMapper.java | 4 +-- .../test/conversion/SourceTargetMapper.java | 4 +-- .../SourceTargetMapper.java | 4 +-- .../attributereference/ErroneousMapper1.java | 2 +- .../ap/test/fields/SourceTargetMapper.java | 2 +- ...rJsr330CompileOptionConstructorMapper.java | 2 +- .../CustomerJsr330ConstructorMapper.java | 2 +- ...rSpringCompileOptionConstructorMapper.java | 2 +- .../CustomerSpringConstructorMapper.java | 2 +- .../test/java8stream/SourceTargetMapper.java | 14 ++++---- .../test/java8stream/base/StreamMapper.java | 4 +-- .../SourceTargetMapper.java | 2 +- .../erroneous/PersonAgeMapper.java | 2 +- .../PersonGarageWrongTargetMapper.java | 4 +-- .../erroneous/PersonNameMapper.java | 2 +- .../SourceTypeTargetDtoMapper.java | 2 +- .../exceptions/ResourceMapper.java | 2 +- .../CustomerDefaultMapper.java | 4 +-- .../CustomerMapper.java | 4 +-- ...ustomerNvpmsOnBeanMappingMethodMapper.java | 2 +- .../CustomerNvpmsOnConfigMapper.java | 2 +- .../CustomerNvpmsOnMapperMapper.java | 2 +- .../CustomerNvpmsPropertyMappingMapper.java | 2 +- .../ErroneousCustomerMapper1.java | 2 +- .../ErroneousCustomerMapper2.java | 2 +- .../ErroneousCustomerMapper3.java | 2 +- .../ErroneousCustomerMapper4.java | 2 +- .../ErroneousCustomerMapper5.java | 2 +- .../ap/test/oneway/SourceTargetMapper.java | 2 +- .../ap/test/reverse/SourceTargetMapper.java | 12 +++---- .../SourceTargetMapperAmbiguous1.java | 12 +++---- .../SourceTargetMapperAmbiguous2.java | 12 +++---- .../SourceTargetMapperAmbiguous3.java | 12 +++---- .../SourceTargetMapperNonMatchingName.java | 6 ++-- .../source/constants/ErroneousMapper1.java | 2 +- .../source/constants/ErroneousMapper4.java | 2 +- .../SourceTargetMapperSeveralSources.java | 4 +-- .../SourceTargetMapperSeveralSources.java | 4 +-- .../SourceTargetConfig.java | 4 +-- .../SourceTargetMapper.java | 10 +++--- .../SourceTargetMapper.java | 8 ++--- .../ErroneousOrganizationMapper1.java | 6 ++-- .../ErroneousOrganizationMapper2.java | 2 +- .../updatemethods/OrganizationMapper.java | 8 ++--- 70 files changed, 154 insertions(+), 154 deletions(-) diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java index 13ae2ef5fc..6137ff0eca 100644 --- a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java +++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/src/main/java/org/mapstruct/itest/gradle/lib/TestMapper.java @@ -14,6 +14,6 @@ @Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface TestMapper { - @Mapping(source = "value", target = "field") + @Mapping(target = "field", source = "value") public Target toTarget(Source source); } diff --git a/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetMapper.java b/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetMapper.java index b619771fd3..85102e1918 100644 --- a/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetMapper.java +++ b/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/ap/test/bugs/_636/SourceTargetMapper.java @@ -15,8 +15,8 @@ public interface SourceTargetMapper extends SourceTargetBaseMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mappings({ - @Mapping(source = "idFoo", target = "foo"), - @Mapping(source = "idBar", target = "bar") + @Mapping(target = "foo", source = "idFoo"), + @Mapping(target = "bar", source = "idBar") }) Target mapSourceToTarget(Source source); } diff --git a/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/itest/java8/Java8Mapper.java b/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/itest/java8/Java8Mapper.java index 4acd23d08e..f16228b60e 100644 --- a/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/itest/java8/Java8Mapper.java +++ b/integrationtest/src/test/resources/java8Test/src/main/java/org/mapstruct/itest/java8/Java8Mapper.java @@ -14,7 +14,7 @@ public interface Java8Mapper { Java8Mapper INSTANCE = Mappers.getMapper( Java8Mapper.class ); - @Mapping(source = "firstName", target = "givenName") - @Mapping(source = "lastName", target = "surname") + @Mapping(target = "givenName", source = "firstName") + @Mapping(target = "surname", source = "lastName") Target sourceToTarget(Source source); } diff --git a/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetAbstractMapper.java b/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetAbstractMapper.java index c5a222a6ca..dd893384f7 100644 --- a/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetAbstractMapper.java +++ b/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetAbstractMapper.java @@ -16,9 +16,9 @@ public abstract class SourceTargetAbstractMapper { public static SourceTargetAbstractMapper INSTANCE = Mappers.getMapper( SourceTargetAbstractMapper.class ); @Mappings({ - @Mapping(source = "qax", target = "baz"), - @Mapping(source = "baz", target = "qax"), - @Mapping(source = "forNested.value", target = "fromNested") + @Mapping(target = "baz", source = "qax"), + @Mapping(target = "qax", source = "baz"), + @Mapping(target = "fromNested", source = "forNested.value") }) public abstract Target sourceToTarget(Source source); diff --git a/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetMapper.java b/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetMapper.java index 3de0b84722..b3fd443c6b 100644 --- a/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetMapper.java +++ b/integrationtest/src/test/resources/simpleTest/src/main/java/org/mapstruct/itest/simple/SourceTargetMapper.java @@ -17,9 +17,9 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mappings({ - @Mapping(source = "qax", target = "baz"), - @Mapping(source = "baz", target = "qax"), - @Mapping(source = "forNested.value", target = "fromNested") + @Mapping(target = "baz", source = "qax"), + @Mapping(target = "qax", source = "baz"), + @Mapping(target = "fromNested", source = "forNested.value") }) Target sourceToTarget(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperPrivate.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperPrivate.java index fa612bd956..507e63a957 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperPrivate.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperPrivate.java @@ -19,6 +19,6 @@ public abstract class AbstractSourceTargetMapperPrivate extends SourceTargetmapp public static final AbstractSourceTargetMapperPrivate INSTANCE = Mappers.getMapper( AbstractSourceTargetMapperPrivate.class ); - @Mapping(source = "referencedSource", target = "referencedTarget") + @Mapping(target = "referencedTarget", source = "referencedSource") public abstract Target toTarget(Source source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperProtected.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperProtected.java index 2ae40488f2..ef5df10285 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperProtected.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperProtected.java @@ -19,6 +19,6 @@ public abstract class AbstractSourceTargetMapperProtected extends SourceTargetma public static final AbstractSourceTargetMapperProtected INSTANCE = Mappers.getMapper( AbstractSourceTargetMapperProtected.class ); - @Mapping(source = "referencedSource", target = "referencedTarget") + @Mapping(target = "referencedTarget", source = "referencedSource") public abstract Target toTarget(Source source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperDefaultOther.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperDefaultOther.java index 4ada977bc8..682a258e3d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperDefaultOther.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperDefaultOther.java @@ -20,6 +20,6 @@ public interface SourceTargetMapperDefaultOther { SourceTargetMapperDefaultOther INSTANCE = Mappers.getMapper( SourceTargetMapperDefaultOther.class ); - @Mapping(source = "referencedSource", target = "referencedTarget") + @Mapping(target = "referencedTarget", source = "referencedSource") Target toTarget(Source source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperDefaultSame.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperDefaultSame.java index 36127e1af6..536f45d5be 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperDefaultSame.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperDefaultSame.java @@ -18,6 +18,6 @@ public interface SourceTargetMapperDefaultSame { SourceTargetMapperDefaultSame INSTANCE = Mappers.getMapper( SourceTargetMapperDefaultSame.class ); - @Mapping(source = "referencedSource", target = "referencedTarget") + @Mapping(target = "referencedTarget", source = "referencedSource") Target toTarget(Source source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperPrivate.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperPrivate.java index 1ec1aa6c5b..4615ff3e6f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperPrivate.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperPrivate.java @@ -18,6 +18,6 @@ public interface SourceTargetMapperPrivate { SourceTargetMapperPrivate INSTANCE = Mappers.getMapper( SourceTargetMapperPrivate.class ); - @Mapping(source = "referencedSource", target = "referencedTarget") + @Mapping(target = "referencedTarget", source = "referencedSource") Target toTarget(Source source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperProtected.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperProtected.java index f8ac1b4d28..c18c6127e4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperProtected.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperProtected.java @@ -18,6 +18,6 @@ public interface SourceTargetMapperProtected { SourceTargetMapperProtected INSTANCE = Mappers.getMapper( SourceTargetMapperProtected.class ); - @Mapping(source = "referencedSource", target = "referencedTarget") + @Mapping(target = "referencedTarget", source = "referencedSource") Target toTarget(Source source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1124/Issue1124Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1124/Issue1124Mapper.java index fce8ffcd6d..cae987aabe 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1124/Issue1124Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1124/Issue1124Mapper.java @@ -59,6 +59,6 @@ public void setEntity(DTO entity) { class MappingContext { } - @Mapping(source = "entity.id", target = "id") + @Mapping(target = "id", source = "entity.id") DTO map(Entity entity, @Context MappingContext context); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1148/Issue1148Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1148/Issue1148Mapper.java index fa997becf0..cf2cfdfaf4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1148/Issue1148Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1148/Issue1148Mapper.java @@ -19,26 +19,26 @@ public interface Issue1148Mapper { Issue1148Mapper INSTANCE = Mappers.getMapper( Issue1148Mapper.class ); @Mappings({ - @Mapping(source = "senderId", target = "sender.nestedClient.id"), - @Mapping(source = "recipientId", target = "recipient.nestedClient.id"), - @Mapping(source = "sameLevel.client.id", target = "client.nestedClient.id"), - @Mapping(source = "sameLevel2.client.id", target = "client2.nestedClient.id"), - @Mapping(source = "level.client.id", target = "nested.id"), - @Mapping(source = "level2.client.id", target = "nested2.id"), - @Mapping(source = "nestedDto.id", target = "id"), - @Mapping(source = "nestedDto2.id", target = "id2") + @Mapping(target = "sender.nestedClient.id", source = "senderId"), + @Mapping(target = "recipient.nestedClient.id", source = "recipientId"), + @Mapping(target = "client.nestedClient.id", source = "sameLevel.client.id"), + @Mapping(target = "client2.nestedClient.id", source = "sameLevel2.client.id"), + @Mapping(target = "nested.id", source = "level.client.id"), + @Mapping(target = "nested2.id", source = "level2.client.id"), + @Mapping(target = "id", source = "nestedDto.id"), + @Mapping(target = "id2", source = "nestedDto2.id") }) Entity toEntity(Entity.Dto dto); @Mappings({ - @Mapping(source = "dto2.senderId", target = "sender.nestedClient.id"), - @Mapping(source = "dto1.recipientId", target = "recipient.nestedClient.id"), - @Mapping(source = "dto1.sameLevel.client.id", target = "client.nestedClient.id"), - @Mapping(source = "dto2.sameLevel2.client.id", target = "client2.nestedClient.id"), - @Mapping(source = "dto1.level.client.id", target = "nested.id"), - @Mapping(source = "dto2.level2.client.id", target = "nested2.id"), - @Mapping(source = "dto1.nestedDto.id", target = "id"), - @Mapping(source = "dto2.nestedDto2.id", target = "id2") + @Mapping(target = "sender.nestedClient.id", source = "dto2.senderId"), + @Mapping(target = "recipient.nestedClient.id", source = "dto1.recipientId"), + @Mapping(target = "client.nestedClient.id", source = "dto1.sameLevel.client.id"), + @Mapping(target = "client2.nestedClient.id", source = "dto2.sameLevel2.client.id"), + @Mapping(target = "nested.id", source = "dto1.level.client.id"), + @Mapping(target = "nested2.id", source = "dto2.level2.client.id"), + @Mapping(target = "id", source = "dto1.nestedDto.id"), + @Mapping(target = "id2", source = "dto2.nestedDto2.id") }) Entity toEntity(Entity.Dto dto1, Entity.Dto dto2); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1155/Issue1155Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1155/Issue1155Mapper.java index cb2fd3453c..072121bf79 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1155/Issue1155Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1155/Issue1155Mapper.java @@ -17,6 +17,6 @@ public interface Issue1155Mapper { Issue1155Mapper INSTANCE = Mappers.getMapper( Issue1155Mapper.class ); - @Mapping(source = "clientId", target = "client.id") + @Mapping(target = "client.id", source = "clientId") Entity toEntity(Entity.Dto dto); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/AMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/AMapper.java index 860c3b21e3..ba6eeda861 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/AMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/AMapper.java @@ -15,12 +15,12 @@ public interface AMapper { AMapper INSTANCE = Mappers.getMapper( AMapper.class ); - @Mapping(source = "b.c", target = "cPrime") + @Mapping(target = "cPrime", source = "b.c") APrime toAPrime(A a, @MappingTarget APrime mappingTarget); CPrime toCPrime(C c, @MappingTarget CPrime mappingTarget); - @Mapping(source = "b.c", target = "cPrime") + @Mapping(target = "cPrime", source = "b.c") APrime toAPrime(A a); CPrime toCPrime(C c); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1685/UserMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1685/UserMapper.java index 38800dc757..4c838a3d55 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1685/UserMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1685/UserMapper.java @@ -21,11 +21,11 @@ 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") + @Mapping(target = "contactDataDTO.email", source = "email" ), + @Mapping(target = "contactDataDTO.phone", source = "phone"), + @Mapping(target = "contactDataDTO.address", source = "address"), + @Mapping(target = "contactDataDTO.preferences", source = "preferences"), + @Mapping(target = "contactDataDTO.settings", source = "settings") }) UserDTO userToUserDTO(User user); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1714/Issue1714Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1714/Issue1714Mapper.java index 0273457e8a..3985606d94 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1714/Issue1714Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1714/Issue1714Mapper.java @@ -15,7 +15,7 @@ public interface Issue1714Mapper { Issue1714Mapper INSTANCE = Mappers.getMapper( Issue1714Mapper.class ); - @Mapping(source = "programInstance", target = "seasonNumber", qualifiedByName = "getSeasonNumber") + @Mapping(target = "seasonNumber", source = "programInstance", qualifiedByName = "getSeasonNumber") OfferEntity map(OnDemand offerStatusDTO); @Named("getTitle") diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/VehicleDtoMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/VehicleDtoMapper.java index 34d9f77a75..79ccf8f3a0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/VehicleDtoMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/VehicleDtoMapper.java @@ -15,9 +15,9 @@ public interface VehicleDtoMapper { VehicleDtoMapper INSTANCE = Mappers.getMapper( VehicleDtoMapper.class ); - @Mapping(source = "name", target = "name") - @Mapping(source = "size", target = "vehicleProperties.size") - @Mapping(source = "type", target = "vehicleProperties.type") + @Mapping(target = "name", source = "name") + @Mapping(target = "vehicleProperties.size", source = "size") + @Mapping(target = "vehicleProperties.type", source = "type") VehicleDto map(Vehicle vehicle); class VehicleDto { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_394/SameNameForSourceAndTargetCarsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_394/SameNameForSourceAndTargetCarsMapper.java index 8cca546200..1d43087a51 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_394/SameNameForSourceAndTargetCarsMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_394/SameNameForSourceAndTargetCarsMapper.java @@ -21,7 +21,7 @@ public interface SameNameForSourceAndTargetCarsMapper { SameNameForSourceAndTargetCarsMapper INSTANCE = Mappers.getMapper( SameNameForSourceAndTargetCarsMapper.class ); @Mappings({ - @Mapping(source = "numberOfSeats", target = "seatCount") + @Mapping(target = "seatCount", source = "numberOfSeats") }) AnotherCar sourceCarToTargetCar(org.mapstruct.ap.test.bugs._394.source.AnotherCar car); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_405/PersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_405/PersonMapper.java index b9e15b1da9..4be1356aed 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_405/PersonMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_405/PersonMapper.java @@ -19,7 +19,7 @@ public abstract class PersonMapper { public static final PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class ); @Mappings( { - @Mapping( source = "id", target = "name" ) } + @Mapping(target = "name", source = "id") } ) abstract People personToPeople(Person person); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_846/Mapper846.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_846/Mapper846.java index f52f226ef1..5ce79afa03 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_846/Mapper846.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_846/Mapper846.java @@ -56,7 +56,7 @@ public void setaName(String name) { @Mapper interface MyMapper { - @Mapping(source = "name", target = "aName") + @Mapping(target = "aName", source = "name") A convert(BInterface b); @InheritInverseConfiguration diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_849/Issue849Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_849/Issue849Mapper.java index a54a3cb3bc..2891971dfd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_849/Issue849Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_849/Issue849Mapper.java @@ -18,7 +18,7 @@ public interface Issue849Mapper { Issue849Mapper INSTANCE = Mappers.getMapper( Issue849Mapper.class ); - @Mapping(source = "sourceList", target = "targetList") + @Mapping(target = "targetList", source = "sourceList") Target mapSourceToTarget(Source source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_891/BuggyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_891/BuggyMapper.java index ebc654bf16..fbb9fcd6c9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_891/BuggyMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_891/BuggyMapper.java @@ -18,6 +18,6 @@ public interface BuggyMapper { BuggyMapper INSTANCE = Mappers.getMapper( BuggyMapper.class ); - @Mapping(source = "nested.propInt", target = "propLong") + @Mapping(target = "propLong", source = "nested.propInt") Dest convert(Src src); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java index d716918d78..1a07dd1f78 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java @@ -21,13 +21,13 @@ public abstract class SourceTargetMapper { static final SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mappings({ - @Mapping(source = "integerList", target = "integerCollection"), - @Mapping(source = "integerSet", target = "set"), - @Mapping(source = "anotherIntegerSet", target = "anotherStringSet"), - @Mapping(source = "stringList2", target = "stringListNoSetter"), - @Mapping(source = "stringSet2", target = "stringListNoSetter2"), - @Mapping(source = "stringList3", target = "nonGenericStringList"), - @Mapping(source = "stringLongMapForNonGeneric", target = "nonGenericMapStringtoLong") + @Mapping(target = "integerCollection", source = "integerList"), + @Mapping(target = "set", source = "integerSet"), + @Mapping(target = "anotherStringSet", source = "anotherIntegerSet"), + @Mapping(target = "stringListNoSetter", source = "stringList2"), + @Mapping(target = "stringListNoSetter2", source = "stringSet2"), + @Mapping(target = "nonGenericStringList", source = "stringList3"), + @Mapping(target = "nonGenericMapStringtoLong", source = "stringLongMapForNonGeneric") }) public abstract Target sourceToTarget(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/SourceTargetMapper.java index 5e3017c19a..18b542e99d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/SourceTargetMapper.java @@ -42,6 +42,6 @@ public interface SourceTargetMapper { TargetViaTargetType toTargetViaTargetType(Source source); - @Mapping(source = "pet", target = "pets") + @Mapping(target = "pets", source = "pet") Target fromSingleElementSource(SingleElementSource source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapper.java index 1143a311ad..2f0d8085e8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapper.java @@ -26,7 +26,7 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); - @Mapping(source = "fooList", target = "fooListNoSetter") + @Mapping(target = "fooListNoSetter", source = "fooList") Target sourceToTarget(Source source); TargetFoo sourceFooToTargetFoo(SourceFoo sourceFoo); diff --git a/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapper.java index 20309ccae4..b635f6b023 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapper.java @@ -24,8 +24,8 @@ public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); @Mappings({ - @Mapping(source = "numberOfSeats", target = "seatCount"), - @Mapping(source = "manufacturingDate", target = "manufacturingYear") + @Mapping(target = "seatCount", source = "numberOfSeats"), + @Mapping(target = "manufacturingYear", source = "manufacturingDate") }) CarDto carToCarDto(Car car); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/SourceTargetMapper.java index ae5a4e9ce0..0bd3888809 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/SourceTargetMapper.java @@ -17,8 +17,8 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mappings({ - @Mapping(source = "qax", target = "baz"), - @Mapping(source = "baz", target = "qax") + @Mapping(target = "baz", source = "qax"), + @Mapping(target = "qax", source = "baz") }) Target sourceToTarget(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/localdatetimetoxmlgregoriancalendarconversion/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/localdatetimetoxmlgregoriancalendarconversion/SourceTargetMapper.java index cc456aa0a0..ed4c1d374d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/localdatetimetoxmlgregoriancalendarconversion/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/localdatetimetoxmlgregoriancalendarconversion/SourceTargetMapper.java @@ -17,9 +17,9 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); - @Mapping(source = "xmlGregorianCalendar", target = "localDateTime") + @Mapping(target = "localDateTime", source = "xmlGregorianCalendar") Target toTarget(Source source); - @Mapping(source = "localDateTime", target = "xmlGregorianCalendar") + @Mapping(target = "xmlGregorianCalendar", source = "localDateTime") Source toSource(Target target); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMapper1.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMapper1.java index ce9134cc9a..502ac44b1f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMapper1.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMapper1.java @@ -13,7 +13,7 @@ public interface ErroneousMapper1 { @Mappings({ - @Mapping(source = "source.foobar", target = "foo") + @Mapping(target = "foo", source = "source.foobar") }) Target sourceToTarget(Source source, DummySource source1); diff --git a/processor/src/test/java/org/mapstruct/ap/test/fields/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/fields/SourceTargetMapper.java index a872c25feb..99b7b50aa4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/fields/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/fields/SourceTargetMapper.java @@ -18,7 +18,7 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); - @Mapping(source = "fieldOnlyWithGetter", target = "fieldWithMethods") + @Mapping(target = "fieldWithMethods", source = "fieldOnlyWithGetter") Target toSource(Source source); @InheritInverseConfiguration diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/CustomerJsr330CompileOptionConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/CustomerJsr330CompileOptionConstructorMapper.java index 143c452886..02aeaef088 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/CustomerJsr330CompileOptionConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/CustomerJsr330CompileOptionConstructorMapper.java @@ -18,7 +18,7 @@ uses = GenderJsr330CompileOptionConstructorMapper.class ) public interface CustomerJsr330CompileOptionConstructorMapper { - @Mapping(source = "gender", target = "gender") + @Mapping(target = "gender", source = "gender") CustomerDto asTarget(CustomerEntity customerEntity); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/CustomerJsr330ConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/CustomerJsr330ConstructorMapper.java index 638e71a3aa..32ef9d308b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/CustomerJsr330ConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/CustomerJsr330ConstructorMapper.java @@ -20,7 +20,7 @@ injectionStrategy = InjectionStrategy.CONSTRUCTOR ) public interface CustomerJsr330ConstructorMapper { - @Mapping(source = "gender", target = "gender") + @Mapping(target = "gender", source = "gender") CustomerDto asTarget(CustomerEntity customerEntity); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerSpringCompileOptionConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerSpringCompileOptionConstructorMapper.java index 47060104f7..3d28e3e49e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerSpringCompileOptionConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/CustomerSpringCompileOptionConstructorMapper.java @@ -18,6 +18,6 @@ uses = GenderSpringCompileOptionConstructorMapper.class) public interface CustomerSpringCompileOptionConstructorMapper { - @Mapping( source = "gender", target = "gender" ) + @Mapping(target = "gender", source = "gender") CustomerDto asTarget(CustomerEntity customerEntity); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerSpringConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerSpringConstructorMapper.java index 4ea869ec5f..ef7120585d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerSpringConstructorMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/CustomerSpringConstructorMapper.java @@ -20,6 +20,6 @@ injectionStrategy = InjectionStrategy.CONSTRUCTOR ) public interface CustomerSpringConstructorMapper { - @Mapping( source = "gender", target = "gender" ) + @Mapping(target = "gender", source = "gender") CustomerDto asTarget(CustomerEntity customerEntity); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/SourceTargetMapper.java index 8dc5c93484..86d83844e8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/SourceTargetMapper.java @@ -22,13 +22,13 @@ public abstract class SourceTargetMapper { static final SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mappings({ - @Mapping(source = "stringStream", target = "stringList"), - @Mapping(source = "stringArrayStream", target = "stringArrayList"), - @Mapping(source = "stringStreamToSet", target = "stringSet"), - @Mapping(source = "integerStream", target = "integerCollection"), - @Mapping(source = "anotherIntegerStream", target = "anotherStringSet"), - @Mapping(source = "stringStream2", target = "stringListNoSetter"), - @Mapping(source = "stringStream3", target = "nonGenericStringList") + @Mapping(target = "stringList", source = "stringStream"), + @Mapping(target = "stringArrayList", source = "stringArrayStream"), + @Mapping(target = "stringSet", source = "stringStreamToSet"), + @Mapping(target = "integerCollection", source = "integerStream"), + @Mapping(target = "anotherStringSet", source = "anotherIntegerStream"), + @Mapping(target = "stringListNoSetter", source = "stringStream2"), + @Mapping(target = "nonGenericStringList", source = "stringStream3") }) public abstract Target sourceToTarget(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamMapper.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamMapper.java index edd6b87d19..71a582845c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamMapper.java @@ -21,8 +21,8 @@ public interface StreamMapper { StreamMapper INSTANCE = Mappers.getMapper( StreamMapper.class ); @Mappings( { - @Mapping( source = "stream", target = "targetStream"), - @Mapping( source = "sourceElements", target = "targetElements") + @Mapping(target = "targetStream", source = "stream"), + @Mapping(target = "targetElements", source = "sourceElements") } ) Target map(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/SourceTargetMapper.java index 77d24916f5..b027dc9a0e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/SourceTargetMapper.java @@ -22,7 +22,7 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); - @Mapping(source = "fooStream", target = "fooListNoSetter") + @Mapping(target = "fooListNoSetter", source = "fooStream") Target sourceToTarget(Source source); TargetFoo sourceFooToTargetFoo(SourceFoo sourceFoo); diff --git a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonAgeMapper.java b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonAgeMapper.java index 70c1b13c83..9cf3de5e74 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonAgeMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonAgeMapper.java @@ -16,7 +16,7 @@ public interface PersonAgeMapper { PersonAgeMapper MAPPER = Mappers.getMapper( PersonAgeMapper.class ); - @Mapping(source = "agee", target = "fullAge") + @Mapping(target = "fullAge", source = "agee") Person mapPerson(PersonDto dto); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonGarageWrongTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonGarageWrongTargetMapper.java index 8a7805237a..fc587e0b71 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonGarageWrongTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonGarageWrongTargetMapper.java @@ -16,9 +16,9 @@ public interface PersonGarageWrongTargetMapper { PersonGarageWrongTargetMapper MAPPER = Mappers.getMapper( PersonGarageWrongTargetMapper.class ); - @Mapping(source = "garage.color.rgb", target = "garage.colour.rgb") + @Mapping(target = "garage.colour.rgb", source = "garage.color.rgb") Person mapPerson(PersonDto dto); - @Mapping(source = "garage.color", target = "garage.colour") + @Mapping(target = "garage.colour", source = "garage.color") Person mapPersonGarage(PersonDto dto); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonNameMapper.java index 11bd389a5f..b14a0d0cf9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonNameMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/erroneous/PersonNameMapper.java @@ -16,7 +16,7 @@ public interface PersonNameMapper { PersonNameMapper MAPPER = Mappers.getMapper( PersonNameMapper.class ); - @Mapping(source = "name", target = "fulName") + @Mapping(target = "fulName", source = "name") Person mapPerson(PersonDto dto); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/SourceTypeTargetDtoMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/SourceTypeTargetDtoMapper.java index 87371f004f..895d3dc922 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/SourceTypeTargetDtoMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/SourceTypeTargetDtoMapper.java @@ -17,7 +17,7 @@ public interface SourceTypeTargetDtoMapper { SourceTypeTargetDtoMapper INSTANCE = Mappers.getMapper( SourceTypeTargetDtoMapper.class ); - @Mapping(source = "date", target = "date", dateFormat = "dd.MM.yyyy") + @Mapping(target = "date", source = "date", dateFormat = "dd.MM.yyyy") TargetDto sourceToTarget(SourceType source); SourceType targetToSource( TargetDto source ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsource/exceptions/ResourceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsource/exceptions/ResourceMapper.java index b1dcd8cd04..712d160908 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsource/exceptions/ResourceMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsource/exceptions/ResourceMapper.java @@ -15,7 +15,7 @@ @Mapper public interface ResourceMapper { - @Mapping(source = "bucket.user.uuid", target = "userId") + @Mapping(target = "userId", source = "bucket.user.uuid") ResourceDto map(Resource r) throws NoSuchUser; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDefaultMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDefaultMapper.java index 362a129991..1f95649dd8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDefaultMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDefaultMapper.java @@ -16,10 +16,10 @@ public interface CustomerDefaultMapper { CustomerDefaultMapper INSTANCE = Mappers.getMapper( CustomerDefaultMapper.class ); - @Mapping(source = "address", target = "homeDTO.addressDTO") + @Mapping(target = "homeDTO.addressDTO", source = "address") void mapCustomer(Customer customer, @MappingTarget UserDTO userDTO); - @Mapping(source = "houseNumber", target = "houseNo", defaultValue = "0") + @Mapping(target = "houseNo", defaultValue = "0", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerMapper.java index 56b930c51a..339f70516b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerMapper.java @@ -16,10 +16,10 @@ public interface CustomerMapper { CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class ); - @Mapping(source = "address", target = "homeDTO.addressDTO") + @Mapping(target = "homeDTO.addressDTO", source = "address") void mapCustomer(Customer customer, @MappingTarget UserDTO userDTO); - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnBeanMappingMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnBeanMappingMethodMapper.java index 7801084639..85a685b943 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnBeanMappingMethodMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnBeanMappingMethodMapper.java @@ -21,7 +21,7 @@ public interface CustomerNvpmsOnBeanMappingMethodMapper { void map(Customer customer, @MappingTarget CustomerDTO mappingTarget); @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnConfigMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnConfigMapper.java index 941b7d2b43..7693c2c969 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnConfigMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnConfigMapper.java @@ -17,7 +17,7 @@ public interface CustomerNvpmsOnConfigMapper { void map(Customer customer, @MappingTarget CustomerDTO mappingTarget); - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnMapperMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnMapperMapper.java index 592a687c6e..1b65c6d5fc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnMapperMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnMapperMapper.java @@ -18,7 +18,7 @@ public interface CustomerNvpmsOnMapperMapper { void map(Customer customer, @MappingTarget CustomerDTO mappingTarget); - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsPropertyMappingMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsPropertyMappingMapper.java index 28eaf8f337..a6b3b021fa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsPropertyMappingMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsPropertyMappingMapper.java @@ -21,7 +21,7 @@ public interface CustomerNvpmsPropertyMappingMapper { @Mapping( target = "details", nullValuePropertyMappingStrategy = IGNORE) void map(Customer customer, @MappingTarget CustomerDTO mappingTarget); - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper1.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper1.java index 3c2dd51344..e025cfd86c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper1.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper1.java @@ -21,7 +21,7 @@ public interface ErroneousCustomerMapper1 { @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE) void map(Customer customer, @MappingTarget CustomerDTO mappingTarget); - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper2.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper2.java index 4b7f2bec75..fad47afb27 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper2.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper2.java @@ -21,7 +21,7 @@ public interface ErroneousCustomerMapper2 { @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE) void map(Customer customer, @MappingTarget CustomerDTO mappingTarget); - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper3.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper3.java index 755b468539..1a0eae5f4f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper3.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper3.java @@ -21,7 +21,7 @@ public interface ErroneousCustomerMapper3 { @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE) void map(Customer customer, @MappingTarget CustomerDTO mappingTarget); - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper4.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper4.java index 88fe1a7cbf..da54f8c9b5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper4.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper4.java @@ -21,7 +21,7 @@ public interface ErroneousCustomerMapper4 { @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE) void map(Customer customer, @MappingTarget CustomerDTO mappingTarget); - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper5.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper5.java index 05ed1c1fe6..fc2861bc15 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper5.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper5.java @@ -21,7 +21,7 @@ public interface ErroneousCustomerMapper5 { @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE) void map(Customer customer, @MappingTarget CustomerDTO mappingTarget); - @Mapping(source = "houseNumber", target = "houseNo") + @Mapping(target = "houseNo", source = "houseNumber") void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/oneway/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/oneway/SourceTargetMapper.java index efc62ee6ea..e8cf4c5681 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/oneway/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/oneway/SourceTargetMapper.java @@ -14,7 +14,7 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); - @Mapping(source = "qax", target = "qux") + @Mapping(target = "qux", source = "qax") Target sourceToTarget(Source source); Source targetToSource(Target target); diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapper.java index 79d0ed43ad..014eeba1e3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/reverse/SourceTargetMapper.java @@ -20,16 +20,16 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mappings({ - @Mapping(source = "stringPropX", target = "stringPropY"), - @Mapping(source = "integerPropX", target = "integerPropY"), - @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream") + @Mapping(target = "stringPropY", source = "stringPropX"), + @Mapping(target = "integerPropY", source = "integerPropX"), + @Mapping(target = "propertyNotToIgnoreUpstream", source = "propertyToIgnoreDownstream") }) Target forward(Source source); @Mappings({ - @Mapping(source = "stringPropX", target = "stringPropY"), - @Mapping(source = "integerPropX", target = "integerPropY"), - @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream") + @Mapping(target = "stringPropY", source = "stringPropX"), + @Mapping(target = "integerPropY", source = "integerPropX"), + @Mapping(target = "propertyNotToIgnoreUpstream", source = "propertyToIgnoreDownstream") }) Target forwardNotToReverse(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous1.java b/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous1.java index de2da07ece..d01f54a3a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous1.java +++ b/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous1.java @@ -22,16 +22,16 @@ public interface SourceTargetMapperAmbiguous1 { SourceTargetMapperAmbiguous1 INSTANCE = Mappers.getMapper( SourceTargetMapperAmbiguous1.class ); @Mappings({ - @Mapping(source = "stringPropX", target = "stringPropY"), - @Mapping(source = "integerPropX", target = "integerPropY"), - @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream") + @Mapping(target = "stringPropY", source = "stringPropX"), + @Mapping(target = "integerPropY", source = "integerPropX"), + @Mapping(target = "propertyNotToIgnoreUpstream", source = "propertyToIgnoreDownstream") }) Target forward(Source source); @Mappings({ - @Mapping(source = "stringPropX", target = "stringPropY"), - @Mapping(source = "integerPropX", target = "integerPropY"), - @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream") + @Mapping(target = "stringPropY", source = "stringPropX"), + @Mapping(target = "integerPropY", source = "integerPropX"), + @Mapping(target = "propertyNotToIgnoreUpstream", source = "propertyToIgnoreDownstream") }) Target forwardNotToReverse(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous2.java b/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous2.java index 25a1939b67..fd2ef5ba9e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous2.java +++ b/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous2.java @@ -22,16 +22,16 @@ public interface SourceTargetMapperAmbiguous2 { SourceTargetMapperAmbiguous2 INSTANCE = Mappers.getMapper( SourceTargetMapperAmbiguous2.class ); @Mappings({ - @Mapping(source = "stringPropX", target = "stringPropY"), - @Mapping(source = "integerPropX", target = "integerPropY"), - @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream") + @Mapping(target = "stringPropY", source = "stringPropX"), + @Mapping(target = "integerPropY", source = "integerPropX"), + @Mapping(target = "propertyNotToIgnoreUpstream", source = "propertyToIgnoreDownstream") }) Target forward(Source source); @Mappings({ - @Mapping(source = "stringPropX", target = "stringPropY"), - @Mapping(source = "integerPropX", target = "integerPropY"), - @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream") + @Mapping(target = "stringPropY", source = "stringPropX"), + @Mapping(target = "integerPropY", source = "integerPropX"), + @Mapping(target = "propertyNotToIgnoreUpstream", source = "propertyToIgnoreDownstream") }) Target forwardNotToReverse(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous3.java b/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous3.java index e12505b42d..b7d13a32f9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous3.java +++ b/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperAmbiguous3.java @@ -23,16 +23,16 @@ public interface SourceTargetMapperAmbiguous3 { SourceTargetMapperAmbiguous3 INSTANCE = Mappers.getMapper( SourceTargetMapperAmbiguous3.class ); @Mappings({ - @Mapping(source = "stringPropX", target = "stringPropY"), - @Mapping(source = "integerPropX", target = "integerPropY"), - @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream") + @Mapping(target = "stringPropY", source = "stringPropX"), + @Mapping(target = "integerPropY", source = "integerPropX"), + @Mapping(target = "propertyNotToIgnoreUpstream", source = "propertyToIgnoreDownstream") }) Target forward(Source source); @Mappings({ - @Mapping(source = "stringPropX", target = "stringPropY"), - @Mapping(source = "integerPropX", target = "integerPropY"), - @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream") + @Mapping(target = "stringPropY", source = "stringPropX"), + @Mapping(target = "integerPropY", source = "integerPropX"), + @Mapping(target = "propertyNotToIgnoreUpstream", source = "propertyToIgnoreDownstream") }) Target forward(Source source, @MappingTarget Target target); diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperNonMatchingName.java b/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperNonMatchingName.java index 970b24a721..4fb0afa5e8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperNonMatchingName.java +++ b/processor/src/test/java/org/mapstruct/ap/test/reverse/erroneous/SourceTargetMapperNonMatchingName.java @@ -22,9 +22,9 @@ public interface SourceTargetMapperNonMatchingName { SourceTargetMapperNonMatchingName INSTANCE = Mappers.getMapper( SourceTargetMapperNonMatchingName.class ); @Mappings({ - @Mapping(source = "stringPropX", target = "stringPropY"), - @Mapping(source = "integerPropX", target = "integerPropY"), - @Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream") + @Mapping(target = "stringPropY", source = "stringPropX"), + @Mapping(target = "integerPropY", source = "integerPropX"), + @Mapping(target = "propertyNotToIgnoreUpstream", source = "propertyToIgnoreDownstream") }) Target forward(Source source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ErroneousMapper1.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ErroneousMapper1.java index 6d1990bd0d..8c52cb7ade 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ErroneousMapper1.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ErroneousMapper1.java @@ -21,7 +21,7 @@ public interface ErroneousMapper1 { @Mappings({ @Mapping(target = "stringConstant", constant = "stringConstant"), @Mapping(target = "emptyStringConstant", constant = ""), - @Mapping(source = "test", target = "integerConstant", constant = "14"), + @Mapping(target = "integerConstant", source = "test", constant = "14"), @Mapping(target = "longWrapperConstant", constant = "3001L"), @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"), @Mapping(target = "nameConstants", constant = "jack-jill-tom"), diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ErroneousMapper4.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ErroneousMapper4.java index 3ff8bd1b88..9d40a0683c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ErroneousMapper4.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ErroneousMapper4.java @@ -21,7 +21,7 @@ public interface ErroneousMapper4 { @Mappings({ @Mapping(target = "stringConstant", constant = "stringConstant"), @Mapping(target = "emptyStringConstant", constant = ""), - @Mapping(source = "test", target = "integerConstant", expression = "java('test')"), + @Mapping(target = "integerConstant", source = "test", expression = "java('test')"), @Mapping(target = "longWrapperConstant", constant = "3001L"), @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"), @Mapping(target = "nameConstants", constant = "jack-jill-tom"), diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceTargetMapperSeveralSources.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceTargetMapperSeveralSources.java index 6e9a63e6d2..51a59783ba 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceTargetMapperSeveralSources.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceTargetMapperSeveralSources.java @@ -19,8 +19,8 @@ public interface SourceTargetMapperSeveralSources { SourceTargetMapperSeveralSources INSTANCE = Mappers.getMapper( SourceTargetMapperSeveralSources.class ); @Mappings({ - @Mapping(source = "s1.someProp", target = "someProp" ), - @Mapping(source = "s2.anotherProp", target = "anotherProp" ), + @Mapping(target = "someProp", source = "s1.someProp"), + @Mapping(target = "anotherProp", source = "s2.anotherProp"), @Mapping(target = "someConstant", constant = "stringConstant"), }) Target2 sourceToTarget(Source1 s1, Source2 s2); diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/SourceTargetMapperSeveralSources.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/SourceTargetMapperSeveralSources.java index 5c57b154a8..ea37eafeca 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/SourceTargetMapperSeveralSources.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/SourceTargetMapperSeveralSources.java @@ -20,8 +20,8 @@ public interface SourceTargetMapperSeveralSources { SourceTargetMapperSeveralSources INSTANCE = Mappers.getMapper( SourceTargetMapperSeveralSources.class ); @Mappings( { - @Mapping( target = "timeAndFormat", expression = "java( new TimeAndFormat( s.getTime(), s.getFormat() ))" ), - @Mapping( source = "s1.anotherProp", target = "anotherProp" ) + @Mapping(target = "timeAndFormat", expression = "java( new TimeAndFormat( s.getTime(), s.getFormat() ))"), + @Mapping(target = "anotherProp", source = "s1.anotherProp") } ) Target sourceToTarget( Source s, Source2 s1 ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/SourceTargetConfig.java b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/SourceTargetConfig.java index b64186e6bd..c4fbd1eb9b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/SourceTargetConfig.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/SourceTargetConfig.java @@ -11,8 +11,8 @@ @MapperConfig public interface SourceTargetConfig { - @Mapping(source = "address.houseNo", target = "houseNumber") - @Mapping(source = "person.description", target = "description") + @Mapping(target = "houseNumber", source = "address.houseNo") + @Mapping(target = "description", source = "person.description") DeliveryAddress personAndAddressToDeliveryAddress(Person person, Address address); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/SourceTargetMapper.java index f9289dc99d..78f1654c48 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/SourceTargetMapper.java @@ -17,19 +17,19 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mappings({ - @Mapping(source = "address.houseNo", target = "houseNumber"), - @Mapping(source = "person.description", target = "description") + @Mapping(target = "houseNumber", source = "address.houseNo"), + @Mapping(target = "description", source = "person.description") }) DeliveryAddress personAndAddressToDeliveryAddress(Person person, Address address); @Mappings({ - @Mapping(source = "address.houseNo", target = "houseNumber"), - @Mapping(source = "person.description", target = "description") + @Mapping(target = "houseNumber", source = "address.houseNo"), + @Mapping(target = "description", source = "person.description") }) void personAndAddressToDeliveryAddress(Person person, Address address, @MappingTarget DeliveryAddress deliveryAddress); - @Mapping( target = "description", source = "person.description") + @Mapping(target = "description", source = "person.description") DeliveryAddress personAndAddressToDeliveryAddress(Person person, Integer houseNumber, int zipCode, String street); diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/manytargetproperties/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/manytargetproperties/SourceTargetMapper.java index 06084b4e6e..a3dff45d1d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/manytargetproperties/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/manytargetproperties/SourceTargetMapper.java @@ -19,10 +19,10 @@ public interface SourceTargetMapper { @Mappings( { - @Mapping(source = "name", target = "name1"), - @Mapping(source = "name", target = "name2"), - @Mapping(source = "timeAndFormat", target = "time"), - @Mapping(source = "timeAndFormat", target = "format") + @Mapping(target = "name1", source = "name"), + @Mapping(target = "name2", source = "name"), + @Mapping(target = "time", source = "timeAndFormat"), + @Mapping(target = "format", source = "timeAndFormat") }) Target sourceToTarget(Source s); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper1.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper1.java index 126e6801f0..fbc7de9064 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper1.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper1.java @@ -25,12 +25,12 @@ public interface ErroneousOrganizationMapper1 { void toCompanyEntity(CompanyDto dto, @MappingTarget CompanyEntity entity); - @Mapping(source = "type", target = "type") + @Mapping(target = "type", source = "type") void toName(String type, @MappingTarget OrganizationTypeEntity entity); @Mappings({ - @Mapping( target = "employees", ignore = true ), - @Mapping( target = "secretaryToEmployee", ignore = true ) + @Mapping(target = "employees", ignore = true ), + @Mapping(target = "secretaryToEmployee", ignore = true ) }) DepartmentEntity toDepartmentEntity(DepartmentDto dto); diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper2.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper2.java index f57e4c95a4..dff339d9a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper2.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/ErroneousOrganizationMapper2.java @@ -25,7 +25,7 @@ public interface ErroneousOrganizationMapper2 { void toCompanyEntity(CompanyDto dto, @MappingTarget CompanyEntity entity); - @Mapping(source = "type", target = "type") + @Mapping(target = "type", source = "type") void toName(String type, @MappingTarget OrganizationTypeEntity entity); @Mappings({ diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/OrganizationMapper.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/OrganizationMapper.java index d2b71b439e..4f2715d0db 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/OrganizationMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/OrganizationMapper.java @@ -29,14 +29,14 @@ public interface OrganizationMapper { void toCompanyEntity(CompanyDto dto, @MappingTarget CompanyEntity entity); @Mappings({ - @Mapping( target = "employees", ignore = true ), - @Mapping( target = "secretaryToEmployee", ignore = true ) + @Mapping(target = "employees", ignore = true ), + @Mapping(target = "secretaryToEmployee", ignore = true ) }) DepartmentEntity toDepartmentEntity(DepartmentDto dto); - @Mapping(source = "type", target = "type") + @Mapping(target = "type", source = "type") void toName(String type, @MappingTarget OrganizationTypeEntity entity); - @Mapping(source = "number", target = "number") + @Mapping(target = "number", source = "number") void toNumber(Integer number, @MappingTarget OrganizationTypeNrEntity entity); } From 228660c74f4c863a07bb850c292dfba74b254a2e Mon Sep 17 00:00:00 2001 From: Jude Niroshan Date: Sun, 7 Mar 2021 14:11:45 +0100 Subject: [PATCH 144/597] #2366 Update documentation in regards to Java Module System --- .../main/asciidoc/chapter-2-set-up.asciidoc | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 76626f44ce..fd27b25e42 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -248,20 +248,8 @@ If a policy is given for a specific mapper via `@Mapper#unmappedTargetPolicy()`, |`WARN` |=== -=== Using MapStruct on Java 9 +=== Using MapStruct with the Java Module System -MapStruct can be used with Java 9 (JPMS), support for it is experimental. +MapStruct can be used with Java 9 and higher versions. -A core theme of Java 9 is the modularization of the JDK. One effect of this is that a specific module needs to be enabled for a project in order to use the `javax.annotation.Generated` annotation. `@Generated` is added by MapStruct to generated mapper classes to tag them as generated code, stating the date of generation, the generator version etc. - -To allow usage of the `@Generated` annotation the module _java.xml.ws.annotation_ must be enabled. When using Maven, this can be done like this: - - export MAVEN_OPTS="--add-modules java.xml.ws.annotation" - -If the `@Generated` annotation is not available, MapStruct will detect this situation and not add it to generated mappers. - -[NOTE] -===== -In Java 9 `java.annotation.processing.Generated` was added (part of the `java.compiler` module), -if this annotation is available then it will be used. -===== +To allow usage of the `@Generated` annotation `java.annotation.processing.Generated` (part of the `java.compiler` module) can be enabled. From c4135e68ed8330fbc5180abd828e35b30f606ece Mon Sep 17 00:00:00 2001 From: "jude.niroshan11@gmail.com" Date: Fri, 19 Feb 2021 23:06:42 +0100 Subject: [PATCH 145/597] #2339 Support throwing an exception as an Enum Mapping option --- .../java/org/mapstruct/MappingConstants.java | 8 ++ .../main/java/org/mapstruct/ValueMapping.java | 27 +++++- .../ap/internal/gem/MappingConstantsGem.java | 2 + .../ap/internal/model/ValueMappingMethod.java | 82 +++++++++++++--- .../model/source/ValueMappingOptions.java | 3 +- .../mapstruct/ap/internal/util/Message.java | 3 +- .../ap/internal/model/ValueMappingMethod.ftl | 10 +- .../DefaultOrderThrowExceptionMapper.java | 26 ++++++ .../enum2enum/EnumToEnumMappingTest.java | 30 +++--- .../EnumToEnumThrowExceptionMappingTest.java | 93 +++++++++++++++++++ ...OrderMapperThrowExceptionAsSourceType.java | 32 +++++++ .../enum2enum/OrderThrowExceptionMapper.java | 35 +++++++ .../value/enum2enum/SpecialOrderMapper.java | 8 +- .../SpecialThrowExceptionMapper.java | 47 ++++++++++ .../enum2enum/SpecialOrderMapperImpl.java | 4 +- 15 files changed, 368 insertions(+), 42 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderThrowExceptionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperThrowExceptionAsSourceType.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderThrowExceptionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialThrowExceptionMapper.java diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java index 6e30f08c21..53528f559e 100644 --- a/core/src/main/java/org/mapstruct/MappingConstants.java +++ b/core/src/main/java/org/mapstruct/MappingConstants.java @@ -36,6 +36,14 @@ private MappingConstants() { */ public static final String ANY_UNMAPPED = ""; + /** + * In an {@link ValueMapping} this represents any target that will be mapped to an + * {@link java.lang.IllegalArgumentException} which will be thrown at runtime. + *

    + * NOTE: The value is only applicable to {@link ValueMapping#target()} and not to {@link ValueMapping#source()}. + */ + public static final String THROW_EXCEPTION = ""; + /** * In an {@link EnumMapping} this represent the enum transformation strategy that adds a suffix to the source enum. * diff --git a/core/src/main/java/org/mapstruct/ValueMapping.java b/core/src/main/java/org/mapstruct/ValueMapping.java index f8f2d3b18e..2d4a586366 100644 --- a/core/src/main/java/org/mapstruct/ValueMapping.java +++ b/core/src/main/java/org/mapstruct/ValueMapping.java @@ -23,7 +23,8 @@ * *

      * 
    - * public enum OrderType { RETAIL, B2B, EXTRA, STANDARD, NORMAL }
    + *
    + * public enum OrderType { RETAIL, B2B, C2C, EXTRA, STANDARD, NORMAL }
      *
      * public enum ExternalOrderType { RETAIL, B2B, SPECIAL, DEFAULT }
      *
    @@ -45,13 +46,12 @@
      * +---------------------+----------------------------+
      * 
    * - * MapStruct will WARN on incomplete mappings. However, if for some reason no match is found an - * {@link java.lang.IllegalStateException} will be thrown. *

    * Example 2: * *

      * 
    + *
      * @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ),
      * @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ),
      * @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" )
    @@ -70,6 +70,26 @@
      * +---------------------+----------------------------+
      * 
    * + *

    + * Example 3: + *

    + * + *

    MapStruct will WARN on incomplete mappings. However, if for some reason no match is found, an + * {@link java.lang.IllegalStateException} will be thrown. This compile-time error can be avoided by + * using {@link MappingConstants#THROW_EXCEPTION} for {@link ValueMapping#target()}. It will result an + * {@link java.lang.IllegalArgumentException} at runtime. + *
    + * 
    + *
    + * @ValueMapping( source = "STANDARD", target = "DEFAULT" ),
    + * @ValueMapping( source = "C2C", target = MappingConstants.THROW_EXCEPTION )
    + * ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
    + * 
    + * Mapping result:
    + * {@link java.lang.IllegalArgumentException} with the error message:
    + * Unexpected enum constant: C2C
    + * 
    + * * @author Sjaak Derksen */ @Repeatable(ValueMappings.class) @@ -104,6 +124,7 @@ *
      *
    1. enum constant name
    2. *
    3. {@link MappingConstants#NULL}
    4. + *
    5. {@link MappingConstants#THROW_EXCEPTION}
    6. *
    * * @return The target value. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java index 5df7fd3f92..be8f23b76d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java @@ -21,6 +21,8 @@ private MappingConstantsGem() { public static final String ANY_UNMAPPED = ""; + public static final String THROW_EXCEPTION = ""; + public static final String SUFFIX_TRANSFORMATION = "suffix"; public static final String STRIP_SUFFIX_TRANSFORMATION = "stripSuffix"; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 627a4b7288..e292d38e3a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -14,7 +14,6 @@ import java.util.Set; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; -import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.gem.BeanMappingGem; import org.mapstruct.ap.internal.model.common.Parameter; @@ -25,11 +24,13 @@ import org.mapstruct.ap.internal.model.source.ValueMappingOptions; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.spi.EnumTransformationStrategy; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_REMAINING; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_UNMAPPED; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.NULL; +import static org.mapstruct.ap.internal.gem.MappingConstantsGem.THROW_EXCEPTION; import static org.mapstruct.ap.internal.util.Collections.first; /** @@ -43,6 +44,8 @@ public class ValueMappingMethod extends MappingMethod { private final List valueMappings; private final String defaultTarget; private final String nullTarget; + private boolean nullAsException; + private boolean defaultAsException; private final Type unexpectedValueMappingException; @@ -119,10 +122,12 @@ else if ( sourceType.isString() && targetType.isEnumType() ) { return new ValueMappingMethod( method, mappingEntries, valueMappings.nullValueTarget, + valueMappings.hasNullValueAsException, valueMappings.defaultTargetValue, determineUnexpectedValueMappingException(), beforeMappingMethods, - afterMappingMethods + afterMappingMethods, + determineExceptionMappingForDefaultCase() ); } @@ -313,7 +318,17 @@ private boolean reportErrorIfMappedSourceEnumConstantsDontExist(Method method, T for ( ValueMappingOptions mappedConstant : valueMappings.regularValueMappings ) { - if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { + if ( !enumMapping.isInverse() && THROW_EXCEPTION.equals( mappedConstant.getSource() ) ) { + ctx.getMessager().printMessage( + method.getExecutable(), + mappedConstant.getMirror(), + mappedConstant.getSourceAnnotationValue(), + Message.VALUEMAPPING_THROW_EXCEPTION_SOURCE + ); + foundIncorrectMapping = true; + } + else if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { + ctx.getMessager().printMessage( method.getExecutable(), mappedConstant.getMirror(), @@ -361,6 +376,7 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T for ( ValueMappingOptions mappedConstant : valueMappings.regularValueMappings ) { if ( !NULL.equals( mappedConstant.getTarget() ) + && !THROW_EXCEPTION.equals( mappedConstant.getTarget() ) && !targetEnumConstants.contains( mappedConstant.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -374,7 +390,9 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T } } - if ( valueMappings.defaultTarget != null && !NULL.equals( valueMappings.defaultTarget.getTarget() ) + if ( valueMappings.defaultTarget != null + && !THROW_EXCEPTION.equals( valueMappings.defaultTarget.getTarget() ) + && !NULL.equals( valueMappings.defaultTarget.getTarget() ) && !targetEnumConstants.contains( valueMappings.defaultTarget.getTarget() ) ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -415,7 +433,9 @@ else if ( valueMappings.nullTarget == null && valueMappings.nullValueTarget != n } private Type determineUnexpectedValueMappingException() { - if ( !valueMappings.hasDefaultValue ) { + boolean noDefaultValueForSwitchCase = !valueMappings.hasDefaultValue; + if ( noDefaultValueForSwitchCase || valueMappings.hasAtLeastOneExceptionValue + || valueMappings.hasNullValueAsException ) { TypeMirror unexpectedValueMappingException = enumMapping.getUnexpectedValueMappingException(); if ( unexpectedValueMappingException != null ) { return ctx.getTypeFactory().getType( unexpectedValueMappingException ); @@ -427,6 +447,15 @@ private Type determineUnexpectedValueMappingException() { return null; } + + private boolean determineExceptionMappingForDefaultCase() { + if ( valueMappings.hasDefaultValue ) { + return THROW_EXCEPTION.equals( valueMappings.defaultTargetValue ); + } + else { + return true; + } + } } private static class EnumTransformationStrategyInvoker { @@ -464,7 +493,8 @@ private static class ValueMappings { boolean hasMapAnyUnmapped = false; boolean hasMapAnyRemaining = false; boolean hasDefaultValue = false; - boolean hasNullValue = false; + boolean hasNullValueAsException = false; + boolean hasAtLeastOneExceptionValue = false; ValueMappings(List valueMappings) { @@ -484,11 +514,17 @@ else if ( ANY_UNMAPPED.equals( valueMapping.getSource() ) ) { else if ( NULL.equals( valueMapping.getSource() ) ) { nullTarget = valueMapping; nullValueTarget = getValue( nullTarget ); - hasNullValue = true; + if ( THROW_EXCEPTION.equals( nullValueTarget ) ) { + hasNullValueAsException = true; + } } else { regularValueMappings.add( valueMapping ); } + + if ( THROW_EXCEPTION.equals( valueMapping.getTarget() ) ) { + hasAtLeastOneExceptionValue = true; + } } } @@ -497,14 +533,20 @@ String getValue(ValueMappingOptions valueMapping) { } } - private ValueMappingMethod(Method method, List enumMappings, String nullTarget, String defaultTarget, - Type unexpectedValueMappingException, - List beforeMappingMethods, - List afterMappingMethods) { + private ValueMappingMethod(Method method, + List enumMappings, + String nullTarget, + boolean hasNullTargetAsException, + String defaultTarget, + Type unexpectedValueMappingException, + List beforeMappingMethods, + List afterMappingMethods, boolean defaultAsException) { super( method, beforeMappingMethods, afterMappingMethods ); this.valueMappings = enumMappings; this.nullTarget = nullTarget; + this.nullAsException = hasNullTargetAsException; this.defaultTarget = defaultTarget; + this.defaultAsException = defaultAsException; this.unexpectedValueMappingException = unexpectedValueMappingException; this.overridden = method.overridesMethod(); } @@ -532,10 +574,18 @@ public String getNullTarget() { return nullTarget; } + public boolean isNullAsException() { + return nullAsException; + } + public Type getUnexpectedValueMappingException() { return unexpectedValueMappingException; } + public boolean isDefaultAsException() { + return defaultAsException; + } + public Parameter getSourceParameter() { return first( getSourceParameters() ); } @@ -547,17 +597,25 @@ public boolean isOverridden() { public static class MappingEntry { private final String source; private final String target; + private boolean targetAsException = false; - MappingEntry( String source, String target ) { + MappingEntry(String source, String target) { this.source = source; if ( !NULL.equals( target ) ) { this.target = target; + if ( THROW_EXCEPTION.equals( target ) ) { + this.targetAsException = true; + } } else { this.target = null; } } + public boolean isTargetAsException() { + return targetAsException; + } + public String getSource() { return source; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java index 10aef4bfa8..963683b5c5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java @@ -18,6 +18,7 @@ import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_REMAINING; import static org.mapstruct.ap.internal.gem.MappingConstantsGem.ANY_UNMAPPED; +import static org.mapstruct.ap.internal.gem.MappingConstantsGem.THROW_EXCEPTION; /** * Represents the mapping between one value constant and another. @@ -112,7 +113,7 @@ public AnnotationValue getTargetAnnotationValue() { public ValueMappingOptions inverse() { ValueMappingOptions result; - if ( !(ANY_REMAINING.equals( source ) || ANY_UNMAPPED.equals( source ) ) ) { + if ( !(ANY_REMAINING.equals( source ) || ANY_UNMAPPED.equals( source ) || THROW_EXCEPTION.equals( target ) ) ) { result = new ValueMappingOptions( target, source, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 378395d4da..e30407ea20 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -175,7 +175,8 @@ public enum Message { VALUEMAPPING_ANY_REMAINING_FOR_NON_ENUM( "Source = \"\" can only be used on targets of type enum and not for %s." ), VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING( "Source = \"\" or \"\" is advisable for mapping of type String to an enum type.", Diagnostic.Kind.WARNING ), VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI( "Constant %s doesn't exist in enum type %s. Constant was returned from EnumMappingStrategy: %s"), - VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." ); + VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." ), + VALUEMAPPING_THROW_EXCEPTION_SOURCE( "Source = \"\" is not allowed. Target = \"\" can only be used." ); // CHECKSTYLE:ON diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl index 530d5643e1..2844b43e3e 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -15,17 +15,17 @@ if ( ${sourceParameter.name} == null ) { - return <@writeTarget target=nullTarget/>; + <#if nullAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>return <@writeTarget target=nullTarget/>; } <@includeModel object=resultType/> ${resultName}; switch ( ${sourceParameter.name} ) { <#list valueMappings as valueMapping> - case <@writeSource source=valueMapping.source/>: ${resultName} = <@writeTarget target=valueMapping.target/>; - break; + case <@writeSource source=valueMapping.source/>: <#if valueMapping.targetAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>${resultName} = <@writeTarget target=valueMapping.target/>; + break; - default: <#if unexpectedValueMappingException??>throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/>; + default: <#if defaultAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/>; } <#list beforeMappingReferencesWithMappingTarget as callback> <#if callback_index = 0> @@ -65,4 +65,4 @@ null - \ No newline at end of file + diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderThrowExceptionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderThrowExceptionMapper.java new file mode 100644 index 0000000000..ab1ca97828 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/DefaultOrderThrowExceptionMapper.java @@ -0,0 +1,26 @@ +/* + * 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.value.enum2enum; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.Named; +import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Jude Niroshan + */ +@Mapper +public interface DefaultOrderThrowExceptionMapper { + DefaultOrderThrowExceptionMapper INSTANCE = Mappers.getMapper( DefaultOrderThrowExceptionMapper.class ); + + @Named("orderTypeToExternalOrderTypeAnyUnmappedToException") + @ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = MappingConstants.THROW_EXCEPTION) + ExternalOrderType orderTypeToExternalOrderTypeAnyUnmappedToException(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java index 43b621f8cc..1e93f4b00d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.value.enum2enum; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; import org.junit.Rule; @@ -22,14 +20,18 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test for the generation and invocation of enum mapping methods. * * @author Gunnar Morling, Sjaak Derksen */ @IssueKey("128") -@WithClasses({ OrderMapper.class, SpecialOrderMapper.class, DefaultOrderMapper.class, OrderEntity.class, - OrderType.class, OrderDto.class, ExternalOrderType.class }) +@WithClasses({ + OrderMapper.class, SpecialOrderMapper.class, DefaultOrderMapper.class, OrderEntity.class, + OrderType.class, OrderDto.class, ExternalOrderType.class +}) @RunWith(AnnotationProcessorTestRunner.class) public class EnumToEnumMappingTest { @@ -74,16 +76,16 @@ public void shouldInvokeEnumMappingMethodForPropertyMapping() { @Test public void shouldApplyReverseMappings() { - OrderType result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); + OrderType result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); assertThat( result ).isEqualTo( OrderType.EXTRA ); - result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); + result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); assertThat( result ).isEqualTo( OrderType.STANDARD ); - result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); + result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); assertThat( result ).isEqualTo( OrderType.RETAIL ); - result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); + result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); assertThat( result ).isEqualTo( OrderType.B2B ); } @@ -141,16 +143,16 @@ public void shouldApplyDefaultMappings() { @Test public void shouldApplyDefaultReverseMappings() { - OrderType result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); + OrderType result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); assertThat( result ).isEqualTo( OrderType.EXTRA ); - result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); + result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); assertThat( result ).isNull(); - result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); + result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); assertThat( result ).isEqualTo( OrderType.RETAIL ); - result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); + result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); assertThat( result ).isEqualTo( OrderType.B2B ); } @@ -186,7 +188,7 @@ public void shouldMappAllUnmappedToDefault() { } - @IssueKey( "1091" ) + @IssueKey("1091") @Test public void shouldMapAnyRemainingToNullCorrectly() { ExternalOrderType externalOrderType = SpecialOrderMapper.INSTANCE.anyRemainingToNull( OrderType.RETAIL ); @@ -265,7 +267,7 @@ public void shouldRaiseErrorIfSourceConstantWithoutMatchingConstantInTargetTypeI @Diagnostic(type = ErroneousOrderMapperDuplicateANY.class, kind = Kind.ERROR, line = 28, - message = "Source = \"\" or \"\" can only be used once." ) + message = "Source = \"\" or \"\" can only be used once.") } ) public void shouldRaiseErrorIfMappingsContainDuplicateANY() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java new file mode 100644 index 0000000000..15f0d7964d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java @@ -0,0 +1,93 @@ +/* + * 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.value.enum2enum; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * @author Jude Niroshan + */ +@IssueKey("2339") +@WithClasses({ + OrderEntity.class, + OrderType.class, ExternalOrderType.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class EnumToEnumThrowExceptionMappingTest { + + @IssueKey("2339") + @Test + @WithClasses(DefaultOrderThrowExceptionMapper.class) + public void shouldBeAbleToMapAnyUnmappedToThrowException() { + + assertThatThrownBy( () -> + DefaultOrderThrowExceptionMapper.INSTANCE + .orderTypeToExternalOrderTypeAnyUnmappedToException( OrderType.EXTRA ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Unexpected enum constant: EXTRA" ); + } + + @IssueKey("2339") + @Test + @WithClasses({ DefaultOrderThrowExceptionMapper.class, ErroneousOrderMapperThrowExceptionAsSourceType.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperThrowExceptionAsSourceType.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 29, + message = "Source = \"\" is not allowed. " + + "Target = \"\" can only be used.") + } + ) + public void shouldRaiseErrorWhenThrowExceptionUsedAsSourceType() { + } + + @IssueKey("2339") + @Test + @WithClasses({OrderThrowExceptionMapper.class, OrderDto.class}) + public void shouldIgnoreThrowExceptionWhenInverseValueMappings() { + + OrderType target = OrderThrowExceptionMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); + assertThat( target ).isEqualTo( OrderType.B2B ); + } + + @IssueKey("2339") + @Test + @WithClasses({SpecialThrowExceptionMapper.class, OrderDto.class}) + public void shouldBeAbleToMapAnyRemainingToThrowException() { + + assertThatThrownBy( () -> + SpecialThrowExceptionMapper.INSTANCE + .orderTypeToExternalOrderType( OrderType.EXTRA ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Unexpected enum constant: EXTRA" ); + } + + @IssueKey("2339") + @Test + @WithClasses({SpecialThrowExceptionMapper.class, OrderDto.class}) + public void shouldBeAbleToMapNullToThrowException() { + + assertThatThrownBy( () -> + SpecialThrowExceptionMapper.INSTANCE + .anyRemainingToNullToException( null ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Unexpected enum constant: null" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperThrowExceptionAsSourceType.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperThrowExceptionAsSourceType.java new file mode 100644 index 0000000000..5c872c2de8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/ErroneousOrderMapperThrowExceptionAsSourceType.java @@ -0,0 +1,32 @@ +/* + * 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.value.enum2enum; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Jude Niroshan + */ +@Mapper +public interface ErroneousOrderMapperThrowExceptionAsSourceType { + + ErroneousOrderMapperThrowExceptionAsSourceType INSTANCE = Mappers.getMapper( + ErroneousOrderMapperThrowExceptionAsSourceType.class ); + + @ValueMappings({ + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = "DEFAULT"), + @ValueMapping(source = "", target = "DEFAULT"), + @ValueMapping(source = "", target = "DEFAULT") + }) + ExternalOrderType orderTypeToExternalOrderTypeWithErroneousSourceMapping(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderThrowExceptionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderThrowExceptionMapper.java new file mode 100644 index 0000000000..c189b22d51 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/OrderThrowExceptionMapper.java @@ -0,0 +1,35 @@ +/* + * 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.value.enum2enum; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Jude Niroshan + */ +@Mapper +public interface OrderThrowExceptionMapper { + OrderThrowExceptionMapper INSTANCE = Mappers.getMapper( OrderThrowExceptionMapper.class ); + + OrderDto orderEntityToDto(OrderEntity order); + + @ValueMappings({ + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = MappingConstants.THROW_EXCEPTION) + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); + + @InheritInverseConfiguration + OrderType externalOrderTypeToOrderType(ExternalOrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java index 46ffb8b1cf..60d2d5f673 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapper.java @@ -29,14 +29,14 @@ public interface SpecialOrderMapper { @Named("orderTypeToExternalOrderType") @ValueMappings({ - @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ), - @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ), - @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" ) + @ValueMapping(source = MappingConstants.NULL, target = "DEFAULT"), + @ValueMapping(source = "STANDARD", target = MappingConstants.NULL), + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "SPECIAL") }) ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); @InheritInverseConfiguration(name = "orderTypeToExternalOrderType") - @ValueMapping( target = "EXTRA", source = "SPECIAL" ) + @ValueMapping(target = "EXTRA", source = "SPECIAL") OrderType externalOrderTypeToOrderType(ExternalOrderType orderType); @ValueMappings({ diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialThrowExceptionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialThrowExceptionMapper.java new file mode 100644 index 0000000000..0c6d4740ca --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/SpecialThrowExceptionMapper.java @@ -0,0 +1,47 @@ +/* + * 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.value.enum2enum; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.Named; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.factory.Mappers; + +/** + * @author Jude Niroshan + */ +@Mapper +public interface SpecialThrowExceptionMapper { + SpecialThrowExceptionMapper INSTANCE = Mappers.getMapper( SpecialThrowExceptionMapper.class ); + + @Mapping(target = "orderType", source = "orderType", qualifiedByName = "orderTypeToExternalOrderType") + OrderDto orderEntityToDto(OrderEntity order); + + @Named("orderTypeToExternalOrderType") + @ValueMappings({ + @ValueMapping(source = MappingConstants.NULL, target = "DEFAULT"), + @ValueMapping(source = "STANDARD", target = MappingConstants.NULL), + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.THROW_EXCEPTION) + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); + + @InheritInverseConfiguration(name = "orderTypeToExternalOrderType") + @ValueMapping(target = "EXTRA", source = "SPECIAL") + OrderType externalOrderTypeToOrderType(ExternalOrderType orderType); + + @ValueMappings({ + @ValueMapping(source = MappingConstants.NULL, target = MappingConstants.THROW_EXCEPTION), + @ValueMapping(source = "STANDARD", target = MappingConstants.NULL), + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.NULL) + }) + ExternalOrderType anyRemainingToNullToException(OrderType orderType); +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java index f420a63baf..7b5f6f288f 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/value/enum2enum/SpecialOrderMapperImpl.java @@ -11,8 +11,8 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2017-02-20T21:25:45+0100", - comments = "version: , compiler: javac, environment: Java 1.8.0_112 (Oracle Corporation)" + date = "2021-02-19T21:20:19+0100", + comments = "version: , compiler: javac, environment: Java 1.8.0_191 (Oracle Corporation)" ) public class SpecialOrderMapperImpl implements SpecialOrderMapper { From 197dd4327afe1f9cf55a10579491aa3fe162fb0b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 15 Mar 2021 00:21:24 +0100 Subject: [PATCH 146/597] #2339 Polish PR #2362 Use MappingEntry for defaultTarget and nullTarget in ValueMappingMethod to simplify certain things --- .../ap/internal/model/ValueMappingMethod.java | 76 +++++++------------ .../ap/internal/model/ValueMappingMethod.ftl | 6 +- .../mapstruct/ap/test/gem/ConstantTest.java | 1 + .../value/spi/CustomEnumMappingStrategy.java | 21 +++++ .../spi/CustomEnumMappingStrategyTest.java | 23 ++++++ .../value/spi/CustomThrowingCheeseMapper.java | 22 ++++++ .../value/spi/CustomThrowingCheeseType.java | 17 +++++ .../value/spi/CustomThrowingEnumMarker.java | 12 +++ 8 files changed, 125 insertions(+), 53 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingCheeseMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingCheeseType.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingEnumMarker.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index e292d38e3a..6e7fe3f0da 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -42,10 +42,8 @@ public class ValueMappingMethod extends MappingMethod { private final List valueMappings; - private final String defaultTarget; - private final String nullTarget; - private boolean nullAsException; - private boolean defaultAsException; + private final MappingEntry defaultTarget; + private final MappingEntry nullTarget; private final Type unexpectedValueMappingException; @@ -122,12 +120,10 @@ else if ( sourceType.isString() && targetType.isEnumType() ) { return new ValueMappingMethod( method, mappingEntries, valueMappings.nullValueTarget, - valueMappings.hasNullValueAsException, valueMappings.defaultTargetValue, determineUnexpectedValueMappingException(), beforeMappingMethods, - afterMappingMethods, - determineExceptionMappingForDefaultCase() + afterMappingMethods ); } @@ -417,6 +413,7 @@ private boolean reportErrorIfMappedTargetEnumConstantsDontExist(Method method, T foundIncorrectMapping = true; } else if ( valueMappings.nullTarget == null && valueMappings.nullValueTarget != null + && !valueMappings.nullValueTarget.equals( THROW_EXCEPTION ) && !targetEnumConstants.contains( valueMappings.nullValueTarget ) ) { // if there is no nullTarget, but nullValueTarget has a value it means that there was an SPI // which returned an enum for the target enum @@ -433,28 +430,13 @@ else if ( valueMappings.nullTarget == null && valueMappings.nullValueTarget != n } private Type determineUnexpectedValueMappingException() { - boolean noDefaultValueForSwitchCase = !valueMappings.hasDefaultValue; - if ( noDefaultValueForSwitchCase || valueMappings.hasAtLeastOneExceptionValue - || valueMappings.hasNullValueAsException ) { - TypeMirror unexpectedValueMappingException = enumMapping.getUnexpectedValueMappingException(); - if ( unexpectedValueMappingException != null ) { - return ctx.getTypeFactory().getType( unexpectedValueMappingException ); - } - - return ctx.getTypeFactory() - .getType( ctx.getEnumMappingStrategy().getUnexpectedValueMappingExceptionType() ); + TypeMirror unexpectedValueMappingException = enumMapping.getUnexpectedValueMappingException(); + if ( unexpectedValueMappingException != null ) { + return ctx.getTypeFactory().getType( unexpectedValueMappingException ); } - return null; - } - - private boolean determineExceptionMappingForDefaultCase() { - if ( valueMappings.hasDefaultValue ) { - return THROW_EXCEPTION.equals( valueMappings.defaultTargetValue ); - } - else { - return true; - } + return ctx.getTypeFactory() + .getType( ctx.getEnumMappingStrategy().getUnexpectedValueMappingExceptionType() ); } } @@ -493,7 +475,6 @@ private static class ValueMappings { boolean hasMapAnyUnmapped = false; boolean hasMapAnyRemaining = false; boolean hasDefaultValue = false; - boolean hasNullValueAsException = false; boolean hasAtLeastOneExceptionValue = false; ValueMappings(List valueMappings) { @@ -501,22 +482,19 @@ private static class ValueMappings { for ( ValueMappingOptions valueMapping : valueMappings ) { if ( ANY_REMAINING.equals( valueMapping.getSource() ) ) { defaultTarget = valueMapping; - defaultTargetValue = getValue( defaultTarget ); + defaultTargetValue = defaultTarget.getTarget(); hasDefaultValue = true; hasMapAnyRemaining = true; } else if ( ANY_UNMAPPED.equals( valueMapping.getSource() ) ) { defaultTarget = valueMapping; - defaultTargetValue = getValue( defaultTarget ); + defaultTargetValue = defaultTarget.getTarget(); hasDefaultValue = true; hasMapAnyUnmapped = true; } else if ( NULL.equals( valueMapping.getSource() ) ) { nullTarget = valueMapping; nullValueTarget = getValue( nullTarget ); - if ( THROW_EXCEPTION.equals( nullValueTarget ) ) { - hasNullValueAsException = true; - } } else { regularValueMappings.add( valueMapping ); @@ -536,17 +514,14 @@ String getValue(ValueMappingOptions valueMapping) { private ValueMappingMethod(Method method, List enumMappings, String nullTarget, - boolean hasNullTargetAsException, String defaultTarget, Type unexpectedValueMappingException, List beforeMappingMethods, - List afterMappingMethods, boolean defaultAsException) { + List afterMappingMethods) { super( method, beforeMappingMethods, afterMappingMethods ); this.valueMappings = enumMappings; - this.nullTarget = nullTarget; - this.nullAsException = hasNullTargetAsException; - this.defaultTarget = defaultTarget; - this.defaultAsException = defaultAsException; + this.nullTarget = new MappingEntry( null, nullTarget ); + this.defaultTarget = new MappingEntry( null, defaultTarget != null ? defaultTarget : THROW_EXCEPTION); this.unexpectedValueMappingException = unexpectedValueMappingException; this.overridden = method.overridesMethod(); } @@ -556,36 +531,37 @@ public Set getImportTypes() { Set importTypes = super.getImportTypes(); if ( unexpectedValueMappingException != null && !unexpectedValueMappingException.isJavaLangType() ) { - importTypes.addAll( unexpectedValueMappingException.getImportTypes() ); + if ( defaultTarget.isTargetAsException() || nullTarget.isTargetAsException() || + hasMappingWithTargetAsException() ) { + importTypes.addAll( unexpectedValueMappingException.getImportTypes() ); + } } return importTypes; } + protected boolean hasMappingWithTargetAsException() { + return getValueMappings() + .stream() + .anyMatch( MappingEntry::isTargetAsException ); + } + public List getValueMappings() { return valueMappings; } - public String getDefaultTarget() { + public MappingEntry getDefaultTarget() { return defaultTarget; } - public String getNullTarget() { + public MappingEntry getNullTarget() { return nullTarget; } - public boolean isNullAsException() { - return nullAsException; - } - public Type getUnexpectedValueMappingException() { return unexpectedValueMappingException; } - public boolean isDefaultAsException() { - return defaultAsException; - } - public Parameter getSourceParameter() { return first( getSourceParameters() ); } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl index 2844b43e3e..4d961c40af 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -15,7 +15,7 @@ if ( ${sourceParameter.name} == null ) { - <#if nullAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>return <@writeTarget target=nullTarget/>; + <#if nullTarget.targetAsException>throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>return <@writeTarget target=nullTarget.target/>; } <@includeModel object=resultType/> ${resultName}; @@ -25,7 +25,7 @@ case <@writeSource source=valueMapping.source/>: <#if valueMapping.targetAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} );<#else>${resultName} = <@writeTarget target=valueMapping.target/>; break; - default: <#if defaultAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget/>; + default: <#if defaultTarget.targetAsException >throw new <@includeModel object=unexpectedValueMappingException />( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <@writeTarget target=defaultTarget.target/>; } <#list beforeMappingReferencesWithMappingTarget as callback> <#if callback_index = 0> @@ -40,7 +40,7 @@ <@includeModel object=callback targetBeanName=resultName targetType=resultType/> - <#if !(valueMappings.empty && unexpectedValueMappingException??)> + <#if !(valueMappings.empty && defaultTarget.targetAsException)> return ${resultName}; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java index 69fa56a310..711d6d467a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java @@ -23,6 +23,7 @@ public void constantsShouldBeEqual() { assertThat( MappingConstants.ANY_REMAINING ).isEqualTo( MappingConstantsGem.ANY_REMAINING ); assertThat( MappingConstants.ANY_UNMAPPED ).isEqualTo( MappingConstantsGem.ANY_UNMAPPED ); assertThat( MappingConstants.NULL ).isEqualTo( MappingConstantsGem.NULL ); + assertThat( MappingConstants.THROW_EXCEPTION ).isEqualTo( MappingConstantsGem.THROW_EXCEPTION ); assertThat( MappingConstants.SUFFIX_TRANSFORMATION ).isEqualTo( MappingConstantsGem.SUFFIX_TRANSFORMATION ); assertThat( MappingConstants.STRIP_SUFFIX_TRANSFORMATION ) .isEqualTo( MappingConstantsGem.STRIP_SUFFIX_TRANSFORMATION ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategy.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategy.java index b505dd640b..7cf7a24938 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategy.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategy.java @@ -8,6 +8,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; +import org.mapstruct.MappingConstants; import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.spi.DefaultEnumMappingStrategy; import org.mapstruct.ap.spi.EnumMappingStrategy; @@ -20,6 +21,10 @@ public class CustomEnumMappingStrategy extends DefaultEnumMappingStrategy implem @Override public String getDefaultNullEnumConstant(TypeElement enumType) { + if ( isCustomThrowingEnum( enumType ) ) { + return MappingConstants.THROW_EXCEPTION; + } + if ( isCustomEnum( enumType ) ) { return "UNSPECIFIED"; } @@ -29,6 +34,10 @@ public String getDefaultNullEnumConstant(TypeElement enumType) { @Override public String getEnumConstant(TypeElement enumType, String enumConstant) { + if ( isCustomThrowingEnum( enumType ) ) { + return getCustomEnumConstant( enumConstant ); + } + if ( isCustomEnum( enumType ) ) { return getCustomEnumConstant( enumConstant ); } @@ -53,6 +62,18 @@ protected boolean isCustomEnum(TypeElement enumType) { return false; } + protected boolean isCustomThrowingEnum(TypeElement enumType) { + for ( TypeMirror enumTypeInterface : enumType.getInterfaces() ) { + if ( typeUtils.asElement( enumTypeInterface ) + .getSimpleName() + .contentEquals( "CustomThrowingEnumMarker" ) ) { + return true; + } + } + + return false; + } + @Override protected Class getUnexpectedValueMappingExceptionClass() { return CustomIllegalArgumentException.class; diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java index 9cce52d7bd..313a7553bb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java @@ -9,12 +9,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mapstruct.ap.test.value.CustomIllegalArgumentException; +import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * @author Filip Hrisafov @@ -24,6 +26,8 @@ CheeseType.class, CustomCheeseType.class, CustomEnumMarker.class, + CustomThrowingCheeseType.class, + CustomThrowingEnumMarker.class, CustomIllegalArgumentException.class, }) @WithServiceImplementation(CustomEnumMappingStrategy.class) @@ -122,4 +126,23 @@ public void shouldApplyDefinedMappingsInsteadOfCustomEnumMappingStrategy() { assertThat( mapper.mapStringToCustom( "ROQUEFORT" ) ).isEqualTo( CustomCheeseType.CUSTOM_ROQUEFORT ); assertThat( mapper.mapStringToCustom( "UNKNOWN" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); } + + @Test + @IssueKey("2339") + @WithClasses({ + CustomThrowingCheeseMapper.class + }) + public void shouldApplyCustomEnumMappingStrategyWithThrowingException() { + CustomThrowingCheeseMapper mapper = CustomThrowingCheeseMapper.INSTANCE; + + // CustomCheeseType -> CustomThrowingCheeseType + assertThatThrownBy( () -> mapper.map( (CheeseType) null ) ) + .isInstanceOf( CustomIllegalArgumentException.class ) + .hasMessage( "Unexpected enum constant: null" ); + assertThat( mapper.map( CheeseType.BRIE ) ).isEqualTo( CustomThrowingCheeseType.CUSTOM_BRIE ); + + // CustomThrowingCheeseType -> CustomCheeseType + assertThat( mapper.map( (CustomThrowingCheeseType) null ) ).isNull(); + assertThat( mapper.map( CustomThrowingCheeseType.CUSTOM_BRIE ) ).isEqualTo( CheeseType.BRIE ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingCheeseMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingCheeseMapper.java new file mode 100644 index 0000000000..a62dba55fc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingCheeseMapper.java @@ -0,0 +1,22 @@ +/* + * 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.value.spi; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CustomThrowingCheeseMapper { + + CustomThrowingCheeseMapper INSTANCE = Mappers.getMapper( CustomThrowingCheeseMapper.class ); + + CustomThrowingCheeseType map(CheeseType cheese); + + CheeseType map(CustomThrowingCheeseType cheese); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingCheeseType.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingCheeseType.java new file mode 100644 index 0000000000..f97b39a6d8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingCheeseType.java @@ -0,0 +1,17 @@ +/* + * 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.value.spi; + +/** + * @author Filip Hrisafov + */ +public enum CustomThrowingCheeseType implements CustomThrowingEnumMarker { + + UNSPECIFIED, + CUSTOM_BRIE, + CUSTOM_ROQUEFORT, + UNRECOGNIZED, +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingEnumMarker.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingEnumMarker.java new file mode 100644 index 0000000000..aa5943bbcb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomThrowingEnumMarker.java @@ -0,0 +1,12 @@ +/* + * 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.value.spi; + +/** + * @author Filip Hrisafov + */ +public interface CustomThrowingEnumMarker { +} From 1964c809d86ad670c04b08a8b0e924251241c0be Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Tue, 16 Mar 2021 20:24:51 +0100 Subject: [PATCH 147/597] Fix typo in missing code formatting in documentation Closes #2385 --- .../src/main/asciidoc/chapter-5-data-type-conversions.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 9978cd3f25..ce842df0e8 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -20,7 +20,7 @@ Currently the following conversions are applied automatically: [WARNING] ==== -Converting from larger data types to smaller ones (e.g. from `long` to `int`) can cause a value or precision loss. The `Mapper` and `MapperConfig` annotations have a method `typeConversionPolicy` to control warnings / errors. Due to backward compatibility reasons the default value is 'ReportingPolicy.IGNORE`. +Converting from larger data types to smaller ones (e.g. from `long` to `int`) can cause a value or precision loss. The `Mapper` and `MapperConfig` annotations have a method `typeConversionPolicy` to control warnings / errors. Due to backward compatibility reasons the default value is `ReportingPolicy.IGNORE`. ==== * Between all Java primitive types (including their wrappers) and `String`, e.g. between `int` and `String` or `Boolean` and `String`. A format string as understood by `java.text.DecimalFormat` can be specified. From e7f6813d9ac03e0e0881d2ca7e5f525952f19dfc Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Feb 2021 13:56:08 +0100 Subject: [PATCH 148/597] #2356 Implicitly ignore reverse inherited mappings that do not have read and write methods --- .../ap/internal/model/BeanMappingMethod.java | 26 +++++++--- .../ap/test/bugs/_2356/Issue2356Mapper.java | 49 +++++++++++++++++++ .../ap/test/bugs/_2356/Issue2356Test.java | 45 +++++++++++++++++ 3 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 459a4f82d9..0ce80c993c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -938,14 +938,24 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTyp if ( targetWriteAccessor == null ) { if ( targetReadAccessor == null ) { - if ( mapping.getInheritContext() != null && mapping.getInheritContext().isForwarded() && - mapping.getInheritContext().getTemplateMethod().isUpdateMethod() != method.isUpdateMethod() ) { - // When a configuration is inherited and the template method is not same type as the current - // method then we can safely ignore this mapping. - // This means that a property which is inherited might be present for a direct mapping - // via the Builder, but not for an update mapping (directly on the object itself), - // or vice versa - return false; + MappingOptions.InheritContext inheritContext = mapping.getInheritContext(); + if ( inheritContext != null ) { + if ( inheritContext.isForwarded() && + inheritContext.getTemplateMethod().isUpdateMethod() != method.isUpdateMethod() ) { + // When a configuration is inherited and the template method is not same type as the current + // method then we can safely ignore this mapping. + // This means that a property which is inherited might be present for a direct mapping + // via the Builder, but not for an update mapping (directly on the object itself), + // or vice versa + return false; + } + else if ( inheritContext.isReversed() ) { + // When a configuration is reverse inherited and there are no read or write accessor + // then we should ignore this mapping. + // This most likely means that we were mapping the source parameter to the target. + // If the error is due to something else it will be reported on the original mapping + return false; + } } Set readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet(); String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Mapper.java new file mode 100644 index 0000000000..173645d118 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Mapper.java @@ -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._2356; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2356Mapper { + + Issue2356Mapper INSTANCE = Mappers.getMapper( Issue2356Mapper.class ); + + class Car { + //CHECKSTYLE:OFF + public String brand; + public String model; + public String modelInternational; + //CHECKSTYLE:ON + } + + class CarDTO { + //CHECKSTYLE:OFF + public String brand; + public String modelName; + //CHECKSTYLE:ON + } + + // When using InheritInverseConfiguration the mapping from in to modelName should be ignored + // and shouldn't lead to a compile error. + @Mapping(target = "modelName", source = "in") + CarDTO map(Car in); + + default String mapToModel(Car in) { + return in.modelInternational == null ? in.model : in.modelInternational; + } + + @InheritInverseConfiguration + @Mapping(target = "model", source = "modelName") + @Mapping(target = "modelInternational", ignore = true) + Car map(CarDTO in); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Test.java new file mode 100644 index 0000000000..e37f36528c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Test.java @@ -0,0 +1,45 @@ +/* + * 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._2356; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2356") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2356Mapper.class +}) +public class Issue2356Test { + + @Test + public void shouldCompile() { + Issue2356Mapper.Car car = new Issue2356Mapper.Car(); + car.brand = "Tesla"; + car.model = "X"; + car.modelInternational = "3"; + Issue2356Mapper.CarDTO dto = Issue2356Mapper.INSTANCE.map( car ); + + assertThat( dto ).isNotNull(); + assertThat( dto.brand ).isEqualTo( "Tesla" ); + assertThat( dto.modelName ).isEqualTo( "3" ); + + car = Issue2356Mapper.INSTANCE.map( dto ); + + assertThat( car ).isNotNull(); + assertThat( car.brand ).isEqualTo( "Tesla" ); + assertThat( car.model ).isEqualTo( "3" ); + assertThat( car.modelInternational ).isNull(); + } +} From 1187e357c1bca9d78cd58794b873822802694964 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sun, 28 Mar 2021 17:34:59 +0200 Subject: [PATCH 149/597] #2239 matching generics (#2320) --- .../ap/internal/model/ForgedMethod.java | 5 + .../ap/internal/model/HelperMethod.java | 5 + .../model/LifecycleMethodResolver.java | 1 + .../internal/model/MappingBuilderContext.java | 3 +- .../model/ObjectFactoryMethodResolver.java | 17 +- .../ap/internal/model/common/Type.java | 339 +++++++++-- .../ap/internal/model/common/TypeFactory.java | 6 +- .../ap/internal/model/source/Method.java | 7 + .../internal/model/source/MethodMatcher.java | 564 +++++++++--------- .../internal/model/source/SourceMethod.java | 16 + .../model/source/builtin/BuiltInMethod.java | 7 +- .../selector/CreateOrUpdateSelector.java | 8 +- .../selector/FactoryParameterSelector.java | 8 +- .../source/selector/InheritanceSelector.java | 8 +- .../source/selector/MethodFamilySelector.java | 8 +- .../model/source/selector/MethodSelector.java | 11 +- .../source/selector/MethodSelectors.java | 19 +- .../source/selector/QualifierSelector.java | 8 +- .../source/selector/TargetTypeSelector.java | 8 +- .../model/source/selector/TypeSelector.java | 22 +- .../selector/XmlElementDeclSelector.java | 4 +- .../processor/MethodRetrievalProcessor.java | 4 +- .../creation/MappingResolverImpl.java | 7 +- .../util/EclipseTypeUtilsDecorator.java | 39 ++ .../ap/internal/model/common/Type.ftl | 4 +- .../ap/test/bugs/_1482/Issue1482Test.java | 4 +- .../test/bugs/_1482/TargetSourceMapper.java | 4 +- .../test/collection/SourceTargetMapper.java | 2 - .../selection/generics/ConversionTest.java | 26 +- .../selection/generics/GenericTypeMapper.java | 5 - .../ap/test/selection/generics/Source.java | 27 - .../ap/test/selection/generics/Target.java | 27 - .../array/BothParameterizedMapper.java | 75 +++ .../array/GenericArrayTest.java | 64 ++ .../array/ReturnTypeIsTypeVarArrayMapper.java | 71 +++ .../array/SourceTypeIsTypeVarArrayMapper.java | 67 +++ .../methodgenerics/bounds/BoundsTest.java | 36 ++ .../SourceTypeIsBoundedTypeVarMapper.java | 87 +++ .../multiple/MultipleTypeVarTest.java | 73 +++ ...peHasMultipleTypeVarBothGenericMapper.java | 76 +++ ...ypeHasMultipleTypeVarOneGenericMapper.java | 58 ++ ...peHasMultipleTypeVarBothGenericMapper.java | 86 +++ .../nestedgenerics/NestedGenericsTest.java | 49 ++ .../ReturnTypeHasNestedTypeVarMapper.java | 57 ++ .../SourceTypeHasNestedTypeVarMapper.java | 58 ++ .../plain/BothParameterizedMapper.java | 75 +++ .../methodgenerics/plain/PlainTest.java | 75 +++ .../plain/ReturnTypeIsRawTypeMapper.java | 50 ++ .../plain/ReturnTypeIsTypeVarMapper.java | 64 ++ .../plain/SourceTypeIsTypeVarMapper.java | 63 ++ .../targettype/NestedTargetTypeMapper.java | 75 +++ .../targettype/PlainTargetTypeMapper.java | 66 ++ .../targettype/TargetTypeTest.java | 43 ++ .../wildcards/IntersectionMapper.java | 77 +++ .../SourceWildCardExtendsMapper.java | 87 +++ .../wildcards/WildCardTest.java | 62 ++ .../SourceWildCardExtendsMapper.java | 66 ++ .../selection/typegenerics/WildCardTest.java | 41 ++ .../ReturnTypeWildCardExtendsMapper.java | 70 +++ .../SourceWildCardExtendsMapper.java | 66 ++ .../selection/wildcards/WildCardTest.java | 58 ++ 61 files changed, 2640 insertions(+), 478 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/BothParameterizedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/GenericArrayTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/ReturnTypeIsTypeVarArrayMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/SourceTypeIsTypeVarArrayMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/BoundsTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/SourceTypeIsBoundedTypeVarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarBothGenericMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarOneGenericMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/SourceTypeHasMultipleTypeVarBothGenericMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/NestedGenericsTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/ReturnTypeHasNestedTypeVarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/SourceTypeHasNestedTypeVarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/BothParameterizedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/PlainTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/ReturnTypeIsRawTypeMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/ReturnTypeIsTypeVarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/SourceTypeIsTypeVarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/NestedTargetTypeMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/PlainTargetTypeMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/TargetTypeTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/IntersectionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/SourceWildCardExtendsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/SourceWildCardExtendsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/WildCardTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/ReturnTypeWildCardExtendsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/SourceWildCardExtendsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/WildCardTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java index e02107881b..820a09c22a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java @@ -340,6 +340,11 @@ public MappingMethodOptions getOptions() { return basedOn.getOptions(); } + @Override + public List getTypeParameters() { + return Collections.emptyList(); + } + @Override public String describe() { // the name of the forged method is never fully qualified, so no need to distinguish diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java index fcf8068ed6..35b35aee73 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java @@ -128,6 +128,11 @@ public boolean isObjectFactory() { return false; } + @Override + public List getTypeParameters() { + return Collections.emptyList(); + } + /** * the conversion context is used to format an auxiliary parameter in the method call with context specific * information such as a date format. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java index 61610badf8..87527e87f8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java @@ -141,6 +141,7 @@ private static List collectLifecycleCallbackMe callbackMethods, Collections.emptyList(), targetType, + method.getReturnType(), SelectionCriteria.forLifecycleMethods( selectionParameters ) ); return toLifecycleCallbackMethodRefs( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index 46e1d4ee4e..a9d4fd0918 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -78,12 +78,13 @@ public interface MappingResolver { * returns a parameter assignment * * @param mappingMethod target mapping method - * @param description + * @param description the description source * @param targetType return type to match * @param formattingParameters used for formatting dates and numbers * @param criteria parameters criteria in the selection process * @param sourceRHS source information * @param positionHint the mirror for reporting problems + * @param forger the supplier of the callback method to forge a method * * @return an assignment to a method parameter, which can either be: *
      diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index d320bb49da..f0bd9c4dff 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -5,9 +5,12 @@ */ package org.mapstruct.ap.internal.model; +import static org.mapstruct.ap.internal.util.Collections.first; + import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; + import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -23,8 +26,6 @@ import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; import org.mapstruct.ap.internal.util.Message; -import static org.mapstruct.ap.internal.util.Collections.first; - /** * * @author Sjaak Derksen @@ -129,11 +130,13 @@ public static List> getMatchingFactoryMethods( Meth new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() ); return selectors.getMatchingMethods( - method, - getAllAvailableMethods( method, ctx.getSourceModel() ), - java.util.Collections.emptyList(), - alternativeTarget, - SelectionCriteria.forFactoryMethods( selectionParameters ) ); + method, + getAllAvailableMethods( method, ctx.getSourceModel() ), + java.util.Collections.emptyList(), + alternativeTarget, + alternativeTarget, + SelectionCriteria.forFactoryMethods( selectionParameters ) + ); } public static MethodReference getBuilderFactoryMethod(Method method, BuilderType builder ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index b3d01f4131..15f787829b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -12,15 +12,18 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; + import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; @@ -103,6 +106,8 @@ public class Type extends ModelElement implements Comparable { private Type boundingBase = null; + private Type boxedEquivalent = null; + private Boolean hasAccessibleConstructor; private final Filters filters; @@ -311,7 +316,12 @@ public boolean isStreamType() { return isStream; } - public boolean isWildCardSuperBound() { + /** + * A wild card type can have two types of bounds (mutual exclusive): extends and super. + * + * @return true if the bound has a wild card super bound (e.g. ? super Number) + */ + public boolean hasSuperBound() { boolean result = false; if ( typeMirror.getKind() == TypeKind.WILDCARD ) { WildcardType wildcardType = (WildcardType) typeMirror; @@ -320,7 +330,12 @@ public boolean isWildCardSuperBound() { return result; } - public boolean isWildCardExtendsBound() { + /** + * A wild card type can have two types of bounds (mutual exclusive): extends and super. + * + * @return true if the bound has a wild card super bound (e.g. ? extends Number) + */ + public boolean hasExtendsBound() { boolean result = false; if ( typeMirror.getKind() == TypeKind.WILDCARD ) { WildcardType wildcardType = (WildcardType) typeMirror; @@ -329,6 +344,40 @@ public boolean isWildCardExtendsBound() { return result; } + /** + * A type variable type can have two types of bounds (mutual exclusive): lower and upper. + * + * Note that its use is only permitted on a definition (not on the place where its used). For instance: + * {@code T map( T in)} + * + * @return true if the bound has a type variable lower bound (e.g. T super Number) + */ + public boolean hasLowerBound() { + boolean result = false; + if ( typeMirror.getKind() == TypeKind.TYPEVAR ) { + TypeVariable typeVarType = (TypeVariable) typeMirror; + result = typeVarType.getLowerBound() != null; + } + return result; + } + + /** + * A type variable type can have two types of bounds (mutual exclusive): lower and upper. + * + * Note that its use is only permitted on a definition (not on the place where its used). For instance: + * {@code> T map( T in)} + * + * @return true if the bound has a type variable upper bound (e.g. T extends Number) + */ + public boolean hasUpperBound() { + boolean result = false; + if ( typeMirror.getKind() == TypeKind.TYPEVAR ) { + TypeVariable typeVarType = (TypeVariable) typeMirror; + result = typeVarType.getUpperBound() != null; + } + return result; + } + public String getFullyQualifiedName() { return qualifiedName; } @@ -356,7 +405,7 @@ public Set getImportTypes() { result.addAll( parameter.getImportTypes() ); } - if ( ( isWildCardExtendsBound() || isWildCardSuperBound() ) && getTypeBound() != null ) { + if ( ( hasExtendsBound() || hasSuperBound() ) && getTypeBound() != null ) { result.addAll( getTypeBound().getImportTypes() ); } @@ -470,22 +519,19 @@ public Type withoutBounds() { } /** - * Whether this type is assignable to the given other type. + * Whether this type is assignable to the given other type, considering the "extends / upper bounds" + * as well. * * @param other The other type. * * @return {@code true} if and only if this type is assignable to the given other type. */ - // TODO This doesn't yet take super wild card types into account; - // e.g. Number wouldn't be assignable to ? super Number atm. (is there any practical use case) public boolean isAssignableTo(Type other) { - if ( equals( other ) ) { - return true; + if ( TypeKind.WILDCARD == typeMirror.getKind() ) { + return typeUtils.contains( typeMirror, other.typeMirror ); } - TypeMirror typeMirrorToMatch = isWildCardExtendsBound() ? getTypeBound().typeMirror : typeMirror; - - return typeUtils.isAssignable( typeMirrorToMatch, other.typeMirror ); + return typeUtils.isAssignable( typeMirror, other.typeMirror ); } /** @@ -506,6 +552,19 @@ public boolean isRawAssignableTo(Type other) { return typeUtils.isAssignable( typeUtils.erasure( typeMirror ), typeUtils.erasure( other.typeMirror ) ); } + /** + * removes any bounds from this type. + * @return the raw type + */ + public Type asRawType() { + if ( getTypeBound() != null ) { + return typeFactory.getType( typeUtils.erasure( typeMirror ) ); + } + else { + return this; + } + } + /** * getPropertyReadAccessors * @@ -1004,7 +1063,14 @@ public boolean equals(Object obj) { } Type other = (Type) obj; - return typeUtils.isSameType( typeMirror, other.typeMirror ); + if ( this.isWildCardBoundByTypeVar() && other.isWildCardBoundByTypeVar() ) { + return ( this.hasExtendsBound() == this.hasExtendsBound() + || this.hasSuperBound() == this.hasSuperBound() ) + && typeUtils.isSameType( getTypeBound().getTypeMirror(), other.getTypeBound().getTypeMirror() ); + } + else { + return typeUtils.isSameType( typeMirror, other.typeMirror ); + } } @Override @@ -1085,6 +1151,19 @@ public boolean hasAccessibleConstructor() { return hasAccessibleConstructor; } + /** + * Returns the direct supertypes of a type. The interface types, if any, + * will appear last in the list. + * + * @return the direct supertypes, or an empty list if none + */ + public List getDirectSuperTypes() { + return typeUtils.directSupertypes( typeMirror ) + .stream() + .map( typeFactory::getType ) + .collect( Collectors.toList() ); + } + /** * Searches for the given superclass and collects all type arguments for the given class * @@ -1121,59 +1200,239 @@ public boolean isLiteral() { } /** - * Steps through the declaredType in order to find a match for this typevar Type. It allignes with + * Steps through the declaredType in order to find a match for this typeVar Type. It aligns with * the provided parameterized type where this typeVar type is used. * - * @param declaredType the type - * @param parameterizedType the parameterized type + * For example: + * {@code + * this: T + * declaredType: JAXBElement + * parameterizedType: JAXBElement + * result: String + * + * + * this: T, T[] or ? extends T, + * declaredType: E.g. Callable + * parameterizedType: Callable + * return: BigDecimal + * } + * + * @param declared the type + * @param parameterized the parameterized type * - * @return the matching declared type. + * @return - the same type when this is not a type var in the broadest sense (T, T[], or ? extends T) + * - the matching parameter in the parameterized type when this is a type var when found + * - null in all other cases */ - public Type resolveTypeVarToType(Type declaredType, Type parameterizedType) { - if ( isTypeVar() ) { - TypeVarMatcher typeVarMatcher = new TypeVarMatcher( typeUtils, this ); - return typeVarMatcher.visit( parameterizedType.getTypeMirror(), declaredType ); + public ResolvedPair resolveParameterToType(Type declared, Type parameterized) { + if ( isTypeVar() || isArrayTypeVar() || isWildCardBoundByTypeVar() ) { + TypeVarMatcher typeVarMatcher = new TypeVarMatcher( typeFactory, typeUtils, this ); + return typeVarMatcher.visit( parameterized.getTypeMirror(), declared ); } - return this; + return new ResolvedPair( this, this ); + } + + public boolean isWildCardBoundByTypeVar() { + return ( hasExtendsBound() || hasSuperBound() ) && getTypeBound().isTypeVar(); } - private static class TypeVarMatcher extends SimpleTypeVisitor8 { + public boolean isArrayTypeVar() { + return isArrayType() && getComponentType().isTypeVar(); + } + + private static class TypeVarMatcher extends SimpleTypeVisitor8 { - private TypeVariable typeVarToMatch; - private TypeUtils types; + private final TypeFactory typeFactory; + private final Type typeToMatch; + private final TypeUtils types; - TypeVarMatcher(TypeUtils types, Type typeVarToMatch ) { - super( null ); - this.typeVarToMatch = (TypeVariable) typeVarToMatch.getTypeMirror(); + /** + * @param typeFactory factory + * @param types type utils + * @param typeToMatch the typeVar or wildcard with typeVar bound + */ + TypeVarMatcher(TypeFactory typeFactory, TypeUtils types, Type typeToMatch) { + super( new ResolvedPair( typeToMatch, null ) ); + this.typeFactory = typeFactory; + this.typeToMatch = typeToMatch; this.types = types; } @Override - public Type visitTypeVariable(TypeVariable t, Type parameterized) { - if ( types.isSameType( t, typeVarToMatch ) ) { - return parameterized; + public ResolvedPair visitTypeVariable(TypeVariable parameterized, Type declared) { + if ( typeToMatch.isTypeVar() && types.isSameType( parameterized, typeToMatch.getTypeMirror() ) ) { + return new ResolvedPair( typeFactory.getType( parameterized ), declared ); } - return super.visitTypeVariable( t, parameterized ); + return super.DEFAULT_VALUE; } + /** + * If ? extends SomeTime equals the boundary set in typeVarToMatch (NOTE: you can't compare the wildcard itself) + * then return a result; + */ @Override - public Type visitDeclared(DeclaredType t, Type parameterized) { - if ( types.isAssignable( types.erasure( t ), types.erasure( parameterized.getTypeMirror() ) ) ) { + public ResolvedPair visitWildcard(WildcardType parameterized, Type declared) { + if ( typeToMatch.hasExtendsBound() && parameterized.getExtendsBound() != null + && types.isSameType( typeToMatch.getTypeBound().getTypeMirror(), parameterized.getExtendsBound() ) ) { + return new ResolvedPair( typeToMatch, declared); + } + else if ( typeToMatch.hasSuperBound() && parameterized.getSuperBound() != null + && types.isSameType( typeToMatch.getTypeBound().getTypeMirror(), parameterized.getSuperBound() ) ) { + return new ResolvedPair( typeToMatch, declared); + } + if ( parameterized.getExtendsBound() != null ) { + ResolvedPair match = visit( parameterized.getExtendsBound(), declared ); + if ( match.match != null ) { + return new ResolvedPair( typeFactory.getType( parameterized ), declared ); + } + } + else if (parameterized.getSuperBound() != null ) { + ResolvedPair match = visit( parameterized.getSuperBound(), declared ); + if ( match.match != null ) { + return new ResolvedPair( typeFactory.getType( parameterized ), declared ); + } + + } + return super.DEFAULT_VALUE; + } + + @Override + public ResolvedPair visitArray(ArrayType parameterized, Type declared) { + if ( types.isSameType( parameterized.getComponentType(), typeToMatch.getTypeMirror() ) ) { + return new ResolvedPair( typeFactory.getType( parameterized ), declared ); + } + if ( declared.isArrayType() ) { + return visit( parameterized.getComponentType(), declared.getComponentType() ); + } + return super.DEFAULT_VALUE; + } + + @Override + public ResolvedPair visitDeclared(DeclaredType parameterized, Type declared) { + + List results = new ArrayList<>( ); + if ( parameterized.getTypeArguments().isEmpty() ) { + return super.DEFAULT_VALUE; + } + else if ( types.isSameType( types.erasure( parameterized ), types.erasure( declared.getTypeMirror() ) ) ) { // We can't assume that the type args are the same // e.g. List is assignable to Object - if ( t.getTypeArguments().size() != parameterized.getTypeParameters().size() ) { - return super.visitDeclared( t, parameterized ); + if ( parameterized.getTypeArguments().size() != declared.getTypeParameters().size() ) { + return super.visitDeclared( parameterized, declared ); } - for ( int i = 0; i < t.getTypeArguments().size(); i++ ) { - Type result = visit( t.getTypeArguments().get( i ), parameterized.getTypeParameters().get( i ) ); - if ( result != null ) { - return result; + // only possible to compare parameters when the types are exactly the same + for ( int i = 0; i < parameterized.getTypeArguments().size(); i++ ) { + TypeMirror parameterizedTypeArg = parameterized.getTypeArguments().get( i ); + Type declaredTypeArg = declared.getTypeParameters().get( i ); + ResolvedPair result = visit( parameterizedTypeArg, declaredTypeArg ); + if ( result != super.DEFAULT_VALUE ) { + results.add( result ); } } } - return super.visitDeclared( t, parameterized ); + else { + // Also check whether the implemented interfaces are parameterized + for ( Type declaredSuperType : declared.getDirectSuperTypes() ) { + if ( Object.class.getName().equals( declaredSuperType.getFullyQualifiedName() ) ) { + continue; + } + ResolvedPair result = visitDeclared( parameterized, declaredSuperType ); + if ( result != super.DEFAULT_VALUE ) { + results.add( result ); + } + } + + for ( TypeMirror parameterizedSuper : types.directSupertypes( parameterized ) ) { + if ( isJavaLangObject( parameterizedSuper ) ) { + continue; + } + ResolvedPair result = visitDeclared( (DeclaredType) parameterizedSuper, declared ); + if ( result != super.DEFAULT_VALUE ) { + results.add( result ); + } + } + } + if ( results.isEmpty() ) { + return super.DEFAULT_VALUE; + } + else { + return results.stream().allMatch( results.get( 0 )::equals ) ? results.get( 0 ) : super.DEFAULT_VALUE; + } } + + private boolean isJavaLangObject(TypeMirror type) { + if ( type instanceof DeclaredType ) { + return ( (TypeElement) ( (DeclaredType) type ).asElement() ).getQualifiedName() + .contentEquals( Object.class.getName() ); + } + return false; + } + } + + /** + * Reflects any Resolved Pair, examples are + * T, String + * ? extends T, BigDecimal + * T[], Integer[] + */ + public static class ResolvedPair { + + public ResolvedPair(Type parameter, Type match) { + this.parameter = parameter; + this.match = match; + } + + /** + * parameter, e.g. T, ? extends T or T[] + */ + private Type parameter; + + /** + * match, e.g. String, BigDecimal, Integer[] + */ + private Type match; + + public Type getParameter() { + return parameter; + } + + public Type getMatch() { + return match; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + ResolvedPair that = (ResolvedPair) o; + return Objects.equals( parameter, that.parameter ) && Objects.equals( match, that.match ); + } + + @Override + public int hashCode() { + return Objects.hash( parameter ); + } + } + + /** + * Gets the boxed equivalent type if the type is primitive, int will return Integer + * + * @return boxed equivalent + */ + public Type getBoxedEquivalent() { + if ( boxedEquivalent != null ) { + return boxedEquivalent; + } + else if ( isPrimitive() ) { + boxedEquivalent = typeFactory.getType( typeUtils.boxedClass( (PrimitiveType) typeMirror ) ); + return boxedEquivalent; + } + return this; } /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 8d3d215c52..f8c67f740e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -613,7 +613,11 @@ else if ( typeMirror.getKind() == TypeKind.TYPEVAR ) { if ( typeVariableType.getUpperBound() != null ) { return typeVariableType.getUpperBound(); } - // Lowerbounds intentionally left out: Type variables otherwise have a lower bound of NullType. + // lower bounds ( T super Number ) cannot be used for argument parameters, but can be used for + // method parameters: e.g. T map (T in); + if ( typeVariableType.getLowerBound() != null ) { + return typeVariableType.getLowerBound(); + } } return typeMirror; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index 2039aa86aa..8a38ed2acb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -199,4 +199,11 @@ default Type getMappingSourceType() { * @return the short name for error messages when verbose, full name when not */ String describe(); + + /** + * Returns the formal type parameters of this method in declaration order. + * + * @return the formal type parameters, or an empty list if there are none + */ + List getTypeParameters(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index ea3f720440..136828adfc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -5,25 +5,19 @@ */ package org.mapstruct.ap.internal.model.source; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.TypeParameterElement; -import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.PrimitiveType; -import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.type.TypeVariable; -import javax.lang.model.type.WildcardType; -import javax.lang.model.util.SimpleTypeVisitor6; -import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.TypeUtils; /** * SourceMethodMatcher $8.4 of the JavaLanguage specification describes a method body as such: @@ -66,362 +60,348 @@ public class MethodMatcher { * Whether the given source and target types are matched by this matcher's candidate method. * * @param sourceTypes the source types - * @param resultType the target type + * @param targetType the target type * @return {@code true} when both, source type and target types match the signature of this matcher's method; * {@code false} otherwise. */ - boolean matches(List sourceTypes, Type resultType) { - - // check & collect generic types. - Map genericTypesMap = new HashMap<>(); + boolean matches(List sourceTypes, Type targetType) { - if ( candidateMethod.getParameters().size() == sourceTypes.size() ) { - int i = 0; - for ( Parameter candidateParam : candidateMethod.getParameters() ) { - Type sourceType = sourceTypes.get( i++ ); - if ( sourceType == null - || !matchSourceType( sourceType, candidateParam.getType(), genericTypesMap ) ) { - return false; - } - } - } - else { + GenericAnalyser analyser = + new GenericAnalyser( typeFactory, typeUtils, candidateMethod, sourceTypes, targetType ); + if ( !analyser.lineUp() ) { return false; } - // check if the method matches the proper result type to construct - Parameter targetTypeParameter = candidateMethod.getTargetTypeParameter(); - if ( targetTypeParameter != null ) { - Type returnClassType = typeFactory.classTypeOf( resultType ); - if ( !matchSourceType( returnClassType, targetTypeParameter.getType(), genericTypesMap ) ) { + for ( int i = 0; i < sourceTypes.size(); i++ ) { + Type candidateSourceParType = analyser.candidateParTypes.get( i ); + if ( !sourceTypes.get( i ).isAssignableTo( candidateSourceParType ) + || isPrimitiveToObject( sourceTypes.get( i ), candidateSourceParType ) ) { return false; } } + // TODO: TargetType checking should not be part of method selection, it should be in checking the annotation + // (the relation target / target type, target type being a class) - // check result type - if ( !matchResultType( resultType, genericTypesMap ) ) { - return false; - } - - // check if all type parameters are indeed mapped - if ( candidateMethod.getExecutable().getTypeParameters().size() != genericTypesMap.size() ) { - return false; - } - - // check if all entries are in the bounds - for ( Map.Entry entry : genericTypesMap.entrySet() ) { - if ( !isWithinBounds( entry.getValue(), getTypeParamFromCandidate( entry.getKey() ) ) ) { - // checks if the found Type is in bounds of the TypeParameters bounds. + if ( !analyser.candidateReturnType.isVoid() ) { + if ( !( analyser.candidateReturnType.isAssignableTo( targetType ) ) ) { return false; } } + return true; } - private boolean matchSourceType(Type sourceType, - Type candidateSourceType, - Map genericTypesMap) { - - if ( !isJavaLangObject( candidateSourceType.getTypeMirror() ) ) { - TypeMatcher parameterMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_FROM, genericTypesMap ); - if ( !parameterMatcher.visit( candidateSourceType.getTypeMirror(), sourceType.getTypeMirror() ) ) { - if ( sourceType.isPrimitive() ) { - // the candidate source is primitive, so promote to its boxed type and check again (autobox) - TypeMirror boxedType = typeUtils.boxedClass( (PrimitiveType) sourceType.getTypeMirror() ).asType(); - if ( !parameterMatcher.visit( candidateSourceType.getTypeMirror(), boxedType ) ) { - return false; - } - } - else { - // NOTE: unboxing is deliberately not considered here. This should be handled via type-conversion - // (for NPE safety). - return false; - } - } + /** + * Primitive to Object (Boxed Type) should be handled by a type conversion rather than a direct mapping + * Direct mapping runs the risk of null pointer exceptions: e.g. in the assignment of Integer to int, Integer + * can be null. + * + * @param type the type to be assigned + * @param isAssignableTo the type to which @param type should be assignable to + * @return true if isAssignable is a primitive type and type is an object + */ + private boolean isPrimitiveToObject( Type type, Type isAssignableTo ) { + if ( isAssignableTo.isPrimitive() ) { + return !type.isPrimitive(); } - return true; + return false; } - private boolean matchResultType(Type resultType, Map genericTypesMap) { + private static class GenericAnalyser { + + private TypeFactory typeFactory; + private TypeUtils typeUtils; + private Method candidateMethod; + private List sourceTypes; + private Type targetType; + + GenericAnalyser(TypeFactory typeFactory, TypeUtils typeUtils, Method candidateMethod, + List sourceTypes, Type targetType) { + this.typeFactory = typeFactory; + this.typeUtils = typeUtils; + this.candidateMethod = candidateMethod; + this.sourceTypes = sourceTypes; + this.targetType = targetType; + } - Type candidateResultType = candidateMethod.getResultType(); + Type candidateReturnType = null; + List candidateParTypes; - if ( !isJavaLangObject( candidateResultType.getTypeMirror() ) && !candidateResultType.isVoid() ) { + private boolean lineUp() { - final Assignability visitedAssignability; - if ( candidateMethod.getReturnType().isVoid() ) { - // for void-methods, the result-type of the candidate needs to be assignable from the given result type - visitedAssignability = Assignability.VISITED_ASSIGNABLE_FROM; - } - else { - // for non-void methods, the result-type of the candidate needs to be assignable to the given result - // type - visitedAssignability = Assignability.VISITED_ASSIGNABLE_TO; + if ( candidateMethod.getParameters().size() != sourceTypes.size() ) { + return false; } - TypeMatcher returnTypeMatcher = new TypeMatcher( visitedAssignability, genericTypesMap ); - if ( !returnTypeMatcher.visit( candidateResultType.getTypeMirror(), resultType.getTypeMirror() ) ) { - if ( resultType.isPrimitive() ) { - TypeMirror boxedType = typeUtils.boxedClass( (PrimitiveType) resultType.getTypeMirror() ).asType(); - TypeMatcher boxedReturnTypeMatcher = - new TypeMatcher( visitedAssignability, genericTypesMap ); + if ( !candidateMethod.getTypeParameters().isEmpty() ) { - if ( !boxedReturnTypeMatcher.visit( candidateResultType.getTypeMirror(), boxedType ) ) { - return false; + this.candidateParTypes = new ArrayList<>(); + + // Per generic method parameter the associated type variable candidates + Map methodParCandidates = new HashMap<>(); + + // Get candidates + boolean success = getCandidates( methodParCandidates ); + if ( !success ) { + return false; + } + + // Check type bounds + boolean withinBounds = candidatesWithinBounds( methodParCandidates ); + if ( !withinBounds ) { + return false; + } + + // Represent result as map. + Map resolvedPairs = new HashMap<>(); + for ( TypeVarCandidate candidate : methodParCandidates.values() ) { + for ( Type.ResolvedPair pair : candidate.pairs) { + resolvedPairs.put( pair.getParameter(), pair.getMatch() ); } } - else if ( candidateResultType.getTypeMirror().getKind().isPrimitive() ) { - TypeMirror boxedCandidateReturnType = - typeUtils.boxedClass( (PrimitiveType) candidateResultType.getTypeMirror() ).asType(); - TypeMatcher boxedReturnTypeMatcher = - new TypeMatcher( visitedAssignability, genericTypesMap ); - if ( !boxedReturnTypeMatcher.visit( boxedCandidateReturnType, resultType.getTypeMirror() ) ) { + // Resolve parameters and return type by using the found candidates + int nrOfMethodPars = candidateMethod.getParameters().size(); + for ( int i = 0; i < nrOfMethodPars; i++ ) { + Type candidateType = resolve( candidateMethod.getParameters().get( i ).getType(), resolvedPairs ); + if ( candidateType == null ) { return false; } + this.candidateParTypes.add( candidateType ); } + if ( !candidateMethod.getReturnType().isVoid() ) { + this.candidateReturnType = resolve( candidateMethod.getReturnType(), resolvedPairs ); + if ( this.candidateReturnType == null ) { + return false; + } + } else { - return false; + this.candidateReturnType = candidateMethod.getReturnType(); } } + else { + this.candidateParTypes = candidateMethod.getParameters().stream() + .map( Parameter::getType ) + .collect( Collectors.toList() ); + this.candidateReturnType = candidateMethod.getReturnType(); + } + return true; } - return true; - } - - /** - * @param type the type - * @return {@code true}, if the type represents java.lang.Object - */ - private boolean isJavaLangObject(TypeMirror type) { - return type.getKind() == TypeKind.DECLARED - && ( (TypeElement) ( (DeclaredType) type ).asElement() ).getQualifiedName().contentEquals( - Object.class.getName() ); - } - - private enum Assignability { - VISITED_ASSIGNABLE_FROM, VISITED_ASSIGNABLE_TO; - Assignability invert() { - return this == VISITED_ASSIGNABLE_FROM - ? VISITED_ASSIGNABLE_TO - : VISITED_ASSIGNABLE_FROM; + /** + * {@code T map( U in ) } + * + * Resolves all method generic parameter candidates + * + * @param methodParCandidates Map, keyed by the method generic parameter (T, U extends Number), with their + * respective candidates + * + * @return false no match or conflict has been found * + */ + boolean getCandidates( Map methodParCandidates) { + + int nrOfMethodPars = candidateMethod.getParameters().size(); + Type returnType = candidateMethod.getReturnType(); + + for ( int i = 0; i < nrOfMethodPars; i++ ) { + Type sourceType = sourceTypes.get( i ); + Parameter par = candidateMethod.getParameters().get( i ); + Type parType = par.getType(); + boolean success = getCandidates( parType, sourceType, methodParCandidates ); + if ( !success ) { + return false; + } + } + if ( !returnType.isVoid() ) { + boolean success = getCandidates( returnType, targetType, methodParCandidates ); + if ( !success ) { + return false; + } + } + return true; } - } - private class TypeMatcher extends SimpleTypeVisitor6 { - private final Assignability assignability; - private final Map genericTypesMap; - private final TypeMatcher inverse; - - TypeMatcher(Assignability assignability, Map genericTypesMap) { - super( Boolean.FALSE ); // default value - this.assignability = assignability; - this.genericTypesMap = genericTypesMap; - this.inverse = new TypeMatcher( this, genericTypesMap ); - } + /** + * @param aCandidateMethodType parameter type or return type from candidate method + * @param matchingType source type / target type to match + * @param candidates Map, keyed by the method generic parameter, with the candidates + * + * @return false no match or conflict has been found + */ + boolean getCandidates(Type aCandidateMethodType, Type matchingType, Map candidates ) { + + if ( !( aCandidateMethodType.isTypeVar() + || aCandidateMethodType.isArrayTypeVar() + || aCandidateMethodType.isWildCardBoundByTypeVar() + || hasGenericTypeParameters( aCandidateMethodType ) ) ) { + // the typeFromCandidateMethod is not a generic (parameterized) type + return true; + } - TypeMatcher(TypeMatcher inverse, Map genericTypesMap) { - super( Boolean.FALSE ); // default value - this.assignability = inverse.assignability.invert(); - this.genericTypesMap = genericTypesMap; - this.inverse = inverse; - } + for ( Type mthdParType : candidateMethod.getTypeParameters() ) { - @Override - public Boolean visitPrimitive(PrimitiveType t, TypeMirror p) { - return typeUtils.isSameType( t, p ); - } + // typeFromCandidateMethod itself is a generic type, e.g. String method( T par ); + // typeFromCandidateMethod is a generic arrayType e.g. String method( T[] par ); + // typeFromCandidateMethod is embedded in another type e.g. String method( Callable par ); + // typeFromCandidateMethod is a wildcard, bounded by a typeVar + // e.g. String method( List in ) - @Override - public Boolean visitArray(ArrayType t, TypeMirror p) { + Type.ResolvedPair resolved = mthdParType.resolveParameterToType( matchingType, aCandidateMethodType ); + if ( resolved == null ) { + // cannot find a candidate type, but should have since the typeFromCandidateMethod had parameters + // to be resolved + return !hasGenericTypeParameters( aCandidateMethodType ); + } - if ( p.getKind().equals( TypeKind.ARRAY ) ) { - return t.getComponentType().accept( this, ( (ArrayType) p ).getComponentType() ); - } - else { - return Boolean.FALSE; - } - } + // resolved something at this point, a candidate can be fetched or created + TypeVarCandidate typeVarCandidate; + if ( candidates.containsKey( mthdParType ) ) { + typeVarCandidate = candidates.get( mthdParType ); + } + else { + // add a new type + typeVarCandidate = new TypeVarCandidate( ); + candidates.put( mthdParType, typeVarCandidate ); + } - @Override - public Boolean visitDeclared(DeclaredType t, TypeMirror p) { - // its a match when: 1) same kind of type, name is equals, nr of type args are the same - // (type args are checked later). - if ( p.getKind() == TypeKind.DECLARED ) { - DeclaredType t1 = (DeclaredType) p; - if ( rawAssignabilityMatches( t, t1 ) ) { - if ( t.getTypeArguments().size() == t1.getTypeArguments().size() ) { - // compare type var side by side - for ( int i = 0; i < t.getTypeArguments().size(); i++ ) { - if ( !visit( t.getTypeArguments().get( i ), t1.getTypeArguments().get( i ) ) ) { - return Boolean.FALSE; - } + // check what we've resolved + if ( resolved.getParameter().isTypeVar() ) { + // it might be already set, but we just checked if its an equivalent type + if ( typeVarCandidate.match == null ) { + typeVarCandidate.match = resolved.getMatch(); + if ( typeVarCandidate.match == null) { + return false; } - return Boolean.TRUE; + typeVarCandidate.pairs.add( resolved ); } - else { - // return true (e.g. matching Enumeration with an enumeration E) - // but do not try to line up raw type arguments with types that do have arguments. - return assignability == Assignability.VISITED_ASSIGNABLE_TO ? - !t1.getTypeArguments().isEmpty() : !t.getTypeArguments().isEmpty(); + else if ( !areEquivalent( resolved.getMatch(), typeVarCandidate.match ) ) { + // type has been resolved twice, but with a different candidate (conflict) + return false; } + + } + else if ( resolved.getParameter().isArrayTypeVar() + && resolved.getParameter().getComponentType().isAssignableTo( mthdParType ) ) { + // e.g. T map( List in ), the match for T should be assignable + // to the parameter T extends Number + typeVarCandidate.pairs.add( resolved ); + } + else if ( resolved.getParameter().isWildCardBoundByTypeVar() + && resolved.getParameter().getTypeBound().isAssignableTo( mthdParType ) ) { + // e.g. T map( List in ), the match for ? super T should be assignable + // to the parameter T extends Number + typeVarCandidate.pairs.add( resolved ); } else { - return Boolean.FALSE; + // none of the above + return false; } } - else if ( p.getKind() == TypeKind.WILDCARD ) { - return inverse.visit( p, t ); // inverse, as we switch the params - } - else { - return Boolean.FALSE; - } + return true; } - private boolean rawAssignabilityMatches(DeclaredType t1, DeclaredType t2) { - if ( assignability == Assignability.VISITED_ASSIGNABLE_TO ) { - return typeUtils.isAssignable( toRawType( t1 ), toRawType( t2 ) ); - } - else { - return typeUtils.isAssignable( toRawType( t2 ), toRawType( t1 ) ); + /** + * Checks whether all found candidates are within the bounds of the method type var. For instance + * @ U map( T in ). Note that only the relation between the + * match for U and Callable are checked. Not the correct parameter. + * + * @param methodParCandidates + * + * @return true when all within bounds. + */ + private boolean candidatesWithinBounds(Map methodParCandidates ) { + for ( Map.Entry entry : methodParCandidates.entrySet() ) { + Type bound = entry.getKey().getTypeBound(); + if ( bound != null ) { + for ( Type.ResolvedPair pair : entry.getValue().pairs ) { + if ( entry.getKey().hasUpperBound() ) { + if ( !pair.getMatch().asRawType().isAssignableTo( bound.asRawType() ) ) { + return false; + } + } + else { + // lower bound + if ( !bound.asRawType().isAssignableTo( pair.getMatch().asRawType() ) ) { + return false; + } + } + } + } } + return true; } - private DeclaredType toRawType(DeclaredType t) { - return typeUtils.getDeclaredType( (TypeElement) t.asElement() ); - } - - @Override - public Boolean visitTypeVariable(TypeVariable t, TypeMirror p) { - if ( genericTypesMap.containsKey( t ) ) { - // when already found, the same mapping should apply - // Then we should visit the resolved generic type. - // Which can potentially be another generic type - // e.g. - // T fromOptional(Optional optional) - // T resolves to Collection - // We know what T resolves to, so we should treat it as if the method signature was - // Collection fromOptional(Optional optional) - TypeMirror p1 = genericTypesMap.get( t ); - // p (Integer) should be a subType of p1 (Number) - // i.e. you can assign p (Integer) to p1 (Number) - return visit( p, p1 ); - } - else { - // check if types are in bound - TypeMirror lowerBound = t.getLowerBound(); - TypeMirror upperBound = t.getUpperBound(); - if ( ( isNullType( lowerBound ) || typeUtils.isSubtypeErased( lowerBound, p ) ) - && ( isNullType( upperBound ) || typeUtils.isSubtypeErased( p, upperBound ) ) ) { - genericTypesMap.put( t, p ); - return Boolean.TRUE; + private boolean hasGenericTypeParameters(Type typeFromCandidateMethod) { + for ( Type typeParam : typeFromCandidateMethod.getTypeParameters() ) { + if ( typeParam.isTypeVar() || typeParam.isWildCardBoundByTypeVar() || typeParam.isArrayTypeVar() ) { + return true; } else { - return Boolean.FALSE; + if ( hasGenericTypeParameters( typeParam ) ) { + return true; + } } } + return false; } - private boolean isNullType(TypeMirror type) { - return type == null || type.getKind() == TypeKind.NULL; - } - - @Override - public Boolean visitWildcard(WildcardType t, TypeMirror p) { - - // check extends bound - TypeMirror extendsBound = t.getExtendsBound(); - if ( !isNullType( extendsBound ) ) { - switch ( extendsBound.getKind() ) { - case DECLARED: - // for example method: String method(? extends String) - // isSubType checks range [subtype, type], e.g. isSubtype [Object, String]==true - return visit( extendsBound, p ); - - case TYPEVAR: - // for example method: T method(? extends T) - // this can be done the directly by checking: ? extends String & Serializable - // this checks the part? - return isWithinBounds( p, getTypeParamFromCandidate( extendsBound ) ); - - default: - // does this situation occur? - return Boolean.FALSE; - } + private Type resolve( Type typeFromCandidateMethod, Map pairs ) { + if ( typeFromCandidateMethod.isTypeVar() || typeFromCandidateMethod.isArrayTypeVar() ) { + return pairs.get( typeFromCandidateMethod ); } - - // check super bound - TypeMirror superBound = t.getSuperBound(); - if ( !isNullType( superBound ) ) { - switch ( superBound.getKind() ) { - case DECLARED: - // for example method: String method(? super String) - // to check super type, we can simply inverse the argument, but that would initially yield - // a result: T method(? super T) - if ( !isWithinBounds( p, typeParameter ) ) { - // this checks the part? - return Boolean.FALSE; + else if ( hasGenericTypeParameters( typeFromCandidateMethod ) ) { + TypeMirror[] typeArgs = new TypeMirror[ typeFromCandidateMethod.getTypeParameters().size() ]; + for ( int i = 0; i < typeFromCandidateMethod.getTypeParameters().size(); i++ ) { + Type typeFromCandidateMethodTypeParameter = typeFromCandidateMethod.getTypeParameters().get( i ); + if ( hasGenericTypeParameters( typeFromCandidateMethodTypeParameter ) ) { + // nested type var, lets resolve some more (recur) + Type matchingType = resolve( typeFromCandidateMethodTypeParameter, pairs ); + if ( matchingType == null ) { + // something went wrong + return null; + } + typeArgs[i] = matchingType.getTypeMirror(); + } + else if ( typeFromCandidateMethodTypeParameter.isWildCardBoundByTypeVar() + || typeFromCandidateMethodTypeParameter.isTypeVar() + || typeFromCandidateMethodTypeParameter.isArrayTypeVar() + ) { + Type matchingType = pairs.get( typeFromCandidateMethodTypeParameter ); + if ( matchingType == null ) { + // something went wrong + return null; } - // now, it becomes a bit more hairy. We have the relation (? super T). From T we know that - // it is a subclass of String & Serializable. However, The Java Language Secification, - // Chapter 4.4, states that a bound is either: 'A type variable-', 'A class-' or 'An - // interface-' type followed by further interface types. So we must compare with the first - // argument in the Expression String & Serializable & ..., so, in this case String. - // to check super type, we can simply inverse the argument, but that would initially yield - // a result: ), String is not a type var + typeArgs[i] = typeFromCandidateMethodTypeParameter.getTypeMirror(); + } } + DeclaredType typeArg = typeUtils.getDeclaredType( typeFromCandidateMethod.getTypeElement(), typeArgs ); + return typeFactory.getType( typeArg ); + } + else { + // its not a type var or generic parameterized (e.g. just a plain type) + return typeFromCandidateMethod; } - return Boolean.TRUE; } - } - - /** - * Looks through the list of type parameters of the candidate method for a match - * - * @param t type parameter to match - * - * @return matching type parameter - */ - private TypeParameterElement getTypeParamFromCandidate(TypeMirror t) { - for ( TypeParameterElement candidateTypeParam : candidateMethod.getExecutable().getTypeParameters() ) { - if ( typeUtils.isSameType( candidateTypeParam.asType(), t ) ) { - return candidateTypeParam; + boolean areEquivalent( Type a, Type b ) { + if ( a == null || b == null ) { + return false; } + return a.getBoxedEquivalent().equals( b.getBoxedEquivalent() ); } - return null; } - /** - * checks whether a type t is in bounds of the typeParameter tpe - * - * @return true if within bounds - */ - private boolean isWithinBounds(TypeMirror t, TypeParameterElement tpe) { - List bounds = tpe != null ? tpe.getBounds() : null; - if ( t != null && bounds != null ) { - for ( TypeMirror bound : bounds ) { - if ( !( bound.getKind() == TypeKind.DECLARED && typeUtils.isSubtypeErased( t, bound ) ) ) { - return false; - } - } - return true; - } - return false; + private static class TypeVarCandidate { + + private Type match; + private List pairs = new ArrayList<>(); + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index bac1346d97..83898d4b38 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import org.mapstruct.ap.internal.util.TypeUtils; @@ -56,6 +57,7 @@ public class SourceMethod implements Method { private final List sourceParameters; private final List contextParameters; private final ParameterProvidedMethods contextProvidedMethods; + private final List typeParameters; private List parameterNames; @@ -89,6 +91,8 @@ public static class Builder { private List valueMappings; private EnumMappingOptions enumMappingOptions; private ParameterProvidedMethods contextProvidedMethods; + private List typeParameters; + private boolean verboseLogging; public Builder setDeclaringMapper(Type declaringMapper) { @@ -197,6 +201,12 @@ public SourceMethod build() { valueMappings ); + this.typeParameters = this.executable.getTypeParameters() + .stream() + .map( Element::asType ) + .map( typeFactory::getType ) + .collect( Collectors.toList() ); + return new SourceMethod( this, mappingMethodOptions ); } } @@ -214,6 +224,7 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) this.sourceParameters = Parameter.getSourceParameters( parameters ); this.contextParameters = Parameter.getContextParameters( parameters ); this.contextProvidedMethods = builder.contextProvidedMethods; + this.typeParameters = builder.typeParameters; this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters ); this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters ); @@ -533,6 +544,11 @@ public boolean hasObjectFactoryAnnotation() { return hasObjectFactoryAnnotation; } + @Override + public List getTypeParameters() { + return this.typeParameters; + } + @Override public String describe() { if ( verboseLogging ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java index 7854534ed6..e54a8ea4b8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java @@ -67,7 +67,7 @@ public boolean matches(List sourceTypes, Type targetType) { Type sourceType = first( sourceTypes ); - Type returnType = getReturnType().resolveTypeVarToType( sourceType, getParameter().getType() ); + Type returnType = getReturnType().resolveParameterToType( sourceType, getParameter().getType() ).getMatch(); if ( returnType == null ) { return false; } @@ -151,6 +151,11 @@ public String getContextParameter(ConversionContext conversionContext) { return null; } + @Override + public List getTypeParameters() { + return Collections.emptyList(); + } + /** * hashCode based on class * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java index be729220ce..0e24774025 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java @@ -30,9 +30,11 @@ public class CreateOrUpdateSelector implements MethodSelector { @Override public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, Type targetType, - SelectionCriteria criteria) { + List> methods, + List sourceTypes, + Type mappingTargetType, + Type returnType, + SelectionCriteria criteria) { if ( criteria.isLifecycleCallbackRequired() || criteria.isObjectFactoryRequired() ) { return methods; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/FactoryParameterSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/FactoryParameterSelector.java index 4e52731225..41d37e8b69 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/FactoryParameterSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/FactoryParameterSelector.java @@ -22,10 +22,10 @@ public class FactoryParameterSelector implements MethodSelector { @Override public List> getMatchingMethods(Method mappingMethod, - List> methods, - ListsourceTypes, - Type targetType, - SelectionCriteria criteria) { + List> methods, + List sourceTypes, + Type mappingTargetType, Type returnType, + SelectionCriteria criteria) { if ( !criteria.isObjectFactoryRequired() || methods.size() <= 1 ) { return methods; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java index 3866857bbe..a624c1accb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java @@ -23,10 +23,10 @@ public class InheritanceSelector implements MethodSelector { @Override public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type targetType, - SelectionCriteria criteria) { + List> methods, + List sourceTypes, + Type mappingTargetType, Type returnType, + SelectionCriteria criteria) { if ( sourceTypes.size() != 1 ) { return methods; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java index c4a029da90..7e7925ca0e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java @@ -21,9 +21,11 @@ public class MethodFamilySelector implements MethodSelector { @Override public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type targetType, SelectionCriteria criteria) { + List> methods, + List sourceTypes, + Type mappingTargetType, + Type returnType, + SelectionCriteria criteria) { List> result = new ArrayList<>( methods.size() ); for ( SelectedMethod method : methods ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelector.java index 587a37a7fe..86d35bb3aa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelector.java @@ -26,12 +26,15 @@ interface MethodSelector { * @param mappingMethod mapping method, defined in Mapper for which this selection is carried out * @param candidates list of available methods * @param sourceTypes parameter type(s) that should be matched - * @param targetType result type that should be matched + * @param mappingTargetType mappingTargetType that should be matched + * @param returnType return type that should be matched * @param criteria criteria used in the selection process * @return list of methods that passes the matching process */ List> getMatchingMethods(Method mappingMethod, - List> candidates, - List sourceTypes, - Type targetType, SelectionCriteria criteria); + List> candidates, + List sourceTypes, + Type mappingTargetType, + Type returnType, + SelectionCriteria criteria); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java index 74fedcb75d..b88de4f225 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java @@ -8,13 +8,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.Method; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.TypeUtils; /** * Applies all known {@link MethodSelector}s in order. @@ -45,13 +45,17 @@ public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils, TypeFacto * @param mappingMethod mapping method, defined in Mapper for which this selection is carried out * @param methods list of available methods * @param sourceTypes parameter type(s) that should be matched - * @param targetType return type that should be matched + * @param mappingTargetType the mapping target type that should be matched + * @param returnType return type that should be matched * @param criteria criteria used in the selection process * @return list of methods that passes the matching process */ - public List> getMatchingMethods(Method mappingMethod, List methods, - List sourceTypes, Type targetType, - SelectionCriteria criteria) { + public List> getMatchingMethods(Method mappingMethod, + List methods, + List sourceTypes, + Type mappingTargetType, + Type returnType, + SelectionCriteria criteria) { List> candidates = new ArrayList<>( methods.size() ); for ( T method : methods ) { @@ -63,7 +67,8 @@ public List> getMatchingMethods(Method mapp mappingMethod, candidates, sourceTypes, - targetType, + mappingTargetType, + returnType, criteria ); } return candidates; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java index bca471f9c0..210e462e3e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java @@ -50,9 +50,11 @@ public QualifierSelector(TypeUtils typeUtils, ElementUtils elementUtils ) { @Override public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, Type targetType, - SelectionCriteria criteria) { + List> methods, + List sourceTypes, + Type mappingTargetType, + Type returnType, + SelectionCriteria criteria) { int numberOfQualifiersToMatch = 0; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java index ad1a5d47b5..38a907aedb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java @@ -32,9 +32,11 @@ public TargetTypeSelector( TypeUtils typeUtils ) { @Override public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, Type targetType, - SelectionCriteria criteria) { + List> methods, + List sourceTypes, + Type mappingTargetType, + Type returnType, + SelectionCriteria criteria) { TypeMirror qualifyingTypeMirror = criteria.getQualifyingResultType(); if ( qualifyingTypeMirror != null && !criteria.isLifecycleCallbackRequired() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java index c00a90760d..3d0bd4b9b4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java @@ -39,9 +39,11 @@ public TypeSelector(TypeFactory typeFactory, FormattingMessager messager) { @Override public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, Type targetType, - SelectionCriteria criteria) { + List> methods, + List sourceTypes, + Type mappingTargetType, + Type returnType, + SelectionCriteria criteria) { if ( methods.isEmpty() ) { return methods; @@ -54,12 +56,16 @@ public List> getMatchingMethods(Method mapp // if no source types are given, we have a factory or lifecycle method availableBindings = getAvailableParameterBindingsFromMethod( mappingMethod, - targetType, + mappingTargetType, criteria.getSourceRHS() ); } else { - availableBindings = getAvailableParameterBindingsFromSourceTypes( sourceTypes, targetType, mappingMethod ); + availableBindings = getAvailableParameterBindingsFromSourceTypes( + sourceTypes, + mappingTargetType, + mappingMethod + ); } for ( SelectedMethod method : methods ) { @@ -68,7 +74,7 @@ public List> getMatchingMethods(Method mapp if ( parameterBindingPermutations != null ) { SelectedMethod matchingMethod = - getMatchingParameterBinding( targetType, mappingMethod, method, parameterBindingPermutations ); + getMatchingParameterBinding( returnType, mappingMethod, method, parameterBindingPermutations ); if ( matchingMethod != null ) { result.add( matchingMethod ); @@ -143,7 +149,7 @@ else if ( pb.isTargetType() ) { } } - private SelectedMethod getMatchingParameterBinding(Type targetType, + private SelectedMethod getMatchingParameterBinding(Type returnType, Method mappingMethod, SelectedMethod selectedMethodInfo, List> parameterAssignmentVariants) { @@ -155,7 +161,7 @@ private SelectedMethod getMatchingParameterBinding(Type ta // remove all assignment variants that doesn't match the types from the method matchingParameterAssignmentVariants.removeIf( parameterAssignments -> - !selectedMethod.matches( extractTypes( parameterAssignments ), targetType ) + !selectedMethod.matches( extractTypes( parameterAssignments ), returnType ) ); if ( matchingParameterAssignmentVariants.isEmpty() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java index ecf6657991..7bdae0b771 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java @@ -46,7 +46,9 @@ public XmlElementDeclSelector(TypeUtils typeUtils) { @Override public List> getMatchingMethods(Method mappingMethod, List> methods, - List sourceTypes, Type targetType, + List sourceTypes, + Type mappingTargetType, + Type returnType, SelectionCriteria criteria) { List> nameMatches = new ArrayList<>(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 5985d735c2..b959a15dc0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -513,14 +513,14 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List List> getBestMatch(List methods, methods, singletonList( source ), target, + target, selectionCriteria ); } @@ -758,7 +759,7 @@ private MethodMethod getBestMatch(Type sourceType, Type targetType) { attempt.selectionCriteria.setPreferUpdateMapping( false ); for ( T2 yCandidate : yMethods ) { Type ySourceType = yCandidate.getMappingSourceType(); - ySourceType = ySourceType.resolveTypeVarToType( targetType, yCandidate.getResultType() ); + ySourceType = ySourceType.resolveParameterToType( targetType, yCandidate.getResultType() ).getMatch(); Type yTargetType = yCandidate.getResultType(); if ( ySourceType == null || !yTargetType.isRawAssignableTo( targetType ) @@ -876,7 +877,7 @@ private ConversionMethod getBestMatch(Type sourceType, Type targetType) { for ( T yCandidate : methods ) { Type ySourceType = yCandidate.getMappingSourceType(); - ySourceType = ySourceType.resolveTypeVarToType( targetType, yCandidate.getResultType() ); + ySourceType = ySourceType.resolveParameterToType( targetType, yCandidate.getResultType() ).getMatch(); Type yTargetType = yCandidate.getResultType(); if ( ySourceType == null || !yTargetType.isRawAssignableTo( targetType ) @@ -993,7 +994,7 @@ private MethodConversion getBestMatch(Type sourceType, Type targetType) { for ( T xCandidate : methods ) { Type xTargetType = xCandidate.getReturnType(); Type xSourceType = xCandidate.getMappingSourceType(); - xTargetType = xTargetType.resolveTypeVarToType( sourceType, xSourceType ); + xTargetType = xTargetType.resolveParameterToType( sourceType, xSourceType ).getMatch(); if ( xTargetType == null || xCandidate.isUpdateMethod() || !sourceType.isRawAssignableTo( xSourceType ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseTypeUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseTypeUtilsDecorator.java index ce01d99de8..380df67bd5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseTypeUtilsDecorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseTypeUtilsDecorator.java @@ -6,10 +6,49 @@ package org.mapstruct.ap.internal.util; import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; +import javax.lang.model.util.Types; public class EclipseTypeUtilsDecorator extends AbstractTypeUtilsDecorator { + private final Types delegate; + EclipseTypeUtilsDecorator(ProcessingEnvironment processingEnv) { super( processingEnv ); + this.delegate = processingEnv.getTypeUtils(); + } + + @Override + public boolean contains(TypeMirror t1, TypeMirror t2) { + if ( TypeKind.TYPEVAR == t2.getKind() ) { + return containsType( t1, ( (TypeVariable) t2 ).getLowerBound() ); + } + else { + return containsType( t1, t2 ); + } } + + private boolean containsType(TypeMirror t1, TypeMirror t2) { + + boolean result = false; + if ( TypeKind.DECLARED == t2.getKind() ) { + if ( TypeKind.WILDCARD == t1.getKind() ) { + WildcardType wct = (WildcardType) t1; + if ( wct.getExtendsBound() != null ) { + result = isAssignable( t2, wct.getExtendsBound() ); + } + else if ( wct.getSuperBound() != null ) { + result = isAssignable( wct.getSuperBound(), t2 ); + } + else { + result = isAssignable( t2, wct ); + } + } + } + return result; + } + } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/common/Type.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/common/Type.ftl index 89b2279a6d..ee7d0b106f 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/common/Type.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/common/Type.ftl @@ -7,9 +7,9 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.common.Type" --> <@compress single_line=true> - <#if wildCardExtendsBound> + <#if hasExtendsBound()> ? extends <@includeModel object=typeBound /> - <#elseif wildCardSuperBound> + <#elseif hasSuperBound()> ? super <@includeModel object=typeBound /> <#else> <#if ext.asVarArgs!false>${createReferenceName()?remove_ending("[]")}...<#else>${createReferenceName()}<#if (!ext.raw?? && typeParameters?size > 0) ><<#list typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, > diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/Issue1482Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/Issue1482Test.java index 74f6923734..4b77eb2c34 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/Issue1482Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/Issue1482Test.java @@ -20,8 +20,6 @@ Source2.class, Target.class, SourceEnum.class, - SourceTargetMapper.class, - TargetSourceMapper.class, BigDecimalWrapper.class, ValueWrapper.class }) @@ -30,6 +28,7 @@ public class Issue1482Test { @Test + @WithClasses( SourceTargetMapper.class ) public void testForward() { Source source = new Source(); @@ -45,6 +44,7 @@ public void testForward() { } @Test + @WithClasses( TargetSourceMapper.class ) public void testReverse() { Target target = new Target(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/TargetSourceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/TargetSourceMapper.java index d589927a19..401d0c42ae 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/TargetSourceMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/TargetSourceMapper.java @@ -20,9 +20,9 @@ public abstract class TargetSourceMapper { @Mapping(target = "wrapper", source = "bigDecimal") abstract Source2 map(Target target); - protected > Enum map(String in, @TargetType Classclz ) { + protected > T map(String in, @TargetType Classclz ) { if ( clz.isAssignableFrom( SourceEnum.class )) { - return (Enum) SourceEnum.valueOf( in ); + return (T) SourceEnum.valueOf( in ); } return null; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java index 1a07dd1f78..1782ebdb57 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/SourceTargetMapper.java @@ -37,8 +37,6 @@ public abstract class SourceTargetMapper { @InheritConfiguration public abstract Target sourceToTargetTwoArg(Source source, @MappingTarget Target target); - public abstract Set integerSetToStringSet(Set integers); - @InheritInverseConfiguration public abstract Set stringSetToIntegerSet(Set strings); diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java index 9d17f1725b..2e86c96055 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java @@ -5,6 +5,8 @@ */ package org.mapstruct.ap.test.selection.generics; +import static org.assertj.core.api.Assertions.assertThat; + import java.math.BigDecimal; import org.junit.Test; @@ -17,8 +19,6 @@ import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -import static org.assertj.core.api.Assertions.assertThat; - /** * Tests for the invocation of generic methods for mapping bean properties. * @@ -39,7 +39,6 @@ public void shouldApplyGenericTypeMapper() { // setup used types TypeB typeB = new TypeB(); - TypeC typeC = new TypeC(); // setup source Source source = new Source(); @@ -51,10 +50,7 @@ public void shouldApplyGenericTypeMapper() { source.setFooNested( new Wrapper<>( new Wrapper<>( new BigDecimal( 5 ) ) ) ); source.setFooUpperBoundCorrect( new UpperBoundWrapper<>( typeB ) ); source.setFooWildCardExtendsString( new WildCardExtendsWrapper<>( "test3" ) ); - source.setFooWildCardExtendsTypeCCorrect( new WildCardExtendsWrapper<>( typeC ) ); - source.setFooWildCardExtendsTypeBCorrect( new WildCardExtendsWrapper<>( typeB ) ); source.setFooWildCardSuperString( new WildCardSuperWrapper<>( "test4" ) ); - source.setFooWildCardExtendsMBTypeCCorrect( new WildCardExtendsMBWrapper<>( typeC ) ); source.setFooWildCardSuperTypeBCorrect( new WildCardSuperWrapper<>( typeB ) ); // define wrapper @@ -71,12 +67,8 @@ public void shouldApplyGenericTypeMapper() { assertThat( target.getFooNested() ).isEqualTo( new BigDecimal( 5 ) ); assertThat( target.getFooUpperBoundCorrect() ).isEqualTo( typeB ); assertThat( target.getFooWildCardExtendsString() ).isEqualTo( "test3" ); - assertThat( target.getFooWildCardExtendsTypeCCorrect() ).isEqualTo( typeC ); - assertThat( target.getFooWildCardExtendsTypeBCorrect() ).isEqualTo( typeB ); assertThat( target.getFooWildCardSuperString() ).isEqualTo( "test4" ); - assertThat( target.getFooWildCardExtendsMBTypeCCorrect() ).isEqualTo( typeC ); assertThat( target.getFooWildCardSuperTypeBCorrect() ).isEqualTo( typeB ); - } @Test @@ -140,20 +132,6 @@ public void shouldFailOnWildCardMultipleBounds() { public void shouldFailOnSuperBounds1() { } - @Test - @WithClasses({ ErroneousSource5.class, ErroneousTarget5.class, ErroneousSourceTargetMapper5.class }) - @ExpectedCompilationOutcome(value = CompilationResult.FAILED, - diagnostics = { - @Diagnostic(type = ErroneousSourceTargetMapper5.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 16, - message = "No target bean properties found: can't map property \"WildCardSuperWrapper " + - "fooWildCardSuperTypeCFailure\" to \"TypeC fooWildCardSuperTypeCFailure\". " + - "Consider to declare/implement a mapping method: \"TypeC map(WildCardSuperWrapper value)\".") - }) - public void shouldFailOnSuperBounds2() { - } - @Test @WithClasses({ ErroneousSource6.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericTypeMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericTypeMapper.java index 33b93473b1..4dcd033866 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericTypeMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericTypeMapper.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.selection.generics; -import java.io.Serializable; - public class GenericTypeMapper { public T getWrapped(Wrapper source) { @@ -53,7 +51,4 @@ public String getWildCardSupersString(WildCardSuperWrapper t) { return (String) t.getWrapped(); } - public T getWildCardExtendsMBType(WildCardExtendsMBWrapper t) { - return t.getWrapped(); - } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/Source.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/Source.java index ce99476501..28c724e9ae 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/Source.java @@ -17,10 +17,7 @@ public class Source { private Wrapper> fooNested; private UpperBoundWrapper fooUpperBoundCorrect; private WildCardExtendsWrapper fooWildCardExtendsString; - private WildCardExtendsWrapper fooWildCardExtendsTypeCCorrect; - private WildCardExtendsWrapper fooWildCardExtendsTypeBCorrect; private WildCardSuperWrapper fooWildCardSuperString; - private WildCardExtendsMBWrapper fooWildCardExtendsMBTypeCCorrect; private WildCardSuperWrapper fooWildCardSuperTypeBCorrect; public Wrapper getFooInteger() { @@ -87,22 +84,6 @@ public void setFooWildCardExtendsString(WildCardExtendsWrapper fooWildCa this.fooWildCardExtendsString = fooWildCardExtendsString; } - public void setFooWildCardExtendsTypeCCorrect(WildCardExtendsWrapper fooWildCardExtendsTypeCCorrect) { - this.fooWildCardExtendsTypeCCorrect = fooWildCardExtendsTypeCCorrect; - } - - public WildCardExtendsWrapper getFooWildCardExtendsTypeCCorrect() { - return fooWildCardExtendsTypeCCorrect; - } - - public WildCardExtendsWrapper getFooWildCardExtendsTypeBCorrect() { - return fooWildCardExtendsTypeBCorrect; - } - - public void setFooWildCardExtendsTypeBCorrect(WildCardExtendsWrapper fooWildCardExtendsTypeBCorrect) { - this.fooWildCardExtendsTypeBCorrect = fooWildCardExtendsTypeBCorrect; - } - public WildCardSuperWrapper getFooWildCardSuperString() { return fooWildCardSuperString; } @@ -111,14 +92,6 @@ public void setFooWildCardSuperString(WildCardSuperWrapper fooWildCardSu this.fooWildCardSuperString = fooWildCardSuperString; } - public WildCardExtendsMBWrapper getFooWildCardExtendsMBTypeCCorrect() { - return fooWildCardExtendsMBTypeCCorrect; - } - - public void setFooWildCardExtendsMBTypeCCorrect(WildCardExtendsMBWrapper fooWildCardExtendsMBTypeCCorrect) { - this.fooWildCardExtendsMBTypeCCorrect = fooWildCardExtendsMBTypeCCorrect; - } - public WildCardSuperWrapper getFooWildCardSuperTypeBCorrect() { return fooWildCardSuperTypeBCorrect; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/Target.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/Target.java index 85d672ca94..6169356743 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/Target.java @@ -17,10 +17,7 @@ public class Target { private BigDecimal fooNested; private TypeB fooUpperBoundCorrect; private String fooWildCardExtendsString; - private TypeC fooWildCardExtendsTypeCCorrect; - private TypeB fooWildCardExtendsTypeBCorrect; private String fooWildCardSuperString; - private TypeC fooWildCardExtendsMBTypeCCorrect; private TypeB fooWildCardSuperTypeBCorrect; public Integer getFooInteger() { @@ -87,22 +84,6 @@ public void setFooWildCardExtendsString(String fooWildCardExtendsString) { this.fooWildCardExtendsString = fooWildCardExtendsString; } - public TypeC getFooWildCardExtendsTypeCCorrect() { - return fooWildCardExtendsTypeCCorrect; - } - - public void setFooWildCardExtendsTypeCCorrect(TypeC fooWildCardExtendsTypeCCorrect) { - this.fooWildCardExtendsTypeCCorrect = fooWildCardExtendsTypeCCorrect; - } - - public TypeB getFooWildCardExtendsTypeBCorrect() { - return fooWildCardExtendsTypeBCorrect; - } - - public void setFooWildCardExtendsTypeBCorrect(TypeB fooWildCardExtendsTypeBCorrect) { - this.fooWildCardExtendsTypeBCorrect = fooWildCardExtendsTypeBCorrect; - } - public String getFooWildCardSuperString() { return fooWildCardSuperString; } @@ -111,14 +92,6 @@ public void setFooWildCardSuperString(String fooWildCardSuperString) { this.fooWildCardSuperString = fooWildCardSuperString; } - public TypeC getFooWildCardExtendsMBTypeCCorrect() { - return fooWildCardExtendsMBTypeCCorrect; - } - - public void setFooWildCardExtendsMBTypeCCorrect(TypeC fooWildCardExtendsMBTypeCCorrect) { - this.fooWildCardExtendsMBTypeCCorrect = fooWildCardExtendsMBTypeCCorrect; - } - public TypeB getFooWildCardSuperTypeBCorrect() { return fooWildCardSuperTypeBCorrect; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/BothParameterizedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/BothParameterizedMapper.java new file mode 100644 index 0000000000..1c0ddfdc57 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/BothParameterizedMapper.java @@ -0,0 +1,75 @@ +/* + * 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.selection.methodgenerics.array; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface BothParameterizedMapper { + + BothParameterizedMapper INSTANCE = Mappers.getMapper( BothParameterizedMapper.class ); + + Target sourceToTarget(Source source); + + default GenericTargetWrapper map(GenericSourceWrapper in ) { + return new GenericTargetWrapper<>( in.getWrapped() ); + } + + class Source { + + private final GenericSourceWrapper prop; + + public Source(GenericSourceWrapper prop) { + this.prop = prop; + } + + public GenericSourceWrapper getProp() { + return prop; + } + + } + + class Target { + + private GenericTargetWrapper prop; + + public GenericTargetWrapper getProp() { + return prop; + } + + public void setProp(GenericTargetWrapper prop) { + this.prop = prop; + } + } + + class GenericTargetWrapper { + private final T wrapped; + + public GenericTargetWrapper(T someType) { + this.wrapped = someType; + } + + public T getWrapped() { + return wrapped; + } + } + + class GenericSourceWrapper { + private final T wrapped; + + public GenericSourceWrapper(T someType) { + this.wrapped = someType; + } + + public T getWrapped() { + return wrapped; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/GenericArrayTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/GenericArrayTest.java new file mode 100644 index 0000000000..0b6dbc1137 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/GenericArrayTest.java @@ -0,0 +1,64 @@ +/* + * 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.selection.methodgenerics.array; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Sjaak Derksen + * + */ +@RunWith(AnnotationProcessorTestRunner.class) +public class GenericArrayTest { + + @Test + @WithClasses( ReturnTypeIsTypeVarArrayMapper.class ) + public void testGenericReturnTypeVar() { + + ReturnTypeIsTypeVarArrayMapper.GenericWrapper wrapper = + new ReturnTypeIsTypeVarArrayMapper.GenericWrapper<>( "test" ); + ReturnTypeIsTypeVarArrayMapper.Source source = new ReturnTypeIsTypeVarArrayMapper.Source( wrapper ); + + ReturnTypeIsTypeVarArrayMapper.Target target = ReturnTypeIsTypeVarArrayMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).containsExactly( "test" ); + } + + @Test + @WithClasses( SourceTypeIsTypeVarArrayMapper.class ) + public void testGenericSourceTypeVar() { + + SourceTypeIsTypeVarArrayMapper.Source source = + new SourceTypeIsTypeVarArrayMapper.Source( new String[] { "test" } ); + SourceTypeIsTypeVarArrayMapper.Target target = SourceTypeIsTypeVarArrayMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp().getWrapped() ).isEqualTo( "test" ); + + } + + @Test + @WithClasses( BothParameterizedMapper.class ) + public void testBothParameterized() { + + BothParameterizedMapper.GenericSourceWrapper wrapper = + new BothParameterizedMapper.GenericSourceWrapper<>( new String[] { "test" } ); + BothParameterizedMapper.Source source = new BothParameterizedMapper.Source( wrapper ); + BothParameterizedMapper.Target target = BothParameterizedMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp().getWrapped() ).containsExactly( "test" ); + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/ReturnTypeIsTypeVarArrayMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/ReturnTypeIsTypeVarArrayMapper.java new file mode 100644 index 0000000000..76751f2cc8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/ReturnTypeIsTypeVarArrayMapper.java @@ -0,0 +1,71 @@ +/* + * 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.selection.methodgenerics.array; + +import java.lang.reflect.Array; +import java.util.Collections; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ReturnTypeIsTypeVarArrayMapper { + + ReturnTypeIsTypeVarArrayMapper INSTANCE = Mappers.getMapper( ReturnTypeIsTypeVarArrayMapper.class ); + + Target sourceToTarget(Source source); + + @SuppressWarnings( "unchecked" ) + default T[] map(GenericWrapper in) { + return Collections.singletonList( in.getWrapped() ) + .toArray( (T[]) Array.newInstance( in.getWrapped().getClass(), 1 ) ); + } + + class Source { + + private GenericWrapper prop; + + public Source(GenericWrapper prop) { + this.prop = prop; + } + + public GenericWrapper getProp() { + return prop; + } + + public void setProp(GenericWrapper prop) { + this.prop = prop; + } + } + + class Target { + + private String[] prop; + + public String[] getProp() { + return prop; + } + + public void setProp(String[] prop) { + this.prop = prop; + } + } + + class GenericWrapper { + private final T wrapped; + + public GenericWrapper(T someType) { + this.wrapped = someType; + } + + public T getWrapped() { + return wrapped; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/SourceTypeIsTypeVarArrayMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/SourceTypeIsTypeVarArrayMapper.java new file mode 100644 index 0000000000..4abeb4b59f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/SourceTypeIsTypeVarArrayMapper.java @@ -0,0 +1,67 @@ +/* + * 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.selection.methodgenerics.array; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface SourceTypeIsTypeVarArrayMapper { + + SourceTypeIsTypeVarArrayMapper INSTANCE = Mappers.getMapper( SourceTypeIsTypeVarArrayMapper.class ); + + Target sourceToTarget(Source source); + + @SuppressWarnings("unchecked") + default GenericWrapper map( T[] in ) { + if ( in.length > 0 ) { + return new GenericWrapper<>( in[0] ); + } + return null; + } + + class Source { + + private String[] prop; + + public Source(String[] prop) { + this.prop = prop; + } + + public String[] getProp() { + return prop; + } + + } + + class Target { + + private GenericWrapper prop; + + public GenericWrapper getProp() { + return prop; + } + + public void setProp(GenericWrapper prop) { + this.prop = prop; + } + } + + class GenericWrapper { + private final T wrapped; + + public GenericWrapper(T someType) { + this.wrapped = someType; + } + + public T getWrapped() { + return wrapped; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/BoundsTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/BoundsTest.java new file mode 100644 index 0000000000..c181cfe0b9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/BoundsTest.java @@ -0,0 +1,36 @@ +/* + * 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.selection.methodgenerics.bounds; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Sjaak Derksen + * + */ +@RunWith( AnnotationProcessorTestRunner.class ) +public class BoundsTest { + + @Test + @WithClasses( SourceTypeIsBoundedTypeVarMapper.class ) + public void testGenericSourceTypeVar() { + + SourceTypeIsBoundedTypeVarMapper.Source source = new SourceTypeIsBoundedTypeVarMapper.Source( "5", "test" ); + SourceTypeIsBoundedTypeVarMapper.Target target = + SourceTypeIsBoundedTypeVarMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp1() ).isEqualTo( 5L ); + assertThat( target.getProp2().getProp() ).isEqualTo( "test" ); + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/SourceTypeIsBoundedTypeVarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/SourceTypeIsBoundedTypeVarMapper.java new file mode 100644 index 0000000000..4aa260a736 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/SourceTypeIsBoundedTypeVarMapper.java @@ -0,0 +1,87 @@ +/* + * 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.selection.methodgenerics.bounds; + +import org.mapstruct.Mapper; +import org.mapstruct.TargetType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface SourceTypeIsBoundedTypeVarMapper { + + SourceTypeIsBoundedTypeVarMapper INSTANCE = Mappers.getMapper( SourceTypeIsBoundedTypeVarMapper.class ); + + Target sourceToTarget(Source source); + + @SuppressWarnings( "unchecked" ) + default T map(String in, @TargetType Class clz) { + if ( clz == Nested.class ) { + return (T) new Nested( in ); + } + return null; + } + + class Source { + + private final String prop1; + private final String prop2; + + public Source(String prop1, String prop2) { + this.prop1 = prop1; + this.prop2 = prop2; + } + + public String getProp1() { + return prop1; + } + + public String getProp2() { + return prop2; + } + } + + class Target { + + private Long prop1; + private Nested prop2; + + public Long getProp1() { + return prop1; + } + + public void setProp1(Long prop1) { + this.prop1 = prop1; + } + + public Nested getProp2() { + return prop2; + } + + public void setProp2(Nested prop2) { + this.prop2 = prop2; + } + } + + class NestedBase { + } + + class Nested extends NestedBase { + + private String prop; + + public Nested(String prop) { + this.prop = prop; + } + + public String getProp() { + return prop; + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java new file mode 100644 index 0000000000..b11162bb33 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java @@ -0,0 +1,73 @@ +/* + * 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.selection.methodgenerics.multiple; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import java.util.Collections; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Sjaak Derksen + * + */ +@RunWith( AnnotationProcessorTestRunner.class) +public class MultipleTypeVarTest { + + @Test + @WithClasses( ReturnTypeHasMultipleTypeVarOneGenericMapper.class ) + public void testGenericSourceTypeVarOneGeneric() { + + ReturnTypeHasMultipleTypeVarOneGenericMapper.Source src = + new ReturnTypeHasMultipleTypeVarOneGenericMapper.Source( 5L ); + ReturnTypeHasMultipleTypeVarOneGenericMapper.Target target = + ReturnTypeHasMultipleTypeVarOneGenericMapper.INSTANCE.toTarget( src ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) ); + } + + @Test + @WithClasses( ReturnTypeHasMultipleTypeVarBothGenericMapper.class ) + public void testGenericReturnTypeVarBothGeneric() { + + ReturnTypeHasMultipleTypeVarBothGenericMapper.Pair pair + = new ReturnTypeHasMultipleTypeVarBothGenericMapper.Pair( "test", 5L ); + ReturnTypeHasMultipleTypeVarBothGenericMapper.Source src = + new ReturnTypeHasMultipleTypeVarBothGenericMapper.Source( pair ); + ReturnTypeHasMultipleTypeVarBothGenericMapper.Target target = + ReturnTypeHasMultipleTypeVarBothGenericMapper.INSTANCE.toTarget( src ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) ); + } + + @Test + @WithClasses( SourceTypeHasMultipleTypeVarBothGenericMapper.class ) + public void testGenericSourceTypeVarBothGeneric() { + + Map map = Collections.singletonMap( "test", 5L ); + SourceTypeHasMultipleTypeVarBothGenericMapper.Source src = + new SourceTypeHasMultipleTypeVarBothGenericMapper.Source( map ); + SourceTypeHasMultipleTypeVarBothGenericMapper.Target target = + SourceTypeHasMultipleTypeVarBothGenericMapper.INSTANCE.toTarget( src ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp().getFirst() ).isEqualTo( "test" ); + assertThat( target.getProp().getSecond() ).isEqualTo( 5L ); + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarBothGenericMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarBothGenericMapper.java new file mode 100644 index 0000000000..b31a190b60 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarBothGenericMapper.java @@ -0,0 +1,76 @@ +/* + * 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.selection.methodgenerics.multiple; + +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ReturnTypeHasMultipleTypeVarBothGenericMapper { + + ReturnTypeHasMultipleTypeVarBothGenericMapper INSTANCE = + Mappers.getMapper( ReturnTypeHasMultipleTypeVarBothGenericMapper.class ); + + Target toTarget(Source source); + + default HashMap toMap( Pair entry) { + HashMap result = new HashMap<>( ); + result.put( entry.first, entry.second ); + return result; + } + + class Source { + + private Pair prop; + + public Source(Pair prop) { + this.prop = prop; + } + + public Pair getProp() { + return prop; + } + } + + class Target { + + private Map prop; + + public Map getProp() { + return prop; + } + + public Target setProp(Map prop) { + this.prop = prop; + return this; + } + } + + class Pair { + private final T first; + private final U second; + + public Pair(T first, U second) { + this.first = first; + this.second = second; + } + + public T getFirst() { + return first; + } + + public U getSecond() { + return second; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarOneGenericMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarOneGenericMapper.java new file mode 100644 index 0000000000..f22741a87f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarOneGenericMapper.java @@ -0,0 +1,58 @@ +/* + * 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.selection.methodgenerics.multiple; + +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ReturnTypeHasMultipleTypeVarOneGenericMapper { + + ReturnTypeHasMultipleTypeVarOneGenericMapper INSTANCE = + Mappers.getMapper( ReturnTypeHasMultipleTypeVarOneGenericMapper.class ); + + Target toTarget(Source source); + + default HashMap toMap( T entry) { + HashMap result = new HashMap<>( ); + result.put( "test", entry ); + return result; + } + + class Source { + + private Long prop; + + public Source(Long prop) { + this.prop = prop; + } + + public Long getProp() { + return prop; + } + } + + class Target { + + private Map prop; + + public Map getProp() { + return prop; + } + + public Target setProp(Map prop) { + this.prop = prop; + return this; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/SourceTypeHasMultipleTypeVarBothGenericMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/SourceTypeHasMultipleTypeVarBothGenericMapper.java new file mode 100644 index 0000000000..09c427fd05 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/SourceTypeHasMultipleTypeVarBothGenericMapper.java @@ -0,0 +1,86 @@ +/* + * 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.selection.methodgenerics.multiple; + +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface SourceTypeHasMultipleTypeVarBothGenericMapper { + + SourceTypeHasMultipleTypeVarBothGenericMapper INSTANCE = + Mappers.getMapper( SourceTypeHasMultipleTypeVarBothGenericMapper.class ); + + Target toTarget(Source source); + + default HashMap toMap( Pair entry) { + HashMap result = new HashMap<>( ); + result.put( entry.first, entry.second ); + return result; + } + + default Pair toPair( Map map) { + if ( !map.isEmpty() ) { + Map.Entry firstEntry = map.entrySet().iterator().next(); + return new Pair<>( firstEntry.getKey(), firstEntry.getValue() ); + } + return null; + } + + class Source { + + private final Map prop; + + public Source(Map prop) { + this.prop = prop; + } + + public Map getProp() { + return prop; + } + + } + + class Target { + + private Pair prop; + + public Target(Pair prop) { + this.prop = prop; + } + + public Pair getProp() { + return prop; + } + } + + class Pair { + + private final T first; + private final U second; + + public Pair(T first, U second) { + this.first = first; + this.second = second; + } + + public T getFirst() { + return first; + } + + public U getSecond() { + return second; + } + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/NestedGenericsTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/NestedGenericsTest.java new file mode 100644 index 0000000000..c177fb45e8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/NestedGenericsTest.java @@ -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.selection.methodgenerics.nestedgenerics; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Sjaak Derksen + * + */ +@RunWith( AnnotationProcessorTestRunner.class) +public class NestedGenericsTest { + + @Test + @WithClasses( ReturnTypeHasNestedTypeVarMapper.class ) + public void testGenericReturnTypeVar() { + + ReturnTypeHasNestedTypeVarMapper.Source source = new ReturnTypeHasNestedTypeVarMapper.Source("test" ); + ReturnTypeHasNestedTypeVarMapper.Target target = ReturnTypeHasNestedTypeVarMapper.INSTANCE.toTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).hasSize( 1 ); + assertThat( target.getProp().get( 0 ) ).contains( "test" ); + } + + @Test + @WithClasses( SourceTypeHasNestedTypeVarMapper.class ) + public void testGenericSourceTypeVar() { + + SourceTypeHasNestedTypeVarMapper.Source src = + new SourceTypeHasNestedTypeVarMapper.Source( Collections.singletonList( Collections.singleton( "test" ) ) ); + SourceTypeHasNestedTypeVarMapper.Target target = SourceTypeHasNestedTypeVarMapper.INSTANCE.toTarget( src ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isEqualTo( "test" ); + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/ReturnTypeHasNestedTypeVarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/ReturnTypeHasNestedTypeVarMapper.java new file mode 100644 index 0000000000..891058d885 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/ReturnTypeHasNestedTypeVarMapper.java @@ -0,0 +1,57 @@ +/* + * 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.selection.methodgenerics.nestedgenerics; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ReturnTypeHasNestedTypeVarMapper { + + ReturnTypeHasNestedTypeVarMapper INSTANCE = Mappers.getMapper( ReturnTypeHasNestedTypeVarMapper.class ); + + Target toTarget(Source source); + + default List> wrapAsSetInList(T entry) { + return Collections.singletonList( Collections.singleton( entry ) ); + } + + class Source { + + private final String prop; + + public Source(String prop) { + this.prop = prop; + } + + public String getProp() { + return prop; + } + + } + + class Target { + + private List> prop; + + public List> getProp() { + return prop; + } + + public Target setProp(List> prop) { + this.prop = prop; + return this; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/SourceTypeHasNestedTypeVarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/SourceTypeHasNestedTypeVarMapper.java new file mode 100644 index 0000000000..faeb5fe935 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/SourceTypeHasNestedTypeVarMapper.java @@ -0,0 +1,58 @@ +/* + * 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.selection.methodgenerics.nestedgenerics; + +import java.util.List; +import java.util.Set; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface SourceTypeHasNestedTypeVarMapper { + + SourceTypeHasNestedTypeVarMapper INSTANCE = Mappers.getMapper( SourceTypeHasNestedTypeVarMapper.class ); + + Target toTarget(Source source); + + default T unwrapToOneElement(List> listOfSet) { + if ( !listOfSet.isEmpty() && listOfSet.get( 0 ).iterator().hasNext() ) { + return listOfSet.get( 0 ).iterator().next(); + } + return null; + } + + class Source { + + private final List> prop; + + public Source(List> prop) { + this.prop = prop; + } + + public List> getProp() { + return prop; + } + + } + + class Target { + + private String prop; + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/BothParameterizedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/BothParameterizedMapper.java new file mode 100644 index 0000000000..16b09a2749 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/BothParameterizedMapper.java @@ -0,0 +1,75 @@ +/* + * 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.selection.methodgenerics.plain; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface BothParameterizedMapper { + + BothParameterizedMapper INSTANCE = Mappers.getMapper( BothParameterizedMapper.class ); + + Target sourceToTarget(Source source); + + default GenericTargetWrapper map(GenericSourceWrapper in ) { + return new GenericTargetWrapper<>( in.getWrapped() ); + } + + class Source { + + private final GenericSourceWrapper prop; + + public Source(GenericSourceWrapper prop) { + this.prop = prop; + } + + public GenericSourceWrapper getProp() { + return prop; + } + + } + + class Target { + + private GenericTargetWrapper prop; + + public GenericTargetWrapper getProp() { + return prop; + } + + public void setProp(GenericTargetWrapper prop) { + this.prop = prop; + } + } + + class GenericTargetWrapper { + private final T wrapped; + + public GenericTargetWrapper(T someType) { + this.wrapped = someType; + } + + public T getWrapped() { + return wrapped; + } + } + + class GenericSourceWrapper { + private final T wrapped; + + public GenericSourceWrapper(T someType) { + this.wrapped = someType; + } + + public T getWrapped() { + return wrapped; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/PlainTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/PlainTest.java new file mode 100644 index 0000000000..8f7c878810 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/PlainTest.java @@ -0,0 +1,75 @@ +/* + * 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.selection.methodgenerics.plain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.util.Collections; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Sjaak Derksen + * + */ +@RunWith(AnnotationProcessorTestRunner.class) +public class PlainTest { + + @Test + @WithClasses( ReturnTypeIsTypeVarMapper.class ) + public void testGenericReturnTypeVar() { + + ReturnTypeIsTypeVarMapper.Source source = + new ReturnTypeIsTypeVarMapper.Source( new ReturnTypeIsTypeVarMapper.GenericWrapper<>( "test" ) ); + ReturnTypeIsTypeVarMapper.Target target = ReturnTypeIsTypeVarMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isEqualTo( "test" ); + } + + @Test + @WithClasses( SourceTypeIsTypeVarMapper.class ) + public void testGenericSourceTypeVar() { + + SourceTypeIsTypeVarMapper.Source source = new SourceTypeIsTypeVarMapper.Source( "test" ); + SourceTypeIsTypeVarMapper.Target target = SourceTypeIsTypeVarMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp().getWrapped() ).isEqualTo( "test" ); + + } + + @Test + @WithClasses( BothParameterizedMapper.class ) + public void testBothParameterized() { + + BothParameterizedMapper.Source source = + new BothParameterizedMapper.Source( new BothParameterizedMapper.GenericSourceWrapper<>( "test" ) ); + BothParameterizedMapper.Target target = BothParameterizedMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp().getWrapped() ).isEqualTo( "test" ); + + } + + @Test + @WithClasses( ReturnTypeIsRawTypeMapper.class ) + public void testRaw() { + + ReturnTypeIsRawTypeMapper.Source source = new ReturnTypeIsRawTypeMapper.Source( Collections.singleton( 5 ) ); + + ReturnTypeIsRawTypeMapper.Target target = ReturnTypeIsRawTypeMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp().iterator().next() ).isEqualTo( "5" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/ReturnTypeIsRawTypeMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/ReturnTypeIsRawTypeMapper.java new file mode 100644 index 0000000000..24af41648c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/ReturnTypeIsRawTypeMapper.java @@ -0,0 +1,50 @@ +/* + * 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.selection.methodgenerics.plain; + +import java.util.Set; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ReturnTypeIsRawTypeMapper { + + ReturnTypeIsRawTypeMapper INSTANCE = Mappers.getMapper( ReturnTypeIsRawTypeMapper.class ); + + Target sourceToTarget(Source source); + + Set selectMe(Set integers); + + Set doNotSelectMe(Set strings); + + class Source { + + private final Set prop; + + public Source(Set prop) { + this.prop = prop; + } + + public Set getProp() { + return prop; + } + } + + class Target { + + private Set prop; + + public Set getProp() { + return prop; + } + + public Target setProp(Set prop) { + this.prop = prop; + return this; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/ReturnTypeIsTypeVarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/ReturnTypeIsTypeVarMapper.java new file mode 100644 index 0000000000..137f00ca62 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/ReturnTypeIsTypeVarMapper.java @@ -0,0 +1,64 @@ +/* + * 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.selection.methodgenerics.plain; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ReturnTypeIsTypeVarMapper { + + ReturnTypeIsTypeVarMapper INSTANCE = Mappers.getMapper( ReturnTypeIsTypeVarMapper.class ); + + Target sourceToTarget(Source source); + + @SuppressWarnings("unchecked") + default T map(GenericWrapper in) { + return in.getWrapped(); + } + + class Source { + + private final GenericWrapper prop; + + public Source(GenericWrapper prop) { + this.prop = prop; + } + + public GenericWrapper getProp() { + return prop; + } + + } + + class Target { + + private String prop; + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } + } + + class GenericWrapper { + private final T wrapped; + + public GenericWrapper(T someType) { + this.wrapped = someType; + } + + public T getWrapped() { + return wrapped; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/SourceTypeIsTypeVarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/SourceTypeIsTypeVarMapper.java new file mode 100644 index 0000000000..3f2c2a50ce --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/SourceTypeIsTypeVarMapper.java @@ -0,0 +1,63 @@ +/* + * 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.selection.methodgenerics.plain; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface SourceTypeIsTypeVarMapper { + + SourceTypeIsTypeVarMapper INSTANCE = Mappers.getMapper( SourceTypeIsTypeVarMapper.class ); + + Target sourceToTarget(Source source); + + @SuppressWarnings("unchecked") + default GenericWrapper map( T in ) { + return new GenericWrapper<>( in ); + } + + class Source { + + private final String prop; + + public Source(String prop) { + this.prop = prop; + } + + public String getProp() { + return prop; + } + } + + class Target { + + private GenericWrapper prop; + + public GenericWrapper getProp() { + return prop; + } + + public void setProp(GenericWrapper prop) { + this.prop = prop; + } + } + + class GenericWrapper { + private final T wrapped; + + public GenericWrapper(T someType) { + this.wrapped = someType; + } + + public T getWrapped() { + return wrapped; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/NestedTargetTypeMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/NestedTargetTypeMapper.java new file mode 100644 index 0000000000..665683f653 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/NestedTargetTypeMapper.java @@ -0,0 +1,75 @@ +/* + * 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.selection.methodgenerics.targettype; + +import org.mapstruct.Mapper; +import org.mapstruct.TargetType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface NestedTargetTypeMapper { + + NestedTargetTypeMapper INSTANCE = Mappers.getMapper( NestedTargetTypeMapper.class ); + + Target sourceToTarget(Source source); + + @SuppressWarnings("unchecked") + default T map(String string, @TargetType Class clazz) { + if ( clazz == GenericWrapper.class ) { + return (T) new GenericWrapper<>( string ); + } + + return null; + } + + class Source { + + private String prop; + + public Source(String prop) { + this.prop = prop; + } + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } + } + + class Target { + + private GenericWrapper prop; + + public GenericWrapper getProp() { + return prop; + } + + public void setProp(GenericWrapper prop) { + this.prop = prop; + } + } + + class BaseType { + } + + class GenericWrapper extends BaseType { + private final T wrapped; + + public GenericWrapper(T someType) { + this.wrapped = someType; + } + + public T getWrapped() { + return wrapped; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/PlainTargetTypeMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/PlainTargetTypeMapper.java new file mode 100644 index 0000000000..15e27da824 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/PlainTargetTypeMapper.java @@ -0,0 +1,66 @@ +/* + * 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.selection.methodgenerics.targettype; + +import java.math.BigDecimal; + +import org.mapstruct.Mapper; +import org.mapstruct.TargetType; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface PlainTargetTypeMapper { + + PlainTargetTypeMapper INSTANCE = Mappers.getMapper( PlainTargetTypeMapper.class ); + + Target sourceToTarget(Source source); + + @SuppressWarnings("unchecked") + default T map(String string, @TargetType Class clazz) { + if ( clazz == BigDecimal.class ) { + return (T) new BigDecimal( string ); + } + + return null; + } + + class Source { + + private String prop; + + public Source(String prop) { + this.prop = prop; + } + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } + } + + class Target { + + private BigDecimal prop; + + public BigDecimal getProp() { + return prop; + } + + public void setProp(BigDecimal prop) { + this.prop = prop; + } + } + + class BaseType { + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/TargetTypeTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/TargetTypeTest.java new file mode 100644 index 0000000000..47037ec6be --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/TargetTypeTest.java @@ -0,0 +1,43 @@ +/* + * 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.selection.methodgenerics.targettype; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Sjaak Derksen + * + */ +@RunWith(AnnotationProcessorTestRunner.class) +public class TargetTypeTest { + + @Test + @WithClasses( PlainTargetTypeMapper.class ) + public void testPlain() { + + PlainTargetTypeMapper.Target target = + PlainTargetTypeMapper.INSTANCE.sourceToTarget( new PlainTargetTypeMapper.Source( "15" ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp().toPlainString() ).isEqualTo( "15" ); + } + + @Test + @WithClasses( NestedTargetTypeMapper.class ) + public void testNestedTypeVar() { + NestedTargetTypeMapper.Target target = + NestedTargetTypeMapper.INSTANCE.sourceToTarget( new NestedTargetTypeMapper.Source( "test" ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp().getWrapped() ).isEqualTo( "test" ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/IntersectionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/IntersectionMapper.java new file mode 100644 index 0000000000..376acd0d57 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/IntersectionMapper.java @@ -0,0 +1,77 @@ +/* + * 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.selection.methodgenerics.wildcards; + +import java.io.Serializable; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface IntersectionMapper { + + IntersectionMapper INSTANCE = Mappers.getMapper( IntersectionMapper.class ); + + Target map( Source source); + + default T unwrap(Wrapper t) { + return t.getWrapped(); + } + + class Source { + + private final Wrapper prop; + + public Source(Wrapper prop) { + this.prop = prop; + } + + public Wrapper getProp() { + return prop; + } + + } + + class Wrapper { + + private final T wrapped; + + public Wrapper(T wrapped) { + this.wrapped = wrapped; + } + + public T getWrapped() { + return wrapped; + } + + } + + class Target { + + private TypeC prop; + + public TypeC getProp() { + return prop; + } + + public void setProp(TypeC prop) { + this.prop = prop; + } + } + + /** + * TypeC must intersect both TypeB & Serializable + */ + class TypeC extends TypeB implements Serializable { + } + + class TypeB extends TypeA { + } + + class TypeA { + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/SourceWildCardExtendsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/SourceWildCardExtendsMapper.java new file mode 100644 index 0000000000..24c8b77379 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/SourceWildCardExtendsMapper.java @@ -0,0 +1,87 @@ +/* + * 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.selection.methodgenerics.wildcards; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SourceWildCardExtendsMapper { + + SourceWildCardExtendsMapper INSTANCE = Mappers.getMapper( SourceWildCardExtendsMapper.class ); + + Target map( Source source); + + default T unwrap(Wrapper t) { + return t.getWrapped(); + } + + class Source { + + private final Wrapper propB; + private final Wrapper propC; + + public Source(Wrapper propB, Wrapper propC) { + this.propB = propB; + this.propC = propC; + } + + public Wrapper getPropB() { + return propB; + } + + public Wrapper getPropC() { + return propC; + } + + } + + class Wrapper { + + private final T wrapped; + + public Wrapper(T wrapped) { + this.wrapped = wrapped; + } + + public T getWrapped() { + return wrapped; + } + + } + + class Target { + + private TypeB propB; + private TypeC propC; + + public TypeB getPropB() { + return propB; + } + + public void setPropB(TypeB propB) { + this.propB = propB; + } + + public TypeC getPropC() { + return propC; + } + + public void setPropC(TypeC propC) { + this.propC = propC; + } + } + + class TypeC extends TypeB { + } + + class TypeB extends TypeA { + } + + class TypeA { + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java new file mode 100644 index 0000000000..1df99d4d9a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java @@ -0,0 +1,62 @@ +/* + * 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.selection.methodgenerics.wildcards; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import org.mapstruct.ap.testutil.runner.Compiler; +import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Sjaak Derksen + * + */ +@RunWith(AnnotationProcessorTestRunner.class) +public class WildCardTest { + + @Test + @WithClasses( SourceWildCardExtendsMapper.class ) + public void testExtendsRelation() { + + // prepare source + SourceWildCardExtendsMapper.TypeB typeB = new SourceWildCardExtendsMapper.TypeB(); + SourceWildCardExtendsMapper.Wrapper wrapperB = new SourceWildCardExtendsMapper.Wrapper( typeB ); + SourceWildCardExtendsMapper.TypeC typeC = new SourceWildCardExtendsMapper.TypeC(); + SourceWildCardExtendsMapper.Wrapper wrapperC = new SourceWildCardExtendsMapper.Wrapper( typeC ); + SourceWildCardExtendsMapper.Source source = new SourceWildCardExtendsMapper.Source( wrapperB, wrapperC ); + + // action + SourceWildCardExtendsMapper.Target target = SourceWildCardExtendsMapper.INSTANCE.map( source ); + + // verify target + assertThat( target ).isNotNull(); + assertThat( target.getPropB() ).isEqualTo( typeB ); + assertThat( target.getPropC() ).isEqualTo( typeC ); + } + + @Test + @WithClasses( IntersectionMapper.class ) + // Eclipse does not handle intersection types correctly (TODO: worthwhile to investigate?) + @DisabledOnCompiler( Compiler.ECLIPSE ) + public void testIntersectionRelation() { + + // prepare source + IntersectionMapper.TypeC typeC = new IntersectionMapper.TypeC(); + IntersectionMapper.Wrapper wrapper = new IntersectionMapper.Wrapper( typeC ); + IntersectionMapper.Source source = new IntersectionMapper.Source( wrapper ); + + // action + IntersectionMapper.Target target = IntersectionMapper.INSTANCE.map( source ); + + // verify target + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isEqualTo( typeC ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/SourceWildCardExtendsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/SourceWildCardExtendsMapper.java new file mode 100644 index 0000000000..feb49267d8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/SourceWildCardExtendsMapper.java @@ -0,0 +1,66 @@ +/* + * 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.selection.typegenerics; + +import java.math.BigInteger; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SourceWildCardExtendsMapper { + + SourceWildCardExtendsMapper INSTANCE = Mappers.getMapper( SourceWildCardExtendsMapper.class ); + + Target map( Source source); + + default String unwrap(Wrapper t) { + return t.getWrapped().toString(); + } + + class Source { + + private final Wrapper prop; + + public Source(Wrapper prop) { + + this.prop = prop; + } + + public Wrapper getProp() { + return prop; + } + } + + class Wrapper { + + private final T wrapped; + + public Wrapper(T wrapped) { + this.wrapped = wrapped; + } + + public T getWrapped() { + return wrapped; + } + + } + + class Target { + + private String prop; + + public String getProp() { + return prop; + } + + public Target setProp(String prop) { + this.prop = prop; + return this; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/WildCardTest.java new file mode 100644 index 0000000000..362e00331c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/WildCardTest.java @@ -0,0 +1,41 @@ +/* + * 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.selection.typegenerics; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigInteger; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Sjaak Derksen + * + */ +@RunWith(AnnotationProcessorTestRunner.class) +public class WildCardTest { + + @Test + @WithClasses( SourceWildCardExtendsMapper.class ) + public void testWildCard() { + + // prepare source + SourceWildCardExtendsMapper.Wrapper wrapper = + new SourceWildCardExtendsMapper.Wrapper<>( new BigInteger( "5" ) ); + SourceWildCardExtendsMapper.Source source = new SourceWildCardExtendsMapper.Source( wrapper ); + + // action + SourceWildCardExtendsMapper.Target target = SourceWildCardExtendsMapper.INSTANCE.map( source ); + + // verify target + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isEqualTo( "5" ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/ReturnTypeWildCardExtendsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/ReturnTypeWildCardExtendsMapper.java new file mode 100644 index 0000000000..3e4366e06f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/ReturnTypeWildCardExtendsMapper.java @@ -0,0 +1,70 @@ +/* + * 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.selection.wildcards; + +import java.math.BigInteger; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ReturnTypeWildCardExtendsMapper { + + ReturnTypeWildCardExtendsMapper INSTANCE = Mappers.getMapper( ReturnTypeWildCardExtendsMapper.class ); + + Target map(Source source); + + default Wrapper wrap(String in) { + return new Wrapper<>( new BigInteger( in ) ); + } + + class Source { + + private String prop; + + public Source(String prop) { + this.prop = prop; + } + + public String getProp() { + return prop; + } + + public Source setProp(String prop) { + this.prop = prop; + return this; + } + } + + class Target { + + private Wrapper prop; + + public Target setProp(Wrapper prop) { + this.prop = prop; + return this; + } + + public Wrapper getProp() { + return prop; + } + } + + class Wrapper { + + private final T wrapped; + + public Wrapper(T wrapped) { + this.wrapped = wrapped; + } + + public T getWrapped() { + return wrapped; + } + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/SourceWildCardExtendsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/SourceWildCardExtendsMapper.java new file mode 100644 index 0000000000..c82ae3cccd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/SourceWildCardExtendsMapper.java @@ -0,0 +1,66 @@ +/* + * 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.selection.wildcards; + +import java.math.BigInteger; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SourceWildCardExtendsMapper { + + SourceWildCardExtendsMapper INSTANCE = Mappers.getMapper( SourceWildCardExtendsMapper.class ); + + Target map( Source source); + + default String unwrap(Wrapper t) { + return t.getWrapped().toString(); + } + + class Source { + + private final Wrapper prop; + + public Source(Wrapper prop) { + + this.prop = prop; + } + + public Wrapper getProp() { + return prop; + } + } + + class Wrapper { + + private final T wrapped; + + public Wrapper(T wrapped) { + this.wrapped = wrapped; + } + + public T getWrapped() { + return wrapped; + } + + } + + class Target { + + private String prop; + + public String getProp() { + return prop; + } + + public Target setProp(String prop) { + this.prop = prop; + return this; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/WildCardTest.java new file mode 100644 index 0000000000..405a22320e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/WildCardTest.java @@ -0,0 +1,58 @@ +/* + * 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.selection.wildcards; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigInteger; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Sjaak Derksen + * + */ +@RunWith(AnnotationProcessorTestRunner.class) +public class WildCardTest { + + @Test + @WithClasses( SourceWildCardExtendsMapper.class ) + public void testWildCardAsSourceType() { + + // prepare source + SourceWildCardExtendsMapper.Wrapper wrapper = + new SourceWildCardExtendsMapper.Wrapper<>( new BigInteger( "5" ) ); + SourceWildCardExtendsMapper.Source source = new SourceWildCardExtendsMapper.Source( wrapper ); + + // action + SourceWildCardExtendsMapper.Target target = SourceWildCardExtendsMapper.INSTANCE.map( source ); + + // verify target + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isEqualTo( "5" ); + } + + @Test + @WithClasses( ReturnTypeWildCardExtendsMapper.class ) + public void testWildCardAsReturnType() { + + // prepare source + ReturnTypeWildCardExtendsMapper.Source source = new ReturnTypeWildCardExtendsMapper.Source( "5" ); + + // action + ReturnTypeWildCardExtendsMapper.Target target = ReturnTypeWildCardExtendsMapper.INSTANCE.map( source ); + + // verify target + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp().getWrapped() ).isEqualTo( BigInteger.valueOf( 5 ) ); + + } + +} From 85d3b310f781f186ed8f18b56f9514a8a3c8e65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Silv=C3=A8re=20Marie?= Date: Mon, 29 Mar 2021 17:42:22 +0200 Subject: [PATCH 150/597] Fix method naming --- .../java/org/mapstruct/ap/test/fields/FieldsMappingTest.java | 2 +- .../java/org/mapstruct/ap/test/fields/SourceTargetMapper.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/processor/src/test/java/org/mapstruct/ap/test/fields/FieldsMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/fields/FieldsMappingTest.java index 8c87e7e14d..d215a00ab5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/fields/FieldsMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/fields/FieldsMappingTest.java @@ -29,7 +29,7 @@ public void shouldMapSourceToTarget() { source.normalList = Lists.newArrayList( 10, 11, 12 ); source.fieldOnlyWithGetter = 20; - Target target = SourceTargetMapper.INSTANCE.toSource( source ); + Target target = SourceTargetMapper.INSTANCE.toTarget( source ); assertThat( target ).isNotNull(); assertThat( target.finalInt ).isEqualTo( "10" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/fields/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/fields/SourceTargetMapper.java index 99b7b50aa4..0716c380be 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/fields/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/fields/SourceTargetMapper.java @@ -19,7 +19,7 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mapping(target = "fieldWithMethods", source = "fieldOnlyWithGetter") - Target toSource(Source source); + Target toTarget(Source source); @InheritInverseConfiguration Source toSource(Target target); From c9199b7068726c279f8daf738f3a2d4076c940b7 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 5 Apr 2021 12:44:55 +0200 Subject: [PATCH 151/597] #2393 Use includeModel when generating GeneratedType With this we make sure that the implementation type will have a correct import in case of a clash with another mapper named the same --- .../ap/internal/model/Decorator.java | 25 ++++++----- .../ap/internal/model/GeneratedType.java | 26 ++++-------- .../mapstruct/ap/internal/model/Mapper.java | 17 ++++---- .../processor/MapperServiceProcessor.java | 23 ++++++++-- .../ap/internal/model/GeneratedType.ftl | 2 +- .../mapstruct/ap/test/bugs/_2393/Address.java | 28 +++++++++++++ .../ap/test/bugs/_2393/AddressDto.java | 42 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2393/Country.java | 22 ++++++++++ .../ap/test/bugs/_2393/CountryDto.java | 42 +++++++++++++++++++ .../ap/test/bugs/_2393/Issue2393Test.java | 40 ++++++++++++++++++ 10 files changed, 224 insertions(+), 43 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Address.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/AddressDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Country.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/CountryDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Issue2393Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java index e61a25a475..a54045dca0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.List; import java.util.SortedSet; -import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import org.mapstruct.ap.internal.model.common.Accessibility; @@ -74,7 +73,8 @@ public Decorator build() { hasDelegateConstructor ); - String elementPackage = elementUtils.getPackageOf( mapperElement ).getQualifiedName().toString(); + Type mapperType = typeFactory.getType( mapperElement ); + String elementPackage = mapperType.getPackageName(); String packageName = implPackage.replace( Mapper.PACKAGE_NAME_PLACEHOLDER, elementPackage ); return new Decorator( @@ -82,10 +82,8 @@ public Decorator build() { packageName, implementationName, decoratorType, - elementPackage, - mapperElement.getKind() == ElementKind.INTERFACE ? mapperElement.getSimpleName().toString() : null, + mapperType, methods, - Arrays.asList( new Field( typeFactory.getType( mapperElement ), "delegate", true ) ), options, versionInformation, Accessibility.fromModifiers( mapperElement.getModifiers() ), @@ -96,22 +94,22 @@ public Decorator build() { } private final Type decoratorType; + private final Type mapperType; @SuppressWarnings( "checkstyle:parameternumber" ) private Decorator(TypeFactory typeFactory, String packageName, String name, Type decoratorType, - String interfacePackage, String interfaceName, List methods, - List fields, Options options, VersionInformation versionInformation, + Type mapperType, + List methods, + Options options, VersionInformation versionInformation, Accessibility accessibility, SortedSet extraImports, DecoratorConstructor decoratorConstructor) { super( typeFactory, packageName, name, - decoratorType.getName(), - interfacePackage, - interfaceName, + decoratorType, methods, - fields, + Arrays.asList( new Field( mapperType, "delegate", true ) ), options, versionInformation, accessibility, @@ -120,6 +118,7 @@ private Decorator(TypeFactory typeFactory, String packageName, String name, Type ); this.decoratorType = decoratorType; + this.mapperType = mapperType; } @Override @@ -148,4 +147,8 @@ public SortedSet getImportTypes() { protected String getTemplateName() { return getTemplateNameForClass( GeneratedType.class ); } + + public Type getMapperType() { + return mapperType; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java index a6f573d4ff..620559ea24 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java @@ -80,9 +80,7 @@ public T methods(List methods) { private final String packageName; private final String name; - private final String superClassName; - private final String interfacePackage; - private final String interfaceName; + private final Type mapperDefinitionType; private final List annotations; private final List methods; @@ -102,15 +100,13 @@ public T methods(List methods) { private final boolean generatedTypeAvailable; // CHECKSTYLE:OFF - protected GeneratedType(TypeFactory typeFactory, String packageName, String name, String superClassName, - String interfacePackage, String interfaceName, List methods, + protected GeneratedType(TypeFactory typeFactory, String packageName, String name, + Type mapperDefinitionType, List methods, List fields, Options options, VersionInformation versionInformation, Accessibility accessibility, SortedSet extraImportedTypes, Constructor constructor) { this.packageName = packageName; this.name = name; - this.superClassName = superClassName; - this.interfacePackage = interfacePackage; - this.interfaceName = interfaceName; + this.mapperDefinitionType = mapperDefinitionType; this.extraImportedTypes = extraImportedTypes; this.annotations = new ArrayList<>(); @@ -153,16 +149,8 @@ public String getName() { return name; } - public String getSuperClassName() { - return superClassName; - } - - public String getInterfacePackage() { - return interfacePackage; - } - - public String getInterfaceName() { - return interfaceName; + public Type getMapperDefinitionType() { + return mapperDefinitionType; } public List getAnnotations() { @@ -214,6 +202,8 @@ public SortedSet getImportTypes() { SortedSet importedTypes = new TreeSet<>(); addIfImportRequired( importedTypes, generatedType ); + addIfImportRequired( importedTypes, mapperDefinitionType ); + for ( MappingMethod mappingMethod : methods ) { for ( Type type : mappingMethod.getImportTypes() ) { addIfImportRequired( importedTypes, type ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java index 0ebd1f0ffd..db63a8431d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java @@ -10,7 +10,6 @@ import java.util.SortedSet; import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import org.mapstruct.ap.internal.model.common.Accessibility; @@ -90,13 +89,14 @@ public Mapper build() { if ( !fragments.isEmpty() ) { constructor = new NoArgumentConstructor( implementationName, fragments ); } + + Type definitionType = typeFactory.getType( element ); + return new Mapper( typeFactory, packageName, implementationName, - element.getKind() != ElementKind.INTERFACE ? element.getSimpleName().toString() : null, - elementPackage, - element.getKind() == ElementKind.INTERFACE ? element.getSimpleName().toString() : null, + definitionType, customPackage, customName, methods, @@ -117,8 +117,9 @@ public Mapper build() { private Decorator decorator; @SuppressWarnings( "checkstyle:parameternumber" ) - private Mapper(TypeFactory typeFactory, String packageName, String name, String superClassName, - String interfacePackage, String interfaceName, boolean customPackage, boolean customImplName, + private Mapper(TypeFactory typeFactory, String packageName, String name, + Type mapperDefinitionType, + boolean customPackage, boolean customImplName, List methods, Options options, VersionInformation versionInformation, Accessibility accessibility, List fields, Constructor constructor, Decorator decorator, SortedSet extraImportedTypes ) { @@ -127,9 +128,7 @@ private Mapper(TypeFactory typeFactory, String packageName, String name, String typeFactory, packageName, name, - superClassName, - interfacePackage, - interfaceName, + mapperDefinitionType, methods, fields, options, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java index c9e37596f7..ee0bf34634 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperServiceProcessor.java @@ -12,9 +12,11 @@ import javax.tools.StandardLocation; import org.mapstruct.ap.internal.gem.MappingConstantsGem; +import org.mapstruct.ap.internal.model.Decorator; import org.mapstruct.ap.internal.model.GeneratedType; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.ServicesEntry; +import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.MapperOptions; import org.mapstruct.ap.internal.writer.ModelWriter; @@ -55,15 +57,28 @@ public int getPriority() { private void writeToSourceFile(Filer filer, Mapper model) { ModelWriter modelWriter = new ModelWriter(); - ServicesEntry servicesEntry = getServicesEntry( model.getDecorator() == null ? model : model.getDecorator() ); + ServicesEntry servicesEntry = getServicesEntry( model ); createSourceFile( servicesEntry, modelWriter, filer ); } - private ServicesEntry getServicesEntry(GeneratedType model) { - String mapperName = model.getInterfaceName() != null ? model.getInterfaceName() : model.getSuperClassName(); + private ServicesEntry getServicesEntry(Mapper mapper) { + if ( mapper.getDecorator() != null ) { + return getServicesEntry( mapper.getDecorator() ); + } + + return getServicesEntry( mapper.getMapperDefinitionType(), mapper ); + } + + private ServicesEntry getServicesEntry(Decorator decorator) { + return getServicesEntry( decorator.getMapperType(), decorator ); + } + + private ServicesEntry getServicesEntry(Type mapperType, GeneratedType model) { + String mapperName = mapperType.getName(); + String mapperPackageName = mapperType.getPackageName(); - return new ServicesEntry(model.getInterfacePackage(), mapperName, + return new ServicesEntry(mapperPackageName, mapperName, model.getPackageName(), model.getName()); } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/GeneratedType.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/GeneratedType.ftl index 3bdae0582a..4981882403 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/GeneratedType.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/GeneratedType.ftl @@ -24,7 +24,7 @@ import ${importedType}; <#list annotations as annotation> <#nt><@includeModel object=annotation/> -<#lt>${accessibility.keyword} class ${name}<#if superClassName??> extends ${superClassName}<#if interfaceName??> implements ${interfaceName} { +<#lt>${accessibility.keyword} class ${name} <#if mapperDefinitionType.interface>implements<#else>extends <@includeModel object=mapperDefinitionType/> { <#list fields as field><#if field.used><#nt> <@includeModel object=field/> diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Address.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Address.java new file mode 100644 index 0000000000..8651165d82 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Address.java @@ -0,0 +1,28 @@ +/* + * 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._2393; + +/** + * @author Filip Hrisafov + */ +public class Address { + + private final String city; + private final Country country; + + public Address(String city, Country country) { + this.city = city; + this.country = country; + } + + public String getCity() { + return city; + } + + public Country getCountry() { + return country; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/AddressDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/AddressDto.java new file mode 100644 index 0000000000..41f771f319 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/AddressDto.java @@ -0,0 +1,42 @@ +/* + * 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._2393; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +public class AddressDto { + + private String city; + private CountryDto country; + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public CountryDto getCountry() { + return country; + } + + public void setCountry(CountryDto country) { + this.country = country; + } + + @Mapper(uses = CountryDto.Converter.class) + public interface Converter { + + Converter INSTANCE = Mappers.getMapper( Converter.class ); + + AddressDto convert(Address address); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Country.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Country.java new file mode 100644 index 0000000000..efeaad6fc0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Country.java @@ -0,0 +1,22 @@ +/* + * 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._2393; + +/** + * @author Filip Hrisafov + */ +public class Country { + + private final String name; + + public Country(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/CountryDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/CountryDto.java new file mode 100644 index 0000000000..7e3751efb3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/CountryDto.java @@ -0,0 +1,42 @@ +/* + * 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._2393; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * @author Filip Hrisafov + */ +public class CountryDto { + + private String name; + private String code; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + @Mapper + public interface Converter { + + @Mapping(target = "code", constant = "UNKNOWN") + CountryDto convert(Country from); + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Issue2393Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Issue2393Test.java new file mode 100644 index 0000000000..0bb88e5e59 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Issue2393Test.java @@ -0,0 +1,40 @@ +/* + * 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._2393; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2393") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Address.class, + AddressDto.class, + Country.class, + CountryDto.class, +}) +public class Issue2393Test { + + @Test + public void shouldUseCorrectImport() { + AddressDto dto = AddressDto.Converter.INSTANCE.convert( new Address( + "Zurich", + new Country( "Switzerland" ) + ) ); + + assertThat( dto.getCity() ).isEqualTo( "Zurich" ); + assertThat( dto.getCountry().getName() ).isEqualTo( "Switzerland" ); + assertThat( dto.getCountry().getCode() ).isEqualTo( "UNKNOWN" ); + } +} From 5f1b3d7862446269924bd5386576f6d55fd600a0 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 5 Apr 2021 10:04:16 +0200 Subject: [PATCH 152/597] #2402 Always add source parameter name when constructing the source references for target this --- .../model/beanmapping/SourceReference.java | 7 ++- .../ap/test/bugs/_2042/Issue2402Mapper.java | 59 +++++++++++++++++++ .../ap/test/bugs/_2042/Issue2402Test.java | 36 +++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index d43a242278..c87c6fb46d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -421,8 +421,13 @@ public List push(TypeFactory typeFactory, FormattingMessager me if ( deepestProperty != null ) { Type type = deepestProperty.getType(); Map newDeepestReadAccessors = type.getPropertyReadAccessors(); + String parameterName = getParameter().getName(); + String deepestPropertyFullName = deepestProperty.getFullName(); for ( Map.Entry newDeepestReadAccessorEntry : newDeepestReadAccessors.entrySet() ) { - String newFullName = deepestProperty.getFullName() + "." + newDeepestReadAccessorEntry.getKey(); + // Always include the parameter name in the new full name. + // Otherwise multi source parameters might be reported incorrectly + String newFullName = + parameterName + "." + deepestPropertyFullName + "." + newDeepestReadAccessorEntry.getKey(); SourceReference sourceReference = new BuilderFromMapping() .sourceName( newFullName ) .method( method ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Mapper.java new file mode 100644 index 0000000000..3fe52aaafd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Mapper.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.bugs._2042; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2402Mapper { + + Issue2402Mapper INSTANCE = Mappers.getMapper( Issue2402Mapper.class ); + + @Mapping(target = ".", source = "source.info") + Target map(Source source, String other); + + class Target { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + class Source { + + private final Info info; + + public Source(Info info) { + this.info = info; + } + + public Info getInfo() { + return info; + } + } + + class Info { + private final String name; + + public Info(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Test.java new file mode 100644 index 0000000000..26e585c23d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Test.java @@ -0,0 +1,36 @@ +/* + * 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._2042; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2402") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2402Mapper.class +}) +public class Issue2402Test { + + @Test + public void shouldCompile() { + Issue2402Mapper.Target target = Issue2402Mapper.INSTANCE. + map( + new Issue2402Mapper.Source( new Issue2402Mapper.Info( "test" ) ), + "other test" + ); + + assertThat( target.getName() ).isEqualTo( "test" ); + } +} From 2be536bb65d220495b8d7f7f224451f1d59a1bb9 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 5 Apr 2021 13:27:08 +0200 Subject: [PATCH 153/597] #2303 Generated code should use iteration order preserving LinkedHash(Map|Set) instead of Hash(Map|Set) --- ...apter-10-advanced-mapping-options.asciidoc | 2 +- .../chapter-6-mapping-collections.asciidoc | 8 ++--- .../chapter-7-mapping-streams.asciidoc | 2 +- .../ap/internal/model/common/TypeFactory.java | 6 ++-- .../test/bugs/_1453/Issue1453MapperImpl.java | 8 ++--- .../ap/test/bugs/_1707/ConverterImpl.java | 6 ++-- .../DomainDtoWithNcvsAlwaysMapperImpl.java | 16 ++++----- .../DomainDtoWithNvmsDefaultMapperImpl.java | 34 +++++++++---------- .../_913/DomainDtoWithNvmsNullMapperImpl.java | 16 ++++----- .../DomainDtoWithPresenceCheckMapperImpl.java | 16 ++++----- .../SourceTargetMapperImpl.java | 8 ++--- .../updatemethods/CompanyMapper1Impl.java | 4 +-- .../selection/DepartmentMapperImpl.java | 4 +-- 13 files changed, 66 insertions(+), 64 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 4ccad69f2c..158aa632f9 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -180,7 +180,7 @@ By default the target property will be set to null. However: 1. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MapperConfig`, the mapping result can be altered to return *default* values. -For `List` MapStruct generates an `ArrayList`, for `Map` a `HashMap`, for arrays an empty array, for `String` `""` and for primitive / boxed types a representation of `false` or `0`. +For `List` MapStruct generates an `ArrayList`, for `Map` a `LinkedHashMap`, for arrays an empty array, for `String` `""` and for primitive / boxed types a representation of `false` or `0`. For all other objects an new instance is created. Please note that a default constructor is required. If not available, use the `@Mapping#defaultValue`. 2. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MapperConfig`, the mapping result will be equal to the original value of the `@MappingTarget` annotated target. diff --git a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc index 785b460b12..17025da4de 100644 --- a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc +++ b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc @@ -36,7 +36,7 @@ public Set integerSetToStringSet(Set integers) { return null; } - Set set = new HashSet(); + Set set = new LinkedHashSet(); for ( Integer integer : integers ) { set.add( String.valueOf( integer ) ); @@ -125,7 +125,7 @@ public Map stringStringMapToLongDateMap(Map source) return null; } - Map map = new HashMap(); + Map map = new LinkedHashMap(); for ( Map.Entry entry : source.entrySet() ) { @@ -210,13 +210,13 @@ When an iterable or map mapping method declares an interface type as return type |`List`|`ArrayList` -|`Set`|`HashSet` +|`Set`|`LinkedHashSet` |`SortedSet`|`TreeSet` |`NavigableSet`|`TreeSet` -|`Map`|`HashMap` +|`Map`|`LinkedHashMap` |`SortedMap`|`TreeMap` diff --git a/documentation/src/main/asciidoc/chapter-7-mapping-streams.asciidoc b/documentation/src/main/asciidoc/chapter-7-mapping-streams.asciidoc index ccc3e56854..ec41719eb8 100644 --- a/documentation/src/main/asciidoc/chapter-7-mapping-streams.asciidoc +++ b/documentation/src/main/asciidoc/chapter-7-mapping-streams.asciidoc @@ -42,7 +42,7 @@ public Set integerStreamToStringSet(Stream integers) { } return integers.map( integer -> String.valueOf( integer ) ) - .collect( Collectors.toCollection( HashSet::new ) ); + .collect( Collectors.toCollection( LinkedHashSet::new ) ); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index f8c67f740e..127ec74270 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -10,6 +10,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NavigableMap; @@ -116,11 +118,11 @@ public TypeFactory(ElementUtils elementUtils, TypeUtils typeUtils, FormattingMes implementationTypes.put( Collection.class.getName(), withInitialCapacity( getType( ArrayList.class ) ) ); implementationTypes.put( List.class.getName(), withInitialCapacity( getType( ArrayList.class ) ) ); - implementationTypes.put( Set.class.getName(), withLoadFactorAdjustment( getType( HashSet.class ) ) ); + implementationTypes.put( Set.class.getName(), withLoadFactorAdjustment( getType( LinkedHashSet.class ) ) ); implementationTypes.put( SortedSet.class.getName(), withDefaultConstructor( getType( TreeSet.class ) ) ); implementationTypes.put( NavigableSet.class.getName(), withDefaultConstructor( getType( TreeSet.class ) ) ); - implementationTypes.put( Map.class.getName(), withLoadFactorAdjustment( getType( HashMap.class ) ) ); + implementationTypes.put( Map.class.getName(), withLoadFactorAdjustment( getType( LinkedHashMap.class ) ) ); implementationTypes.put( SortedMap.class.getName(), withDefaultConstructor( getType( TreeMap.class ) ) ); implementationTypes.put( NavigableMap.class.getName(), withDefaultConstructor( getType( TreeMap.class ) ) ); implementationTypes.put( diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java index 00e01f6f17..201b007603 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java @@ -6,7 +6,7 @@ package org.mapstruct.ap.test.bugs._1453; import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.Generated; @@ -68,7 +68,7 @@ public Map mapExtend(Map map = new HashMap( Math.max( (int) ( auctions.size() / .75f ) + 1, 16 ) ); + Map map = new LinkedHashMap( Math.max( (int) ( auctions.size() / .75f ) + 1, 16 ) ); for ( java.util.Map.Entry entry : auctions.entrySet() ) { AuctionDto key = map( entry.getKey() ); @@ -85,7 +85,7 @@ public Map mapExtend(Map map = new HashMap( Math.max( (int) ( auctions.size() / .75f ) + 1, 16 ) ); + Map map = new LinkedHashMap( Math.max( (int) ( auctions.size() / .75f ) + 1, 16 ) ); for ( java.util.Map.Entry entry : auctions.entrySet() ) { AuctionDto key = map( entry.getKey() ); @@ -126,7 +126,7 @@ protected Map paymentPaymentMapToPaymentDtoPaymentDtoMap return null; } - Map map1 = new HashMap( Math.max( (int) ( map.size() / .75f ) + 1, 16 ) ); + Map map1 = new LinkedHashMap( Math.max( (int) ( map.size() / .75f ) + 1, 16 ) ); for ( java.util.Map.Entry entry : map.entrySet() ) { PaymentDto key = paymentToPaymentDto( entry.getKey() ); diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1707/ConverterImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1707/ConverterImpl.java index 898b134188..abe4a62a3d 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1707/ConverterImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1707/ConverterImpl.java @@ -5,7 +5,7 @@ */ package org.mapstruct.ap.test.bugs._1707; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -24,10 +24,10 @@ public Set convert(Stream source) { return null; } - Set set = new HashSet(); + Set set = new LinkedHashSet(); set.addAll( source.map( source1 -> convert( source1 ) ) - .collect( Collectors.toCollection( HashSet::new ) ) + .collect( Collectors.toCollection( LinkedHashSet::new ) ) ); addCustomValue( set ); diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java index e7e1be5b1d..b3a8c89c60 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java @@ -6,7 +6,7 @@ package org.mapstruct.ap.test.bugs._913; import java.util.ArrayList; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.annotation.Generated; @@ -30,14 +30,14 @@ public Domain create(DtoWithPresenceCheck source) { if ( source.hasStrings() ) { List list = source.getStrings(); - domain.setStrings( new HashSet( list ) ); + domain.setStrings( new LinkedHashSet( list ) ); } if ( source.hasStrings() ) { domain.setLongs( stringListToLongSet( source.getStrings() ) ); } if ( source.hasStringsInitialized() ) { List list1 = source.getStringsInitialized(); - domain.setStringsInitialized( new HashSet( list1 ) ); + domain.setStringsInitialized( new LinkedHashSet( list1 ) ); } if ( source.hasStringsInitialized() ) { domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); @@ -68,7 +68,7 @@ public void update(DtoWithPresenceCheck source, Domain target) { else { if ( source.hasStrings() ) { List list = source.getStrings(); - target.setStrings( new HashSet( list ) ); + target.setStrings( new LinkedHashSet( list ) ); } } if ( target.getLongs() != null ) { @@ -91,7 +91,7 @@ public void update(DtoWithPresenceCheck source, Domain target) { else { if ( source.hasStringsInitialized() ) { List list1 = source.getStringsInitialized(); - target.setStringsInitialized( new HashSet( list1 ) ); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); } } if ( target.getLongsInitialized() != null ) { @@ -140,7 +140,7 @@ public Domain updateWithReturn(DtoWithPresenceCheck source, Domain target) { else { if ( source.hasStrings() ) { List list = source.getStrings(); - target.setStrings( new HashSet( list ) ); + target.setStrings( new LinkedHashSet( list ) ); } } if ( target.getLongs() != null ) { @@ -163,7 +163,7 @@ public Domain updateWithReturn(DtoWithPresenceCheck source, Domain target) { else { if ( source.hasStringsInitialized() ) { List list1 = source.getStringsInitialized(); - target.setStringsInitialized( new HashSet( list1 ) ); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); } } if ( target.getLongsInitialized() != null ) { @@ -204,7 +204,7 @@ protected Set stringListToLongSet(List list) { return null; } - Set set = new HashSet( Math.max( (int) ( list.size() / .75f ) + 1, 16 ) ); + Set set = new LinkedHashSet( Math.max( (int) ( list.size() / .75f ) + 1, 16 ) ); for ( String string : list ) { set.add( Long.parseLong( string ) ); } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java index f0f2ea5f01..f1bf0fa78f 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java @@ -6,7 +6,7 @@ package org.mapstruct.ap.test.bugs._913; import java.util.ArrayList; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.annotation.Generated; @@ -28,12 +28,12 @@ public Domain create(Dto source) { if ( source != null ) { List list = source.getStrings(); if ( list != null ) { - domain.setStrings( new HashSet( list ) ); + domain.setStrings( new LinkedHashSet( list ) ); } domain.setLongs( stringListToLongSet( source.getStrings() ) ); List list1 = source.getStringsInitialized(); if ( list1 != null ) { - domain.setStringsInitialized( new HashSet( list1 ) ); + domain.setStringsInitialized( new LinkedHashSet( list1 ) ); } domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); List list2 = source.getStringsWithDefault(); @@ -59,13 +59,13 @@ public void update(Dto source, Domain target) { target.getStrings().addAll( list ); } else { - target.setStrings( new HashSet() ); + target.setStrings( new LinkedHashSet() ); } } else { List list = source.getStrings(); if ( list != null ) { - target.setStrings( new HashSet( list ) ); + target.setStrings( new LinkedHashSet( list ) ); } } if ( target.getLongs() != null ) { @@ -75,7 +75,7 @@ public void update(Dto source, Domain target) { target.getLongs().addAll( set ); } else { - target.setLongs( new HashSet() ); + target.setLongs( new LinkedHashSet() ); } } else { @@ -91,13 +91,13 @@ public void update(Dto source, Domain target) { target.getStringsInitialized().addAll( list1 ); } else { - target.setStringsInitialized( new HashSet() ); + target.setStringsInitialized( new LinkedHashSet() ); } } else { List list1 = source.getStringsInitialized(); if ( list1 != null ) { - target.setStringsInitialized( new HashSet( list1 ) ); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); } } if ( target.getLongsInitialized() != null ) { @@ -107,7 +107,7 @@ public void update(Dto source, Domain target) { target.getLongsInitialized().addAll( set1 ); } else { - target.setLongsInitialized( new HashSet() ); + target.setLongsInitialized( new LinkedHashSet() ); } } else { @@ -149,13 +149,13 @@ public Domain updateWithReturn(Dto source, Domain target) { target.getStrings().addAll( list ); } else { - target.setStrings( new HashSet() ); + target.setStrings( new LinkedHashSet() ); } } else { List list = source.getStrings(); if ( list != null ) { - target.setStrings( new HashSet( list ) ); + target.setStrings( new LinkedHashSet( list ) ); } } if ( target.getLongs() != null ) { @@ -165,7 +165,7 @@ public Domain updateWithReturn(Dto source, Domain target) { target.getLongs().addAll( set ); } else { - target.setLongs( new HashSet() ); + target.setLongs( new LinkedHashSet() ); } } else { @@ -181,13 +181,13 @@ public Domain updateWithReturn(Dto source, Domain target) { target.getStringsInitialized().addAll( list1 ); } else { - target.setStringsInitialized( new HashSet() ); + target.setStringsInitialized( new LinkedHashSet() ); } } else { List list1 = source.getStringsInitialized(); if ( list1 != null ) { - target.setStringsInitialized( new HashSet( list1 ) ); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); } } if ( target.getLongsInitialized() != null ) { @@ -197,7 +197,7 @@ public Domain updateWithReturn(Dto source, Domain target) { target.getLongsInitialized().addAll( set1 ); } else { - target.setLongsInitialized( new HashSet() ); + target.setLongsInitialized( new LinkedHashSet() ); } } else { @@ -232,10 +232,10 @@ public Domain updateWithReturn(Dto source, Domain target) { protected Set stringListToLongSet(List list) { if ( list == null ) { - return new HashSet(); + return new LinkedHashSet(); } - Set set = new HashSet( Math.max( (int) ( list.size() / .75f ) + 1, 16 ) ); + Set set = new LinkedHashSet( Math.max( (int) ( list.size() / .75f ) + 1, 16 ) ); for ( String string : list ) { set.add( Long.parseLong( string ) ); } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java index a07601a4df..59a770d049 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java @@ -6,7 +6,7 @@ package org.mapstruct.ap.test.bugs._913; import java.util.ArrayList; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.annotation.Generated; @@ -30,12 +30,12 @@ public Domain create(Dto source) { List list = source.getStrings(); if ( list != null ) { - domain.setStrings( new HashSet( list ) ); + domain.setStrings( new LinkedHashSet( list ) ); } domain.setLongs( stringListToLongSet( source.getStrings() ) ); List list1 = source.getStringsInitialized(); if ( list1 != null ) { - domain.setStringsInitialized( new HashSet( list1 ) ); + domain.setStringsInitialized( new LinkedHashSet( list1 ) ); } domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); List list2 = source.getStringsWithDefault(); @@ -68,7 +68,7 @@ public void update(Dto source, Domain target) { else { List list = source.getStrings(); if ( list != null ) { - target.setStrings( new HashSet( list ) ); + target.setStrings( new LinkedHashSet( list ) ); } } if ( target.getLongs() != null ) { @@ -100,7 +100,7 @@ public void update(Dto source, Domain target) { else { List list1 = source.getStringsInitialized(); if ( list1 != null ) { - target.setStringsInitialized( new HashSet( list1 ) ); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); } } if ( target.getLongsInitialized() != null ) { @@ -159,7 +159,7 @@ public Domain updateWithReturn(Dto source, Domain target) { else { List list = source.getStrings(); if ( list != null ) { - target.setStrings( new HashSet( list ) ); + target.setStrings( new LinkedHashSet( list ) ); } } if ( target.getLongs() != null ) { @@ -191,7 +191,7 @@ public Domain updateWithReturn(Dto source, Domain target) { else { List list1 = source.getStringsInitialized(); if ( list1 != null ) { - target.setStringsInitialized( new HashSet( list1 ) ); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); } } if ( target.getLongsInitialized() != null ) { @@ -238,7 +238,7 @@ protected Set stringListToLongSet(List list) { return null; } - Set set = new HashSet( Math.max( (int) ( list.size() / .75f ) + 1, 16 ) ); + Set set = new LinkedHashSet( Math.max( (int) ( list.size() / .75f ) + 1, 16 ) ); for ( String string : list ) { set.add( Long.parseLong( string ) ); } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java index 6ed369c4d9..ba18dfda11 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java @@ -6,7 +6,7 @@ package org.mapstruct.ap.test.bugs._913; import java.util.ArrayList; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.annotation.Generated; @@ -30,12 +30,12 @@ public Domain create(DtoWithPresenceCheck source) { if ( source.hasStrings() ) { List list = source.getStrings(); - domain.setStrings( new HashSet( list ) ); + domain.setStrings( new LinkedHashSet( list ) ); } domain.setLongs( stringListToLongSet( source.getStrings() ) ); if ( source.hasStringsInitialized() ) { List list1 = source.getStringsInitialized(); - domain.setStringsInitialized( new HashSet( list1 ) ); + domain.setStringsInitialized( new LinkedHashSet( list1 ) ); } domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); if ( source.hasStringsWithDefault() ) { @@ -64,7 +64,7 @@ public void update(DtoWithPresenceCheck source, Domain target) { else { if ( source.hasStrings() ) { List list = source.getStrings(); - target.setStrings( new HashSet( list ) ); + target.setStrings( new LinkedHashSet( list ) ); } } if ( target.getLongs() != null ) { @@ -87,7 +87,7 @@ public void update(DtoWithPresenceCheck source, Domain target) { else { if ( source.hasStringsInitialized() ) { List list1 = source.getStringsInitialized(); - target.setStringsInitialized( new HashSet( list1 ) ); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); } } if ( target.getLongsInitialized() != null ) { @@ -136,7 +136,7 @@ public Domain updateWithReturn(DtoWithPresenceCheck source, Domain target) { else { if ( source.hasStrings() ) { List list = source.getStrings(); - target.setStrings( new HashSet( list ) ); + target.setStrings( new LinkedHashSet( list ) ); } } if ( target.getLongs() != null ) { @@ -159,7 +159,7 @@ public Domain updateWithReturn(DtoWithPresenceCheck source, Domain target) { else { if ( source.hasStringsInitialized() ) { List list1 = source.getStringsInitialized(); - target.setStringsInitialized( new HashSet( list1 ) ); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); } } if ( target.getLongsInitialized() != null ) { @@ -200,7 +200,7 @@ protected Set stringListToLongSet(List list) { return null; } - Set set = new HashSet( Math.max( (int) ( list.size() / .75f ) + 1, 16 ) ); + Set set = new LinkedHashSet( Math.max( (int) ( list.size() / .75f ) + 1, 16 ) ); for ( String string : list ) { set.add( Long.parseLong( string ) ); } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java index 6310026170..853ad59560 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java @@ -7,8 +7,8 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NavigableMap; @@ -82,7 +82,7 @@ public Set sourceFoosToTargetFoos(Set foos) { return null; } - Set set = new HashSet( Math.max( (int) ( foos.size() / .75f ) + 1, 16 ) ); + Set set = new LinkedHashSet( Math.max( (int) ( foos.size() / .75f ) + 1, 16 ) ); for ( SourceFoo sourceFoo : foos ) { set.add( sourceFooToTargetFoo( sourceFoo ) ); } @@ -178,7 +178,7 @@ public Map sourceFooMapToTargetFooMap(Map fo return null; } - Map map = new HashMap( Math.max( (int) ( foos.size() / .75f ) + 1, 16 ) ); + Map map = new LinkedHashMap( Math.max( (int) ( foos.size() / .75f ) + 1, 16 ) ); for ( java.util.Map.Entry entry : foos.entrySet() ) { String key = String.valueOf( entry.getKey() ); diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/CompanyMapper1Impl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/CompanyMapper1Impl.java index 121dbea3ec..5eff50e342 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/CompanyMapper1Impl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/CompanyMapper1Impl.java @@ -5,7 +5,7 @@ */ package org.mapstruct.ap.test.updatemethods; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import javax.annotation.Generated; @@ -83,7 +83,7 @@ protected Map secretaryDtoEmployeeDtoMapToSecre return null; } - Map map1 = new HashMap( Math.max( (int) ( map.size() / .75f ) + 1, 16 ) ); + Map map1 = new LinkedHashMap( Math.max( (int) ( map.size() / .75f ) + 1, 16 ) ); for ( java.util.Map.Entry entry : map.entrySet() ) { SecretaryEntity key = secretaryDtoToSecretaryEntity( entry.getKey() ); diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/DepartmentMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/DepartmentMapperImpl.java index 338d6e9b57..1f21f0f637 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/DepartmentMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/updatemethods/selection/DepartmentMapperImpl.java @@ -6,7 +6,7 @@ package org.mapstruct.ap.test.updatemethods.selection; import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedHashMap; import javax.annotation.Generated; import org.mapstruct.ap.test.updatemethods.DepartmentDto; import org.mapstruct.ap.test.updatemethods.DepartmentEntity; @@ -42,7 +42,7 @@ public void toDepartmentEntity(DepartmentDto dto, DepartmentEntity entity) { } if ( dto.getSecretaryToEmployee() != null ) { if ( entity.getSecretaryToEmployee() == null ) { - entity.setSecretaryToEmployee( new HashMap() ); + entity.setSecretaryToEmployee( new LinkedHashMap() ); } externalHandWrittenMapper.toSecretaryEmployeeEntityMap( dto.getSecretaryToEmployee(), entity.getSecretaryToEmployee() ); } From 903e6f3f44dda85f935125b81d7e66e0056f5aa4 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 5 Apr 2021 13:58:18 +0200 Subject: [PATCH 154/597] #597 Add String <-> StringBuilder conversion in the documentation --- .../src/main/asciidoc/chapter-5-data-type-conversions.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index ce842df0e8..7800b6a933 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -117,6 +117,8 @@ public interface CarMapper { * Between `java.util.Currency` and `String`. ** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/ISO_4217[ISO-4217] alphabetic code otherwise an `IllegalArgumentException` is thrown +* Between `String` and `StringBuilder` + [[mapping-object-references]] === Mapping object references From 1c8fff1475829ad7524cf706d1d0b2ab36dc29bc Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 24 Apr 2021 17:55:25 +0200 Subject: [PATCH 155/597] #2423 Use SetterWrapperForCollectionsAndMapsWithNullCheck if the source has a presence check method --- .../model/CollectionAssignmentBuilder.java | 28 ++++++- .../ap/internal/model/PropertyMapping.java | 5 ++ .../spi/CollectionPresenceMapper.java | 52 +++++++++++++ .../spi/CollectionWithNonDirectMapper.java | 77 +++++++++++++++++++ .../CollectionWithNullValueCheckMapper.java | 53 +++++++++++++ .../presencecheck/spi/NonDirectMapper.java | 76 ++++++++++++++++++ .../spi/PresenceCheckMappingTest.java | 70 +++++++++++++++++ .../DomainDtoWithPresenceCheckMapperImpl.java | 8 +- 8 files changed, 365 insertions(+), 4 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionPresenceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionWithNonDirectMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionWithNullValueCheckMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/NonDirectMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckMappingTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index 7ab9814e23..dd6db1b0ee 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -22,6 +22,7 @@ import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; +import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_NULL; @@ -168,8 +169,7 @@ else if ( method.isUpdateMethod() && !targetImmutable ) { targetAccessorType.isFieldAssignment() ); } - else if ( result.getType() == Assignment.AssignmentType.DIRECT || - nvcs == NullValueCheckStrategyGem.ALWAYS ) { + else if ( setterWrapperNeedsSourceNullCheck( result ) ) { result = new SetterWrapperForCollectionsAndMapsWithNullCheck( result, @@ -212,4 +212,28 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT || return result; } + /** + * Checks whether the setter wrapper should include a null / presence check or not + * + * @param rhs the source right hand side + * @return whether to include a null / presence check or not + */ + private boolean setterWrapperNeedsSourceNullCheck(Assignment rhs) { + if ( rhs.getSourcePresenceCheckerReference() != null ) { + // If there is a source presence check then we should do a null check + return true; + } + + if ( nvcs == ALWAYS ) { + // NullValueCheckStrategy is ALWAYS -> do a null check + return true; + } + + if ( rhs.getType().isDirect() ) { + return true; + } + + return false; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 48ebc34d29..3e53e2ce06 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -454,6 +454,11 @@ private boolean setterWrapperNeedsSourceNullCheck(Assignment rhs, Type targetTyp return true; } + if ( rhs.getSourcePresenceCheckerReference() != null ) { + // There is an explicit source presence check method -> do a null / presence check + return true; + } + if ( nvpms == SET_TO_DEFAULT || nvpms == IGNORE ) { // NullValuePropertyMapping is SET_TO_DEFAULT or IGNORE -> do a null check return true; diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionPresenceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionPresenceMapper.java new file mode 100644 index 0000000000..f99cae4d3e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionPresenceMapper.java @@ -0,0 +1,52 @@ +/* + * 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.source.presencecheck.spi; + +import java.util.Collection; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CollectionPresenceMapper { + + CollectionPresenceMapper INSTANCE = Mappers.getMapper( CollectionPresenceMapper.class ); + + Target map(Source source); + + class Target { + + private Collection players; + + public Collection getPlayers() { + return players; + } + + public void setPlayers(Collection players) { + this.players = players; + } + } + + class Source { + + private final Collection players; + + public Source(Collection players) { + this.players = players; + } + + public Collection getPlayers() { + return players; + } + + public boolean hasPlayers() { + return players != null && !players.isEmpty(); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionWithNonDirectMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionWithNonDirectMapper.java new file mode 100644 index 0000000000..67daa22116 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionWithNonDirectMapper.java @@ -0,0 +1,77 @@ +/* + * 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.source.presencecheck.spi; + +import java.util.Collection; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CollectionWithNonDirectMapper { + + CollectionWithNonDirectMapper INSTANCE = Mappers.getMapper( CollectionWithNonDirectMapper.class ); + + Target map(Source source); + + class Target { + + private Collection players; + + public Collection getPlayers() { + return players; + } + + public void setPlayers(Collection players) { + this.players = players; + } + } + + class Player { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + class Source { + + private final Collection players; + + public Source(Collection players) { + this.players = players; + } + + public Collection getPlayers() { + return players; + } + + public boolean hasPlayers() { + return players != null && !players.isEmpty(); + } + } + + class PlayerSource { + + private final String name; + + public PlayerSource(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionWithNullValueCheckMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionWithNullValueCheckMapper.java new file mode 100644 index 0000000000..ef03be20c5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/CollectionWithNullValueCheckMapper.java @@ -0,0 +1,53 @@ +/* + * 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.source.presencecheck.spi; + +import java.util.Collection; + +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +public interface CollectionWithNullValueCheckMapper { + + CollectionWithNullValueCheckMapper INSTANCE = Mappers.getMapper( CollectionWithNullValueCheckMapper.class ); + + Target map(Source source); + + class Target { + + private Collection players; + + public Collection getPlayers() { + return players; + } + + public void setPlayers(Collection players) { + this.players = players; + } + } + + class Source { + + private final Collection players; + + public Source(Collection players) { + this.players = players; + } + + public Collection getPlayers() { + return players; + } + + public boolean hasPlayers() { + return players != null && !players.isEmpty(); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/NonDirectMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/NonDirectMapper.java new file mode 100644 index 0000000000..100ff39370 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/NonDirectMapper.java @@ -0,0 +1,76 @@ +/* + * 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.source.presencecheck.spi; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface NonDirectMapper { + + NonDirectMapper INSTANCE = Mappers.getMapper( NonDirectMapper.class ); + + SoccerTeam map(SoccerTeamSource source); + + class SoccerTeam { + + private Goalkeeper goalKeeper; + + public Goalkeeper getGoalKeeper() { + return goalKeeper; + } + + public void setGoalKeeper(Goalkeeper goalKeeper) { + this.goalKeeper = goalKeeper; + } + } + + class Goalkeeper { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + class SoccerTeamSource { + + private final GoalKeeperSource goalKeeper; + + public SoccerTeamSource(GoalKeeperSource goalKeeper) { + this.goalKeeper = goalKeeper; + } + + public GoalKeeperSource getGoalKeeper() { + return goalKeeper; + } + + public boolean hasGoalKeeper() { + return goalKeeper != null && goalKeeper.getName() != null && !goalKeeper.getName().isEmpty(); + } + } + + class GoalKeeperSource { + + private final String name; + + public GoalKeeperSource(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckMappingTest.java new file mode 100644 index 0000000000..c25dad70b5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckMappingTest.java @@ -0,0 +1,70 @@ +/* + * 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.source.presencecheck.spi; + +import java.util.Collections; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@RunWith(AnnotationProcessorTestRunner.class) +public class PresenceCheckMappingTest { + + @Test + @WithClasses({ + CollectionPresenceMapper.class + }) + public void collectionPresenceCheckShouldBeUsedByDefault() { + CollectionPresenceMapper.Source source = new CollectionPresenceMapper.Source( Collections.emptyList() ); + CollectionPresenceMapper.Target target = CollectionPresenceMapper.INSTANCE.map( source ); + + assertThat( target.getPlayers() ).isNull(); + } + + @Test + @WithClasses({ + CollectionWithNullValueCheckMapper.class + }) + public void collectionPresenceCheckShouldBeUsedByWhenNullValueCheckIsAlways() { + CollectionWithNullValueCheckMapper.Source source = + new CollectionWithNullValueCheckMapper.Source( Collections.emptyList() ); + CollectionWithNullValueCheckMapper.Target target = + CollectionWithNullValueCheckMapper.INSTANCE.map( source ); + + assertThat( target.getPlayers() ).isNull(); + } + + @Test + @WithClasses({ + CollectionWithNonDirectMapper.class + }) + public void nonDirectCollectionPresenceCheckShouldBeUsedByDefault() { + CollectionWithNonDirectMapper.Source source = + new CollectionWithNonDirectMapper.Source( Collections.emptyList() ); + CollectionWithNonDirectMapper.Target target = CollectionWithNonDirectMapper.INSTANCE.map( source ); + + assertThat( target.getPlayers() ).isNull(); + } + + @Test + @WithClasses({ + NonDirectMapper.class + }) + public void nonDirectMappingWithPresenceCheckShouldBeUsedByDefault() { + NonDirectMapper.SoccerTeamSource source = + new NonDirectMapper.SoccerTeamSource( new NonDirectMapper.GoalKeeperSource( "" ) ); + NonDirectMapper.SoccerTeam target = NonDirectMapper.INSTANCE.map( source ); + + assertThat( target.getGoalKeeper() ).isNull(); + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java index ba18dfda11..8cc41c47d5 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java @@ -32,12 +32,16 @@ public Domain create(DtoWithPresenceCheck source) { List list = source.getStrings(); domain.setStrings( new LinkedHashSet( list ) ); } - domain.setLongs( stringListToLongSet( source.getStrings() ) ); + if ( source.hasStrings() ) { + domain.setLongs( stringListToLongSet( source.getStrings() ) ); + } if ( source.hasStringsInitialized() ) { List list1 = source.getStringsInitialized(); domain.setStringsInitialized( new LinkedHashSet( list1 ) ); } - domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + if ( source.hasStringsInitialized() ) { + domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + } if ( source.hasStringsWithDefault() ) { List list2 = source.getStringsWithDefault(); domain.setStringsWithDefault( new ArrayList( list2 ) ); From a2e1404b9326d9b4953a66ba3226a31137224056 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 27 Mar 2021 18:38:41 +0100 Subject: [PATCH 156/597] Refactor presence checks to object in order to simplify the conditional mapping --- .../ap/internal/model/MethodReference.java | 3 +- .../model/NestedPropertyMappingMethod.java | 46 ++++++++++++--- .../ap/internal/model/PropertyMapping.java | 29 +++++++-- .../ap/internal/model/TypeConversion.java | 3 +- .../model/assignment/AssignmentWrapper.java | 3 +- .../ap/internal/model/common/Assignment.java | 2 +- .../internal/model/common/PresenceCheck.java | 24 ++++++++ .../ap/internal/model/common/SourceRHS.java | 11 +++- .../AllPresenceChecksPresenceCheck.java | 59 +++++++++++++++++++ .../model/presence/NullPresenceCheck.java | 52 ++++++++++++++++ .../SourceReferenceMethodPresenceCheck.java | 59 +++++++++++++++++++ .../model/NestedPropertyMappingMethod.ftl | 9 ++- .../ap/internal/model/macro/CommonMacros.ftl | 4 +- .../AllPresenceChecksPresenceCheck.ftl | 16 +++++ .../model/presence/NullPresenceCheck.ftl | 9 +++ .../SourceReferenceMethodPresenceCheck.ftl | 9 +++ 16 files changed, 309 insertions(+), 29 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/common/PresenceCheck.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.ftl create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.ftl create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.ftl diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java index 58c4ba890a..78a3830f45 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java @@ -19,6 +19,7 @@ import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.ParameterBinding; +import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod; @@ -197,7 +198,7 @@ public String getSourceReference() { } @Override - public String getSourcePresenceCheckerReference() { + public PresenceCheck getSourcePresenceCheckerReference() { return assignment.getSourcePresenceCheckerReference(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java index f5616d1278..82c02f5385 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java @@ -11,8 +11,10 @@ import java.util.Set; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry; +import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.ValueProvider; @@ -52,17 +54,27 @@ public Builder mappingContext(MappingBuilderContext mappingContext) { public NestedPropertyMappingMethod build() { List existingVariableNames = new ArrayList<>(); + Parameter sourceParameter = null; for ( Parameter parameter : method.getSourceParameters() ) { existingVariableNames.add( parameter.getName() ); + if ( sourceParameter == null && !parameter.isMappingTarget() && !parameter.isMappingContext() ) { + sourceParameter = parameter; + } } final List thrownTypes = new ArrayList<>(); List safePropertyEntries = new ArrayList<>(); + if ( sourceParameter == null ) { + throw new IllegalStateException( "Method " + method + " has no source parameter." ); + } + + String previousPropertyName = sourceParameter.getName(); for ( PropertyEntry propertyEntry : propertyEntries ) { String safeName = Strings.getSafeVariableName( propertyEntry.getName(), existingVariableNames ); - safePropertyEntries.add( new SafePropertyEntry( propertyEntry, safeName ) ); + safePropertyEntries.add( new SafePropertyEntry( propertyEntry, safeName, previousPropertyName ) ); existingVariableNames.add( safeName ); thrownTypes.addAll( ctx.getTypeFactory().getThrownTypes( propertyEntry.getReadAccessor() ) ); + previousPropertyName = safeName; } method.addThrownTypes( thrownTypes ); return new NestedPropertyMappingMethod( method, safePropertyEntries ); @@ -92,6 +104,9 @@ public Set getImportTypes() { Set types = super.getImportTypes(); for ( SafePropertyEntry propertyEntry : safePropertyEntries) { types.add( propertyEntry.getType() ); + if ( propertyEntry.getPresenceChecker() != null ) { + types.addAll( propertyEntry.getPresenceChecker().getImportTypes() ); + } } return types; } @@ -143,18 +158,23 @@ public static class SafePropertyEntry { private final String safeName; private final String readAccessorName; - private final String presenceCheckerName; + private final PresenceCheck presenceChecker; + private final String previousPropertyName; private final Type type; - public SafePropertyEntry(PropertyEntry entry, String safeName) { + public SafePropertyEntry(PropertyEntry entry, String safeName, String previousPropertyName) { this.safeName = safeName; this.readAccessorName = ValueProvider.of( entry.getReadAccessor() ).getValue(); if ( entry.getPresenceChecker() != null ) { - this.presenceCheckerName = entry.getPresenceChecker().getSimpleName(); + this.presenceChecker = new SourceReferenceMethodPresenceCheck( + previousPropertyName, + entry.getPresenceChecker().getSimpleName() + ); } else { - this.presenceCheckerName = null; + this.presenceChecker = null; } + this.previousPropertyName = previousPropertyName; this.type = entry.getType(); } @@ -166,8 +186,12 @@ public String getAccessorName() { return readAccessorName; } - public String getPresenceCheckerName() { - return presenceCheckerName; + public PresenceCheck getPresenceChecker() { + return presenceChecker; + } + + public String getPreviousPropertyName() { + return previousPropertyName; } public Type getType() { @@ -189,7 +213,11 @@ public boolean equals(Object o) { return false; } - if ( !Objects.equals( presenceCheckerName, that.presenceCheckerName ) ) { + if ( !Objects.equals( presenceChecker, that.presenceChecker ) ) { + return false; + } + + if ( !Objects.equals( previousPropertyName, that.previousPropertyName ) ) { return false; } @@ -203,7 +231,7 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = readAccessorName != null ? readAccessorName.hashCode() : 0; - result = 31 * result + ( presenceCheckerName != null ? presenceCheckerName.hashCode() : 0 ); + result = 31 * result + ( presenceChecker != null ? presenceChecker.hashCode() : 0 ); result = 31 * result + ( type != null ? type.hashCode() : 0 ); return result; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 3e53e2ce06..d1d7eaebd5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -28,8 +28,12 @@ import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.presence.AllPresenceChecksPresenceCheck; +import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; +import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck; import org.mapstruct.ap.internal.model.source.DelegatingOptions; import org.mapstruct.ap.internal.model.source.MappingControl; import org.mapstruct.ap.internal.model.source.MappingOptions; @@ -609,30 +613,43 @@ else if ( !sourceReference.isNested() ) { } } - private String getSourcePresenceCheckerRef( SourceReference sourceReference ) { - String sourcePresenceChecker = null; + private PresenceCheck getSourcePresenceCheckerRef(SourceReference sourceReference ) { + PresenceCheck sourcePresenceChecker = null; if ( !sourceReference.getPropertyEntries().isEmpty() ) { Parameter sourceParam = sourceReference.getParameter(); // TODO is first correct here?? shouldn't it be last since the remainer is checked // in the forged method? PropertyEntry propertyEntry = sourceReference.getShallowestProperty(); if ( propertyEntry.getPresenceChecker() != null ) { - sourcePresenceChecker = sourceParam.getName() - + "." + propertyEntry.getPresenceChecker().getSimpleName() + "()"; + List presenceChecks = new ArrayList<>(); + presenceChecks.add( new SourceReferenceMethodPresenceCheck( + sourceParam.getName(), + propertyEntry.getPresenceChecker().getSimpleName() + ) ); String variableName = sourceParam.getName() + "." + propertyEntry.getReadAccessor().getSimpleName() + "()"; for (int i = 1; i < sourceReference.getPropertyEntries().size(); i++) { PropertyEntry entry = sourceReference.getPropertyEntries().get( i ); if (entry.getPresenceChecker() != null && entry.getReadAccessor() != null) { - sourcePresenceChecker += " && " + variableName + " != null && " - + variableName + "." + entry.getPresenceChecker().getSimpleName() + "()"; + presenceChecks.add( new NullPresenceCheck( variableName ) ); + presenceChecks.add( new SourceReferenceMethodPresenceCheck( + variableName, + entry.getPresenceChecker().getSimpleName() + ) ); variableName = variableName + "." + entry.getReadAccessor().getSimpleName() + "()"; } else { break; } } + + if ( presenceChecks.size() == 1 ) { + sourcePresenceChecker = presenceChecks.get( 0 ); + } + else { + sourcePresenceChecker = new AllPresenceChecksPresenceCheck( presenceChecks ); + } } } return sourcePresenceChecker; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java index 3f06ce7e85..c5b4bc6915 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java @@ -11,6 +11,7 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; /** @@ -79,7 +80,7 @@ public boolean isSourceReferenceParameter() { } @Override - public String getSourcePresenceCheckerReference() { + public PresenceCheck getSourcePresenceCheckerReference() { return assignment.getSourcePresenceCheckerReference(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java index 551d20d72b..54885012a9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java @@ -10,6 +10,7 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; /** @@ -57,7 +58,7 @@ public boolean isSourceReferenceParameter() { } @Override - public String getSourcePresenceCheckerReference() { + public PresenceCheck getSourcePresenceCheckerReference() { return decoratedAssignment.getSourcePresenceCheckerReference(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java index 3cd32c82ea..293df9516b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java @@ -90,7 +90,7 @@ public boolean isConverted() { * * @return source reference */ - String getSourcePresenceCheckerReference(); + PresenceCheck getSourcePresenceCheckerReference(); /** * the source type used in the matching process diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/PresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/PresenceCheck.java new file mode 100644 index 0000000000..e77e24f218 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/PresenceCheck.java @@ -0,0 +1,24 @@ +/* + * 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.internal.model.common; + +import java.util.Set; + +/** + * Marker interface for presence checks. + * + * @author Filip Hrisafov + */ +public interface PresenceCheck { + + /** + * returns all types required as import by the presence check. + * + * @return imported types + */ + Set getImportTypes(); + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java index 5dc5d91832..7861c03f2a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java @@ -7,6 +7,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Stream; @@ -30,7 +31,7 @@ public class SourceRHS extends ModelElement implements Assignment { private String sourceLoopVarName; private final Set existingVariableNames; private final String sourceErrorMessagePart; - private final String sourcePresenceCheckerReference; + private final PresenceCheck sourcePresenceCheckerReference; private boolean useElementAsSourceTypeForMatching = false; private final String sourceParameterName; @@ -39,7 +40,7 @@ public SourceRHS(String sourceReference, Type sourceType, Set existingVa this( sourceReference, sourceReference, null, sourceType, existingVariableNames, sourceErrorMessagePart ); } - public SourceRHS(String sourceParameterName, String sourceReference, String sourcePresenceCheckerReference, + public SourceRHS(String sourceParameterName, String sourceReference, PresenceCheck sourcePresenceCheckerReference, Type sourceType, Set existingVariableNames, String sourceErrorMessagePart ) { this.sourceReference = sourceReference; this.sourceType = sourceType; @@ -60,7 +61,7 @@ public boolean isSourceReferenceParameter() { } @Override - public String getSourcePresenceCheckerReference() { + public PresenceCheck getSourcePresenceCheckerReference() { return sourcePresenceCheckerReference; } @@ -96,6 +97,10 @@ public void setSourceLoopVarName(String sourceLoopVarName) { @Override public Set getImportTypes() { + if ( sourcePresenceCheckerReference != null ) { + return sourcePresenceCheckerReference.getImportTypes(); + } + return Collections.emptySet(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java new file mode 100644 index 0000000000..4ee62cf542 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.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.internal.model.presence; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Filip Hrisafov + */ +public class AllPresenceChecksPresenceCheck extends ModelElement implements PresenceCheck { + + private final Collection presenceChecks; + + public AllPresenceChecksPresenceCheck(Collection presenceChecks) { + this.presenceChecks = presenceChecks; + } + + public Collection getPresenceChecks() { + return presenceChecks; + } + + @Override + public Set getImportTypes() { + Set importTypes = new HashSet<>(); + for ( PresenceCheck presenceCheck : presenceChecks ) { + importTypes.addAll( presenceCheck.getImportTypes() ); + } + + return importTypes; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + AllPresenceChecksPresenceCheck that = (AllPresenceChecksPresenceCheck) o; + return Objects.equals( presenceChecks, that.presenceChecks ); + } + + @Override + public int hashCode() { + return Objects.hash( presenceChecks ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java new file mode 100644 index 0000000000..c9cae61b7c --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java @@ -0,0 +1,52 @@ +/* + * 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.internal.model.presence; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Filip Hrisafov + */ +public class NullPresenceCheck extends ModelElement implements PresenceCheck { + + private final String sourceReference; + + public NullPresenceCheck(String sourceReference) { + this.sourceReference = sourceReference; + } + + public String getSourceReference() { + return sourceReference; + } + + @Override + public Set getImportTypes() { + return Collections.emptySet(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + NullPresenceCheck that = (NullPresenceCheck) o; + return Objects.equals( sourceReference, that.sourceReference ); + } + + @Override + public int hashCode() { + return Objects.hash( sourceReference ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.java new file mode 100644 index 0000000000..374eac0c8e --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.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.internal.model.presence; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Filip Hrisafov + */ +public class SourceReferenceMethodPresenceCheck extends ModelElement implements PresenceCheck { + + private final String sourceReference; + private final String methodName; + + public SourceReferenceMethodPresenceCheck(String sourceReference, String methodName) { + this.sourceReference = sourceReference; + this.methodName = methodName; + } + + public String getSourceReference() { + return sourceReference; + } + + public String getMethodName() { + return methodName; + } + + @Override + public Set getImportTypes() { + return Collections.emptySet(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + SourceReferenceMethodPresenceCheck that = (SourceReferenceMethodPresenceCheck) o; + return Objects.equals( sourceReference, that.sourceReference ) && + Objects.equals( methodName, that.methodName ); + } + + @Override + public int hashCode() { + return Objects.hash( sourceReference, methodName ); + } +} diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl index 22dc2d8c37..ce6dab0714 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl @@ -11,13 +11,13 @@ return ${returnType.null}; } <#list propertyEntries as entry> - <#if entry.presenceCheckerName?? > - if ( <#if entry_index != 0><@localVarName index=entry_index/> == null || !<@localVarName index=entry_index/>.${entry.presenceCheckerName}() ) { + <#if entry.presenceChecker?? > + if ( <#if entry_index != 0>${entry.previousPropertyName} == null || !<@includeModel object=entry.presenceChecker /> ) { return ${returnType.null}; } - <@includeModel object=entry.type.typeBound/> ${entry.name} = <@localVarName index=entry_index/>.${entry.accessorName}; - <#if !entry.presenceCheckerName?? > + <@includeModel object=entry.type.typeBound/> ${entry.name} = ${entry.previousPropertyName}.${entry.accessorName}; + <#if !entry.presenceChecker?? > <#if !entry.type.primitive> if ( ${entry.name} == null ) { return ${returnType.null}; @@ -29,7 +29,6 @@ } -<#macro localVarName index><#if index == 0>${sourceParameter.name}<#else>${propertyEntries[index-1].name} <#macro throws> <#if (thrownTypes?size > 0)><#lt> throws <@compress single_line=true> <#list thrownTypes as exceptionType> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index de456ca773..048402401b 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -15,7 +15,7 @@ --> <#macro handleSourceReferenceNullCheck> <#if sourcePresenceCheckerReference??> - if ( ${sourcePresenceCheckerReference} ) { + if ( <@includeModel object=sourcePresenceCheckerReference /> ) { <#nested> } <@elseDefaultAssignment/> @@ -57,7 +57,7 @@ --> <#macro handleLocalVarNullCheck needs_explicit_local_var> <#if sourcePresenceCheckerReference??> - if ( ${sourcePresenceCheckerReference} ) { + if ( <@includeModel object=sourcePresenceCheckerReference /> ) { <#if needs_explicit_local_var> <@includeModel object=nullCheckLocalVarType/> ${nullCheckLocalVarName} = <@lib.handleAssignment/>; <#nested> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.ftl new file mode 100644 index 0000000000..d0c81ff2ce --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.ftl @@ -0,0 +1,16 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.AllPresenceChecksPresenceCheck" --> +<@compress single_line=true> +<#list presenceChecks as presenceCheck> + <#if presenceCheck_index != 0> + && + + <@includeModel object=presenceCheck /> + + \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.ftl new file mode 100644 index 0000000000..6e5494c735 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.ftl @@ -0,0 +1,9 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.NullPresenceCheck" --> +${sourceReference} != null \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.ftl new file mode 100644 index 0000000000..930d5e24bc --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.ftl @@ -0,0 +1,9 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck" --> +${sourceReference}.${methodName}() \ No newline at end of file From 51cdbd67e3f6b65ca2f3879f333661be2c5d215b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 4 Apr 2021 19:36:46 +0200 Subject: [PATCH 157/597] #2051, #2084 Add new `@Condition` annotation for custom presence check methods --- .../main/java/org/mapstruct/Condition.java | 78 ++++++ core/src/main/java/org/mapstruct/Mapping.java | 68 ++++++ ...apter-10-advanced-mapping-options.asciidoc | 72 ++++++ .../ap/internal/gem/GemGenerator.java | 2 + .../ap/internal/model/BeanMappingMethod.java | 1 + .../model/MethodReferencePresenceCheck.java | 51 ++++ .../model/PresenceCheckMethodResolver.java | 149 ++++++++++++ .../ap/internal/model/PropertyMapping.java | 61 ++++- .../ap/internal/model/common/SourceRHS.java | 7 +- .../AllPresenceChecksPresenceCheck.java | 1 - .../presence/JavaExpressionPresenceCheck.java | 52 ++++ .../internal/model/source/MappingOptions.java | 35 +++ .../ap/internal/model/source/Method.java | 8 + .../model/source/SelectionParameters.java | 64 ++++- .../internal/model/source/SourceMethod.java | 22 ++ .../selector/CreateOrUpdateSelector.java | 3 +- .../source/selector/MethodFamilySelector.java | 4 +- .../source/selector/MethodSelectors.java | 1 + .../source/selector/SelectionCriteria.java | 55 +++-- .../source/selector/SourceRhsSelector.java | 49 ++++ .../model/source/selector/TypeSelector.java | 2 +- .../processor/MethodRetrievalProcessor.java | 19 +- .../mapstruct/ap/internal/util/Message.java | 2 + .../model/MethodReferencePresenceCheck.ftl | 9 + .../presence/JavaExpressionPresenceCheck.ftl | 9 + .../ap/test/conditional/Employee.java | 36 +++ .../ap/test/conditional/EmployeeDto.java | 36 +++ .../test/conditional/basic/BasicEmployee.java | 22 ++ .../conditional/basic/BasicEmployeeDto.java | 32 +++ .../basic/ConditionalMappingTest.java | 229 ++++++++++++++++++ ...ionalMethodAndBeanPresenceCheckMapper.java | 44 ++++ .../ConditionalMethodForCollectionMapper.java | 81 +++++++ .../ConditionalMethodInContextMapper.java | 29 +++ .../basic/ConditionalMethodInMapper.java | 26 ++ .../basic/ConditionalMethodInUsesMapper.java | 30 +++ .../ConditionalMethodInUsesStaticMapper.java | 30 +++ ...thodWithSourceParameterAndValueMapper.java | 38 +++ ...tionalMethodWithSourceParameterMapper.java | 28 +++ ...neousAmbiguousConditionalMethodMapper.java | 28 +++ .../basic/OptionalLikeConditionalMapper.java | 77 ++++++ .../expression/ConditionalExpressionTest.java | 97 ++++++++ .../ConditionalMethodsInUtilClassMapper.java | 38 +++ .../ErroneousConditionExpressionMapper.java | 21 ++ .../SimpleConditionalExpressionMapper.java | 46 ++++ ...tionalMethodWithClassQualifiersMapper.java | 63 +++++ ...tionalMethodWithSourceParameterMapper.java | 48 ++++ .../qualifier/ConditionalQualifierTest.java | 92 +++++++ .../basic/ConditionalMethodInMapperImpl.java | 31 +++ ...WithSourceParameterAndValueMapperImpl.java | 31 +++ 49 files changed, 2015 insertions(+), 42 deletions(-) create mode 100644 core/src/main/java/org/mapstruct/Condition.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.ftl create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/Employee.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/EmployeeDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/BasicEmployee.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/BasicEmployeeDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodAndBeanPresenceCheckMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForCollectionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInContextMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInUsesMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInUsesStaticMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterAndValueMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousAmbiguousConditionalMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/OptionalLikeConditionalMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalMethodsInUtilClassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ErroneousConditionExpressionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/expression/SimpleConditionalExpressionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithClassQualifiersMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceParameterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterAndValueMapperImpl.java diff --git a/core/src/main/java/org/mapstruct/Condition.java b/core/src/main/java/org/mapstruct/Condition.java new file mode 100644 index 0000000000..ec1bf06a88 --- /dev/null +++ b/core/src/main/java/org/mapstruct/Condition.java @@ -0,0 +1,78 @@ +/* + * 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; + +/** + * This annotation marks a method as a presence check method to check check for presence in beans. + *

      + * By default bean properties are checked against {@code null} or using a presence check method in the source bean. + * If a presence check method is available then it will be used instead. + *

      + * Presence check methods have to return {@code boolean}. + * The following parameters are accepted for the presence check methods: + *

        + *
      • The parameter with the value of the source property. + * e.g. the value given by calling {@code getName()} for the name property of the source bean
      • + *
      • The mapping source parameter
      • + *
      • {@code @}{@link Context} parameter
      • + *
      + * + * Note: The usage of this annotation is mandatory + * for a method to be considered as a presence check method. + * + *
      
      + * public class PresenceCheckUtils {
      + *
      + *   @Condition
      + *   public static boolean isNotEmpty(String value) {
      + *      return value != null && !value.isEmpty();
      + *   }
      + * }
      + *
      + * @Mapper(uses = PresenceCheckUtils.class)
      + * public interface MovieMapper {
      + *
      + *     MovieDto map(Movie movie);
      + * }
      + * 
      + * + * The following implementation of {@code MovieMapper} will be generated: + * + *
      + * 
      + * public class MovieMapperImpl implements MovieMapper {
      + *
      + *     @Override
      + *     public MovieDto map(Movie movie) {
      + *         if ( movie == null ) {
      + *             return null;
      + *         }
      + *
      + *         MovieDto movieDto = new MovieDto();
      + *
      + *         if ( PresenceCheckUtils.isNotEmpty( movie.getTitle() ) ) {
      + *             movieDto.setTitle( movie.getTitle() );
      + *         }
      + *
      + *         return movieDto;
      + *     }
      + * }
      + * 
      + * 
      + * + * @author Filip Hrisafov + * @since 1.5 + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.CLASS) +public @interface Condition { + +} diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index b5ce5c7a83..b4bfa30d88 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -316,6 +316,74 @@ */ String[] qualifiedByName() default { }; + /** + * A qualifier can be specified to aid the selection process of a suitable presence check method. + * This is useful in case multiple presence check methods qualify and thus would result in an + * 'Ambiguous presence check methods found' error. + * A qualifier is a custom annotation and can be placed on a hand written mapper class or a method. + * This is similar to the {@link #qualifiedBy()}, but it is only applied for {@link Condition} methods. + * + * @return the qualifiers + * @see Qualifier + * @see #qualifiedBy() + * @since 1.5 + */ + Class[] conditionQualifiedBy() default { }; + + /** + * String-based form of qualifiers for condition / presence check methods; + * When looking for a suitable presence check method for a given property, MapStruct will + * only consider those methods carrying directly or indirectly (i.e. on the class-level) a {@link Named} annotation + * for each of the specified qualifier names. + * + * This is similar like {@link #qualifiedByName()} but it is only applied for {@link Condition} methods. + *

      + * Note that annotation-based qualifiers are generally preferable as they allow more easily to find references and + * are safe for refactorings, but name-based qualifiers can be a less verbose alternative when requiring a large + * number of qualifiers as no custom annotation types are needed. + *

      + * + * + * @return One or more qualifier name(s) + * @see #conditionQualifiedBy() + * @see #qualifiedByName() + * @see Named + * @since 1.5 + */ + String[] conditionQualifiedByName() default { }; + + /** + * A conditionExpression {@link String} based on which the specified property is to be checked + * whether it is present or not. + *

      + * Currently, Java is the only supported "expression language" and expressions must be given in form of Java + * expressions using the following format: {@code java()}. For instance the mapping: + *

      
      +     * @Mapping(
      +     *     target = "someProp",
      +     *     conditionExpression = "java(s.getAge() < 18)"
      +     * )
      +     * 
      + *

      + * will cause the following target property assignment to be generated: + *

      
      +     *     if (s.getAge() < 18) {
      +     *         targetBean.setSomeProp( s.getSomeProp() );
      +     *     }
      +     * 
      + *

      + *

      + * Any types referenced in expressions must be given via their fully-qualified name. Alternatively, types can be + * imported via {@link Mapper#imports()}. + *

      + * This attribute can not be used together with {@link #expression()} or {@link #constant()}. + * + * @return An expression specifying a condition check for the designated property + * + * @since 1.5 + */ + String conditionExpression() default ""; + /** * Specifies the result type of the mapping method to be used in case multiple mapping methods qualify. * diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 158aa632f9..d3ab2424b1 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -229,6 +229,78 @@ The source presence checker name can be changed in the MapStruct service provide 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 `NullValueCheckStrategy` to avoid addition of `null` to the target collection or map. ==== + +[[conditional-mapping]] +=== Conditional Mapping + +Conditional Mapping is a type of <>. +The difference is that it allows users to write custom condition methods that will be invoked to check if a property needs to be mapped or not. + +A custom condition method is a method that is annotated with `org.mapstruct.Condition` and returns `boolean`. + +e.g. if you only want to map a String property when it is not `null, and it is not empty then you can do something like: + +.Mapper using custom condition check method +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface CarMapper { + + CarDto carToCarDto(Car car); + + @Condition + default boolean isNotEmpty(String value) { + return value != null && !value.isEmpty(); + } +} +---- +==== + +The generated mapper will look like: + +.try-catch block in generated implementation +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class CarMapperImpl implements CarMapper { + + @Override + public CarDto carToCarDto(Car car) { + if ( car == null ) { + return null; + } + + CarDto carDto = new CarDto(); + + if ( isNotEmpty( car.getOwner() ) ) { + carDto.setOwner( car.getOwner() ); + } + + // Mapping of other properties + + return carDto; + } +} +---- +==== + +[IMPORTANT] +==== +If there is a custom `@Condition` method applicable for the property it will have a precedence over a presence check method in the bean itself. +==== + +[NOTE] +==== +Methods annotated with `@Condition` in addition to the value of the source property can also have the source parameter as an input. +==== + +<> is also valid for `@Condition` methods. +In order to use a more specific condition method you will need to use one of `Mapping#conditionQualifiedByName` or `Mapping#conditionQualifiedBy`. + [[exceptions]] === Exceptions diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index 524ea995d3..f8bdbc539d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -12,6 +12,7 @@ import org.mapstruct.BeanMapping; import org.mapstruct.BeforeMapping; import org.mapstruct.Builder; +import org.mapstruct.Condition; import org.mapstruct.Context; import org.mapstruct.DecoratedWith; import org.mapstruct.EnumMapping; @@ -61,6 +62,7 @@ @GemDefinition(ValueMappings.class) @GemDefinition(Context.class) @GemDefinition(Builder.class) +@GemDefinition(Condition.class) @GemDefinition(MappingControl.class) @GemDefinition(MappingControls.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 0ce80c993c..2e82366f8b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1155,6 +1155,7 @@ else if ( mapping.getJavaExpression() != null ) { .dependsOn( mapping.getDependsOn() ) .defaultValue( mapping.getDefaultValue() ) .defaultJavaExpression( mapping.getDefaultJavaExpression() ) + .conditionJavaExpression( mapping.getConditionJavaExpression() ) .mirror( mapping.getMirror() ) .options( mapping ) .build(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java new file mode 100644 index 0000000000..e6adceab03 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java @@ -0,0 +1,51 @@ +/* + * 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.internal.model; + +import java.util.Objects; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Filip Hrisafov + */ +public class MethodReferencePresenceCheck extends ModelElement implements PresenceCheck { + + protected final MethodReference methodReference; + + public MethodReferencePresenceCheck(MethodReference methodReference) { + this.methodReference = methodReference; + } + + @Override + public Set getImportTypes() { + return methodReference.getImportTypes(); + } + + public MethodReference getMethodReference() { + return methodReference; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + MethodReferencePresenceCheck that = (MethodReferencePresenceCheck) o; + return Objects.equals( methodReference, that.methodReference ); + } + + @Override + public int hashCode() { + return Objects.hash( methodReference ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java new file mode 100644 index 0000000000..9d4910bdce --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java @@ -0,0 +1,149 @@ +/* + * 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.internal.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.source.Method; +import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; +import org.mapstruct.ap.internal.model.source.SelectionParameters; +import org.mapstruct.ap.internal.model.source.SourceMethod; +import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; +import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; +import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; +import org.mapstruct.ap.internal.util.Message; + +/** + * @author Filip Hrisafov + */ +public final class PresenceCheckMethodResolver { + + private PresenceCheckMethodResolver() { + + } + + public static PresenceCheck getPresenceCheck( + Method method, + SelectionParameters selectionParameters, + MappingBuilderContext ctx + ) { + SelectedMethod matchingMethod = findMatchingPresenceCheckMethod( + method, + selectionParameters, + ctx + ); + + if ( matchingMethod == null ) { + return null; + } + + MethodReference methodReference = getPresenceCheckMethodReference( method, matchingMethod, ctx ); + + return new MethodReferencePresenceCheck( methodReference ); + + } + + private static SelectedMethod findMatchingPresenceCheckMethod( + Method method, + SelectionParameters selectionParameters, + MappingBuilderContext ctx + ) { + MethodSelectors selectors = new MethodSelectors( + ctx.getTypeUtils(), + ctx.getElementUtils(), + ctx.getTypeFactory(), + ctx.getMessager() + ); + + Type booleanType = ctx.getTypeFactory().getType( Boolean.class ); + List> matchingMethods = selectors.getMatchingMethods( + method, + getAllAvailableMethods( method, ctx.getSourceModel() ), + Collections.emptyList(), + booleanType, + booleanType, + SelectionCriteria.forPresenceCheckMethods( selectionParameters ) + ); + + if ( matchingMethods.isEmpty() ) { + return null; + } + + if ( matchingMethods.size() > 1 ) { + ctx.getMessager().printMessage( + method.getExecutable(), + Message.GENERAL_AMBIGUOUS_PRESENCE_CHECK_METHOD, + selectionParameters.getSourceRHS().getSourceType().describe(), + matchingMethods.stream() + .map( SelectedMethod::getMethod ) + .map( Method::describe ) + .collect( Collectors.joining( ", " ) ) + ); + + return null; + } + + return matchingMethods.get( 0 ); + } + + private static MethodReference getPresenceCheckMethodReference( + Method method, + SelectedMethod matchingMethod, + MappingBuilderContext ctx + ) { + + Parameter providingParameter = + method.getContextProvidedMethods().getParameterForProvidedMethod( matchingMethod.getMethod() ); + if ( providingParameter != null ) { + return MethodReference.forParameterProvidedMethod( + matchingMethod.getMethod(), + providingParameter, + matchingMethod.getParameterBindings() + ); + } + else { + MapperReference ref = MapperReference.findMapperReference( + ctx.getMapperReferences(), + matchingMethod.getMethod() + ); + + return MethodReference.forMapperReference( + matchingMethod.getMethod(), + ref, + matchingMethod.getParameterBindings() + ); + } + } + + private static List getAllAvailableMethods(Method method, List sourceModelMethods) { + ParameterProvidedMethods contextProvidedMethods = method.getContextProvidedMethods(); + if ( contextProvidedMethods.isEmpty() ) { + return sourceModelMethods; + } + + List methodsProvidedByParams = contextProvidedMethods + .getAllProvidedMethodsInParameterOrder( method.getContextParameters() ); + + List availableMethods = + new ArrayList<>( methodsProvidedByParams.size() + sourceModelMethods.size() ); + + for ( SourceMethod methodProvidedByParams : methodsProvidedByParams ) { + // add only methods from context that do have the @Condition annotation + if ( methodProvidedByParams.isPresenceCheck() ) { + availableMethods.add( methodProvidedByParams ); + } + } + availableMethods.addAll( sourceModelMethods ); + + return availableMethods; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index d1d7eaebd5..b53b8f90ec 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -13,6 +13,9 @@ import java.util.Set; import javax.lang.model.element.AnnotationMirror; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.model.assignment.AdderWrapper; import org.mapstruct.ap.internal.model.assignment.ArrayCopyWrapper; import org.mapstruct.ap.internal.model.assignment.EnumConstantWrapper; @@ -32,6 +35,7 @@ import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.presence.AllPresenceChecksPresenceCheck; +import org.mapstruct.ap.internal.model.presence.JavaExpressionPresenceCheck; import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck; import org.mapstruct.ap.internal.model.source.DelegatingOptions; @@ -40,9 +44,6 @@ import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; -import org.mapstruct.ap.internal.gem.BuilderGem; -import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; -import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.NativeTypes; import org.mapstruct.ap.internal.util.Strings; @@ -52,12 +53,12 @@ import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_NULL; import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping; import static org.mapstruct.ap.internal.model.ForgedMethod.forParameterMapping; import static org.mapstruct.ap.internal.model.ForgedMethod.forPropertyMapping; import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT; -import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; -import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_NULL; /** * Represents the mapping between a source and target property, e.g. from {@code String Source#foo} to @@ -142,6 +143,7 @@ public static class PropertyMappingBuilder extends MappingBuilderBase existingVariableNames; private final String sourceErrorMessagePart; - private final PresenceCheck sourcePresenceCheckerReference; + private PresenceCheck sourcePresenceCheckerReference; private boolean useElementAsSourceTypeForMatching = false; private final String sourceParameterName; @@ -65,6 +64,10 @@ public PresenceCheck getSourcePresenceCheckerReference() { return sourcePresenceCheckerReference; } + public void setSourcePresenceCheckerReference(PresenceCheck sourcePresenceCheckerReference) { + this.sourcePresenceCheckerReference = sourcePresenceCheckerReference; + } + @Override public Type getSourceType() { return sourceType; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java index 4ee62cf542..df8b008032 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java @@ -6,7 +6,6 @@ package org.mapstruct.ap.internal.model.presence; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java new file mode 100644 index 0000000000..d4f52c9f27 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java @@ -0,0 +1,52 @@ +/* + * 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.internal.model.presence; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Filip Hrisafov + */ +public class JavaExpressionPresenceCheck extends ModelElement implements PresenceCheck { + + private final String javaExpression; + + public JavaExpressionPresenceCheck(String javaExpression) { + this.javaExpression = javaExpression; + } + + public String getJavaExpression() { + return javaExpression; + } + + @Override + public Set getImportTypes() { + return Collections.emptySet(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + JavaExpressionPresenceCheck that = (JavaExpressionPresenceCheck) o; + return Objects.equals( javaExpression, that.javaExpression ); + } + + @Override + public int hashCode() { + return Objects.hash( javaExpression ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 9db1b1c04c..d7d6ad6291 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -43,6 +43,7 @@ public class MappingOptions extends DelegatingOptions { private final String constant; private final String javaExpression; private final String defaultJavaExpression; + private final String conditionJavaExpression; private final String targetName; private final String defaultValue; private final FormattingParameters formattingParameters; @@ -114,6 +115,7 @@ public static void addInstance(MappingGem mapping, ExecutableElement method, String constant = mapping.constant().getValue(); String expression = getExpression( mapping, method, messager ); String defaultExpression = getDefaultExpression( mapping, method, messager ); + String conditionExpression = getConditionExpression( mapping, method, messager ); String dateFormat = mapping.dateFormat().getValue(); String numberFormat = mapping.numberFormat().getValue(); String defaultValue = mapping.defaultValue().getValue(); @@ -132,6 +134,8 @@ public static void addInstance(MappingGem mapping, ExecutableElement method, SelectionParameters selectionParams = new SelectionParameters( mapping.qualifiedBy().get(), mapping.qualifiedByName().get(), + mapping.conditionQualifiedBy().get(), + mapping.conditionQualifiedByName().get(), mapping.resultType().getValue(), typeUtils ); @@ -145,6 +149,7 @@ public static void addInstance(MappingGem mapping, ExecutableElement method, constant, expression, defaultExpression, + conditionExpression, defaultValue, mapping.ignore().get(), formattingParam, @@ -174,6 +179,7 @@ public static MappingOptions forIgnore(String targetName) { null, null, null, + null, true, null, null, @@ -261,6 +267,7 @@ private MappingOptions(String targetName, String constant, String javaExpression, String defaultJavaExpression, + String conditionJavaExpression, String defaultValue, boolean isIgnored, FormattingParameters formattingParameters, @@ -279,6 +286,7 @@ private MappingOptions(String targetName, this.constant = constant; this.javaExpression = javaExpression; this.defaultJavaExpression = defaultJavaExpression; + this.conditionJavaExpression = conditionJavaExpression; this.defaultValue = defaultValue; this.isIgnored = isIgnored; this.formattingParameters = formattingParameters; @@ -330,6 +338,27 @@ private static String getDefaultExpression(MappingGem mapping, ExecutableElement return javaExpressionMatcher.group( 1 ).trim(); } + private static String getConditionExpression(MappingGem mapping, ExecutableElement element, + FormattingMessager messager) { + if ( !mapping.conditionExpression().hasValue() ) { + return null; + } + + Matcher javaExpressionMatcher = JAVA_EXPRESSION.matcher( mapping.conditionExpression().get() ); + + if ( !javaExpressionMatcher.matches() ) { + messager.printMessage( + element, + mapping.mirror(), + mapping.conditionExpression().getAnnotationValue(), + Message.PROPERTYMAPPING_INVALID_CONDITION_EXPRESSION + ); + return null; + } + + return javaExpressionMatcher.group( 1 ).trim(); + } + public String getTargetName() { return targetName; } @@ -364,6 +393,10 @@ public String getDefaultJavaExpression() { return defaultJavaExpression; } + public String getConditionJavaExpression() { + return conditionJavaExpression; + } + public String getDefaultValue() { return defaultValue; } @@ -452,6 +485,7 @@ public MappingOptions copyForInverseInheritance(SourceMethod templateMethod, null, // constant null, // expression null, // defaultExpression + null, // conditionExpression null, isIgnored, formattingParameters, @@ -481,6 +515,7 @@ public MappingOptions copyForForwardInheritance(SourceMethod templateMethod, constant, javaExpression, defaultJavaExpression, + conditionJavaExpression, defaultValue, isIgnored, formattingParameters, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index 8a38ed2acb..0c60f41364 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -90,6 +90,14 @@ public interface Method { */ boolean isObjectFactory(); + /** + * Returns whether the method is designated as a presence check method + * @return {@code true} if it is a presence check method + */ + default boolean isPresenceCheck() { + return false; + } + /** * Returns the parameter designated as target type (if present) {@link org.mapstruct.TargetType } * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java index a78409582d..697f0d9e4a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java @@ -23,6 +23,8 @@ public class SelectionParameters { private final List qualifiers; private final List qualifyingNames; + private final List conditionQualifiers; + private final List conditionQualifyingNames; private final TypeMirror resultType; private final TypeUtils typeUtils; private final SourceRHS sourceRHS; @@ -39,6 +41,8 @@ public static SelectionParameters forInheritance(SelectionParameters selectionPa return new SelectionParameters( selectionParameters.qualifiers, selectionParameters.qualifyingNames, + selectionParameters.conditionQualifiers, + selectionParameters.conditionQualifyingNames, null, selectionParameters.typeUtils ); @@ -46,13 +50,32 @@ public static SelectionParameters forInheritance(SelectionParameters selectionPa public SelectionParameters(List qualifiers, List qualifyingNames, TypeMirror resultType, TypeUtils typeUtils) { - this( qualifiers, qualifyingNames, resultType, typeUtils, null ); + this( + qualifiers, + qualifyingNames, + Collections.emptyList(), + Collections.emptyList(), + resultType, + typeUtils, + null + ); } - private SelectionParameters(List qualifiers, List qualifyingNames, TypeMirror resultType, + public SelectionParameters(List qualifiers, List qualifyingNames, + List conditionQualifiers, List conditionQualifyingNames, + TypeMirror resultType, + TypeUtils typeUtils) { + this( qualifiers, qualifyingNames, conditionQualifiers, conditionQualifyingNames, resultType, typeUtils, null ); + } + + private SelectionParameters(List qualifiers, List qualifyingNames, + List conditionQualifiers, List conditionQualifyingNames, + TypeMirror resultType, TypeUtils typeUtils, SourceRHS sourceRHS) { this.qualifiers = qualifiers; this.qualifyingNames = qualifyingNames; + this.conditionQualifiers = conditionQualifiers; + this.conditionQualifyingNames = conditionQualifyingNames; this.resultType = resultType; this.typeUtils = typeUtils; this.sourceRHS = sourceRHS; @@ -74,6 +97,21 @@ public List getQualifyingNames() { return qualifyingNames; } + /** + * @return qualifiers used for further select the appropriate presence check method based on class and name + */ + public List getConditionQualifiers() { + return conditionQualifiers; + } + + /** + * @return qualifyingNames, used in combination with with @Named + * @see #getConditionQualifiers() + */ + public List getConditionQualifyingNames() { + return conditionQualifyingNames; + } + /** * * @return resultType used for further select the appropriate mapping method based on resultType (bean mapping) @@ -119,6 +157,14 @@ public boolean equals(Object obj) { return false; } + if ( !Objects.equals( this.conditionQualifiers, other.conditionQualifiers ) ) { + return false; + } + + if ( !Objects.equals( this.conditionQualifyingNames, other.conditionQualifyingNames ) ) { + return false; + } + if ( !Objects.equals( this.sourceRHS, other.sourceRHS ) ) { return false; } @@ -151,8 +197,22 @@ private boolean equals(TypeMirror mirror1, TypeMirror mirror2) { } } + public SelectionParameters withSourceRHS(SourceRHS sourceRHS) { + return new SelectionParameters( + this.qualifiers, + this.qualifyingNames, + this.conditionQualifiers, + this.conditionQualifyingNames, + null, + this.typeUtils, + sourceRHS + ); + } + public static SelectionParameters forSourceRHS(SourceRHS sourceRHS) { return new SelectionParameters( + Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), null, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 83898d4b38..86f19618d4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -14,6 +14,8 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; + +import org.mapstruct.ap.internal.gem.ConditionGem; import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.Accessibility; @@ -47,6 +49,7 @@ public class SourceMethod implements Method { private final Parameter mappingTargetParameter; private final Parameter targetTypeParameter; private final boolean isObjectFactory; + private final boolean isPresenceCheck; private final Type returnType; private final Accessibility accessibility; private final List exceptionTypes; @@ -230,6 +233,7 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters ); this.hasObjectFactoryAnnotation = ObjectFactoryGem.instanceOn( executable ) != null; this.isObjectFactory = determineIfIsObjectFactory(); + this.isPresenceCheck = determineIfIsPresenceCheck(); this.typeUtils = builder.typeUtils; this.typeFactory = builder.typeFactory; @@ -247,6 +251,19 @@ private boolean determineIfIsObjectFactory() { && ( hasObjectFactoryAnnotation || hasNoSourceParameters ); } + private boolean determineIfIsPresenceCheck() { + if ( returnType.isPrimitive() ) { + if ( !returnType.getName().equals( "boolean" ) ) { + return false; + } + } + else if ( !returnType.getFullyQualifiedName().equals( Boolean.class.getCanonicalName() ) ) { + return false; + } + + return ConditionGem.instanceOn( executable ) != null; + } + @Override public Type getDeclaringMapper() { return declaringMapper; @@ -519,6 +536,11 @@ public boolean isLifecycleCallbackMethod() { return Executables.isLifecycleCallbackMethod( getExecutable() ); } + @Override + public boolean isPresenceCheck() { + return isPresenceCheck; + } + public boolean isAfterMappingMethod() { return Executables.isAfterMappingMethod( getExecutable() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java index 0e24774025..ed1c72ab2a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java @@ -36,7 +36,8 @@ public List> getMatchingMethods(Method mapp Type returnType, SelectionCriteria criteria) { - if ( criteria.isLifecycleCallbackRequired() || criteria.isObjectFactoryRequired() ) { + if ( criteria.isLifecycleCallbackRequired() || criteria.isObjectFactoryRequired() + || criteria.isPresenceCheckRequired() ) { return methods; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java index 7e7925ca0e..37502ec56d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java @@ -30,7 +30,9 @@ public List> getMatchingMethods(Method mapp List> result = new ArrayList<>( methods.size() ); for ( SelectedMethod method : methods ) { if ( method.getMethod().isObjectFactory() == criteria.isObjectFactoryRequired() - && method.getMethod().isLifecycleCallbackMethod() == criteria.isLifecycleCallbackRequired() ) { + && method.getMethod().isLifecycleCallbackMethod() == criteria.isLifecycleCallbackRequired() + && method.getMethod().isPresenceCheck() == criteria.isPresenceCheckRequired() + ) { result.add( method ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java index b88de4f225..de429174da 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java @@ -35,6 +35,7 @@ public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils, TypeFacto new XmlElementDeclSelector( typeUtils ), new InheritanceSelector(), new CreateOrUpdateSelector(), + new SourceRhsSelector(), new FactoryParameterSelector() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java index 23c67b230e..b70e94b6d1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java @@ -26,20 +26,23 @@ public class SelectionCriteria { private final String targetPropertyName; private final TypeMirror qualifyingResultType; private final SourceRHS sourceRHS; - private boolean preferUpdateMapping; - private final boolean objectFactoryRequired; - private final boolean lifecycleCallbackRequired; + private Type type; private final boolean allowDirect; private final boolean allowConversion; private final boolean allowMappingMethod; private final boolean allow2Steps; public SelectionCriteria(SelectionParameters selectionParameters, MappingControl mappingControl, - String targetPropertyName, boolean preferUpdateMapping, boolean objectFactoryRequired, - boolean lifecycleCallbackRequired) { + String targetPropertyName, Type type) { if ( selectionParameters != null ) { - qualifiers.addAll( selectionParameters.getQualifiers() ); - qualifiedByNames.addAll( selectionParameters.getQualifyingNames() ); + if ( type == Type.PRESENCE_CHECK ) { + qualifiers.addAll( selectionParameters.getConditionQualifiers() ); + qualifiedByNames.addAll( selectionParameters.getConditionQualifyingNames() ); + } + else { + qualifiers.addAll( selectionParameters.getQualifiers() ); + qualifiedByNames.addAll( selectionParameters.getQualifyingNames() ); + } qualifyingResultType = selectionParameters.getResultType(); sourceRHS = selectionParameters.getSourceRHS(); } @@ -60,23 +63,28 @@ public SelectionCriteria(SelectionParameters selectionParameters, MappingControl this.allow2Steps = true; } this.targetPropertyName = targetPropertyName; - this.preferUpdateMapping = preferUpdateMapping; - this.objectFactoryRequired = objectFactoryRequired; - this.lifecycleCallbackRequired = lifecycleCallbackRequired; + this.type = type; } /** * @return true if factory methods should be selected, false otherwise. */ public boolean isObjectFactoryRequired() { - return objectFactoryRequired; + return type == Type.OBJECT_FACTORY; } /** * @return true if lifecycle callback methods should be selected, false otherwise. */ public boolean isLifecycleCallbackRequired() { - return lifecycleCallbackRequired; + return type == Type.LIFECYCLE_CALLBACK; + } + + /** + * @return {@code true} if presence check methods should be selected, {@code false} otherwise + */ + public boolean isPresenceCheckRequired() { + return type == Type.PRESENCE_CHECK; } public List getQualifiers() { @@ -96,7 +104,7 @@ public TypeMirror getQualifyingResultType() { } public boolean isPreferUpdateMapping() { - return preferUpdateMapping; + return type == Type.PREFER_UPDATE_MAPPING; } public SourceRHS getSourceRHS() { @@ -104,7 +112,7 @@ public SourceRHS getSourceRHS() { } public void setPreferUpdateMapping(boolean preferUpdateMapping) { - this.preferUpdateMapping = preferUpdateMapping; + this.type = preferUpdateMapping ? Type.PREFER_UPDATE_MAPPING : null; } public boolean hasQualfiers() { @@ -135,17 +143,26 @@ public static SelectionCriteria forMappingMethods(SelectionParameters selectionP selectionParameters, mappingControl, targetPropertyName, - preferUpdateMapping, - false, - false + preferUpdateMapping ? Type.PREFER_UPDATE_MAPPING : null ); } public static SelectionCriteria forFactoryMethods(SelectionParameters selectionParameters) { - return new SelectionCriteria( selectionParameters, null, null, false, true, false ); + return new SelectionCriteria( selectionParameters, null, null, Type.OBJECT_FACTORY ); } public static SelectionCriteria forLifecycleMethods(SelectionParameters selectionParameters) { - return new SelectionCriteria( selectionParameters, null, null, false, false, true ); + return new SelectionCriteria( selectionParameters, null, null, Type.LIFECYCLE_CALLBACK ); + } + + public static SelectionCriteria forPresenceCheckMethods(SelectionParameters selectionParameters) { + return new SelectionCriteria( selectionParameters, null, null, Type.PRESENCE_CHECK ); + } + + public enum Type { + PREFER_UPDATE_MAPPING, + OBJECT_FACTORY, + LIFECYCLE_CALLBACK, + PRESENCE_CHECK, } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java new file mode 100644 index 0000000000..930ce2d403 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java @@ -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.internal.model.source.selector; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.ap.internal.model.common.ParameterBinding; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.source.Method; + +/** + * Selector that tries to resolve an ambiquity between methods that contain source parameters and + * {@link org.mapstruct.ap.internal.model.common.SourceRHS SourceRHS} type parameters. + * @author Filip Hrisafov + */ +public class SourceRhsSelector implements MethodSelector { + + @Override + public List> getMatchingMethods(Method mappingMethod, + List> candidates, + List sourceTypes, Type mappingTargetType, + Type returnType, SelectionCriteria criteria) { + if ( candidates.size() < 2 || criteria.getSourceRHS() == null ) { + return candidates; + } + + List> sourceRHSFavoringCandidates = new ArrayList<>(); + + for ( SelectedMethod candidate : candidates ) { + for ( ParameterBinding parameterBinding : candidate.getParameterBindings() ) { + if ( parameterBinding.getSourceRHS() != null ) { + sourceRHSFavoringCandidates.add( candidate ); + break; + } + } + + } + + if ( !sourceRHSFavoringCandidates.isEmpty() ) { + return sourceRHSFavoringCandidates; + } + + return candidates; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java index 3d0bd4b9b4..74e518c84d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java @@ -89,7 +89,7 @@ private List getAvailableParameterBindingsFromMethod(Method me List availableParams = new ArrayList<>( method.getParameters().size() + 3 ); if ( sourceRHS != null ) { - availableParams.addAll( ParameterBinding.fromParameters( method.getContextParameters() ) ); + availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) ); } else { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index b959a15dc0..c6e510d315 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -23,6 +23,7 @@ import javax.lang.model.type.TypeKind; import org.mapstruct.ap.internal.gem.BeanMappingGem; +import org.mapstruct.ap.internal.gem.ConditionGem; import org.mapstruct.ap.internal.gem.IterableMappingGem; import org.mapstruct.ap.internal.gem.MapMappingGem; import org.mapstruct.ap.internal.gem.MappingGem; @@ -225,7 +226,8 @@ private SourceMethod getMethod(TypeElement usedMapper, } // otherwise add reference to existing mapper method else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( method, parameters, returnType ) - || isValidLifecycleCallbackMethod( method ) ) { + || isValidLifecycleCallbackMethod( method ) + || isValidPresenceCheckMethod( method, returnType ) ) { return getReferencedMethod( usedMapper, methodType, method, mapperToImplement, parameters ); } else { @@ -333,7 +335,8 @@ private ParameterProvidedMethods retrieveContextProvidedMethods( List contextProvidedMethods = new ArrayList<>( contextParamMethods.size() ); for ( SourceMethod sourceMethod : contextParamMethods ) { - if ( sourceMethod.isLifecycleCallbackMethod() || sourceMethod.isObjectFactory() ) { + if ( sourceMethod.isLifecycleCallbackMethod() || sourceMethod.isObjectFactory() + || sourceMethod.isPresenceCheck() ) { contextProvidedMethods.add( sourceMethod ); } } @@ -389,10 +392,22 @@ private boolean hasFactoryAnnotation(ExecutableElement method) { return ObjectFactoryGem.instanceOn( method ) != null; } + private boolean isValidPresenceCheckMethod(ExecutableElement method, Type returnType) { + return isBoolean( returnType ) && hasConditionAnnotation( method ); + } + + private boolean hasConditionAnnotation(ExecutableElement method) { + return ConditionGem.instanceOn( method ) != null; + } + private boolean isVoid(Type returnType) { return returnType.getTypeMirror().getKind() == TypeKind.VOID; } + private boolean isBoolean(Type returnType) { + return Boolean.class.getCanonicalName().equals( returnType.getBoxedEquivalent().getFullyQualifiedName() ); + } + private boolean isValidReferencedOrFactoryMethod(int sourceParamCount, int targetParamCount, List parameters) { int validSourceParameters = 0; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index e30407ea20..50aeb701d8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -66,6 +66,7 @@ public enum Message { PROPERTYMAPPING_EXPRESSION_AND_QUALIFIER_BOTH_DEFINED("Expression and a qualifier both defined in @Mapping, either define an expression or a qualifier."), PROPERTYMAPPING_INVALID_EXPRESSION( "Value for expression must be given in the form \"java()\"." ), PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION( "Value for default expression must be given in the form \"java()\"." ), + PROPERTYMAPPING_INVALID_CONDITION_EXPRESSION( "Value for condition expression must be given in the form \"java()\"." ), PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no source parameter named \"%s\". Method source parameters are: \"%s\"." ), PROPERTYMAPPING_NO_PROPERTY_IN_PARAMETER( "The type of parameter \"%s\" has no property named \"%s\"." ), PROPERTYMAPPING_INVALID_PROPERTY_NAME( "No property named \"%s\" exists in source parameter(s). Did you mean \"%s\"?" ), @@ -120,6 +121,7 @@ public enum Message { GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ), GENERAL_AMBIGUOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), GENERAL_AMBIGUOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), + GENERAL_AMBIGUOUS_PRESENCE_CHECK_METHOD( "Ambiguous presence check methods found for checking %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), GENERAL_AMBIGUOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ), GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ), GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ), diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl new file mode 100644 index 0000000000..44ec296736 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl @@ -0,0 +1,9 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MethodReferencePresenceCheck" --> +<@includeModel object=methodReference/> \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.ftl new file mode 100644 index 0000000000..447fa375cf --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.ftl @@ -0,0 +1,9 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.JavaExpressionPresenceCheck" --> +${javaExpression} \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/Employee.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/Employee.java new file mode 100644 index 0000000000..ab7cdf4adf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/Employee.java @@ -0,0 +1,36 @@ +/* + * 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; + +public class Employee { + private String name; + private String ssid; + private String nin; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSsid() { + return ssid; + } + + public void setSsid(String ssid) { + this.ssid = ssid; + } + + public String getNin() { + return nin; + } + + public void setNin(String nin) { + this.nin = nin; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/EmployeeDto.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/EmployeeDto.java new file mode 100644 index 0000000000..f8b9b319fa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/EmployeeDto.java @@ -0,0 +1,36 @@ +/* + * 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; + +public class EmployeeDto { + private String name; + private String country; + private String uniqueIdNumber; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getUniqueIdNumber() { + return uniqueIdNumber; + } + + public void setUniqueIdNumber(String uniqueIdNumber) { + this.uniqueIdNumber = uniqueIdNumber; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/BasicEmployee.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/BasicEmployee.java new file mode 100644 index 0000000000..9fcaa6e761 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/BasicEmployee.java @@ -0,0 +1,22 @@ +/* + * 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.basic; + +/** + * @author Filip Hrisafov + */ +public class BasicEmployee { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/BasicEmployeeDto.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/BasicEmployeeDto.java new file mode 100644 index 0000000000..24a944137c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/BasicEmployeeDto.java @@ -0,0 +1,32 @@ +/* + * 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.basic; + +/** + * @author Filip Hrisafov + */ +public class BasicEmployeeDto { + + private final String name; + private final String strategy; + + public BasicEmployeeDto(String name) { + this( name, "default" ); + } + + public BasicEmployeeDto(String name, String strategy) { + this.name = name; + this.strategy = strategy; + } + + public String getName() { + return name; + } + + public String getStrategy() { + return strategy; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java new file mode 100644 index 0000000000..300f97bce4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java @@ -0,0 +1,229 @@ +/* + * 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.basic; + +import java.util.Arrays; +import java.util.Collections; + +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.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2051") +@WithClasses({ + BasicEmployee.class, + BasicEmployeeDto.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class ConditionalMappingTest { + + @Rule + public final GeneratedSource generatedSource = new GeneratedSource(); + + @Test + @WithClasses({ + ConditionalMethodInMapper.class + }) + public void conditionalMethodInMapper() { + generatedSource.addComparisonToFixtureFor( ConditionalMethodInMapper.class ); + ConditionalMethodInMapper mapper = ConditionalMethodInMapper.INSTANCE; + + BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ) ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + + employee = mapper.map( new BasicEmployeeDto( "" ) ); + assertThat( employee.getName() ).isNull(); + + employee = mapper.map( new BasicEmployeeDto( " " ) ); + assertThat( employee.getName() ).isNull(); + } + + @Test + @WithClasses({ + ConditionalMethodAndBeanPresenceCheckMapper.class + }) + public void conditionalMethodAndBeanPresenceCheckMapper() { + ConditionalMethodAndBeanPresenceCheckMapper mapper = ConditionalMethodAndBeanPresenceCheckMapper.INSTANCE; + + BasicEmployee employee = mapper.map( new ConditionalMethodAndBeanPresenceCheckMapper.EmployeeDto( "Tester" ) ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + + employee = mapper.map( new ConditionalMethodAndBeanPresenceCheckMapper.EmployeeDto( "" ) ); + assertThat( employee.getName() ).isNull(); + + employee = mapper.map( new ConditionalMethodAndBeanPresenceCheckMapper.EmployeeDto( " " ) ); + assertThat( employee.getName() ).isNull(); + } + + @Test + @WithClasses({ + ConditionalMethodInUsesMapper.class + }) + public void conditionalMethodInUsesMapper() { + ConditionalMethodInUsesMapper mapper = ConditionalMethodInUsesMapper.INSTANCE; + + BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ) ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + + employee = mapper.map( new BasicEmployeeDto( "" ) ); + assertThat( employee.getName() ).isNull(); + + employee = mapper.map( new BasicEmployeeDto( " " ) ); + assertThat( employee.getName() ).isNull(); + } + + @Test + @WithClasses({ + ConditionalMethodInUsesStaticMapper.class + }) + public void conditionalMethodInUsesStaticMapper() { + ConditionalMethodInUsesStaticMapper mapper = ConditionalMethodInUsesStaticMapper.INSTANCE; + + BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ) ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + + employee = mapper.map( new BasicEmployeeDto( "" ) ); + assertThat( employee.getName() ).isNull(); + + employee = mapper.map( new BasicEmployeeDto( " " ) ); + assertThat( employee.getName() ).isNull(); + } + + @Test + @WithClasses({ + ConditionalMethodInContextMapper.class + }) + public void conditionalMethodInUsesContextMapper() { + ConditionalMethodInContextMapper mapper = ConditionalMethodInContextMapper.INSTANCE; + + ConditionalMethodInContextMapper.PresenceUtils utils = new ConditionalMethodInContextMapper.PresenceUtils(); + BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ), utils ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + + employee = mapper.map( new BasicEmployeeDto( "" ), utils ); + assertThat( employee.getName() ).isNull(); + + employee = mapper.map( new BasicEmployeeDto( " " ), utils ); + assertThat( employee.getName() ).isNull(); + } + + @Test + @WithClasses({ + ConditionalMethodWithSourceParameterMapper.class + }) + public void conditionalMethodWithSourceParameter() { + ConditionalMethodWithSourceParameterMapper mapper = ConditionalMethodWithSourceParameterMapper.INSTANCE; + + BasicEmployee employee = mapper.map( new BasicEmployeeDto( "Tester" ) ); + assertThat( employee.getName() ).isNull(); + + employee = mapper.map( new BasicEmployeeDto( "Tester", "map" ) ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + } + + @Test + @WithClasses({ + ConditionalMethodWithSourceParameterAndValueMapper.class + }) + public void conditionalMethodWithSourceParameterAndValue() { + generatedSource.addComparisonToFixtureFor( ConditionalMethodWithSourceParameterAndValueMapper.class ); + ConditionalMethodWithSourceParameterAndValueMapper mapper = + ConditionalMethodWithSourceParameterAndValueMapper.INSTANCE; + + BasicEmployee employee = mapper.map( new BasicEmployeeDto( " ", "empty" ) ); + assertThat( employee.getName() ).isEqualTo( " " ); + + employee = mapper.map( new BasicEmployeeDto( " ", "blank" ) ); + assertThat( employee.getName() ).isNull(); + + employee = mapper.map( new BasicEmployeeDto( "Tester", "blank" ) ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + } + + @Test + @WithClasses({ + ErroneousAmbiguousConditionalMethodMapper.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousAmbiguousConditionalMethodMapper.class, + line = 17, + message = "Ambiguous presence check methods found for checking String: " + + "boolean isNotBlank(String value), " + + "boolean isNotEmpty(String value). " + + "See https://mapstruct.org/faq/#ambiguous for more info." + ) + } + ) + public void ambiguousConditionalMethod() { + + } + + @Test + @WithClasses({ + ConditionalMethodForCollectionMapper.class + }) + public void conditionalMethodForCollection() { + ConditionalMethodForCollectionMapper mapper = ConditionalMethodForCollectionMapper.INSTANCE; + + ConditionalMethodForCollectionMapper.Author author = new ConditionalMethodForCollectionMapper.Author(); + ConditionalMethodForCollectionMapper.AuthorDto dto = mapper.map( author ); + + assertThat( dto.getBooks() ).isNull(); + + author.setBooks( Collections.emptyList() ); + dto = mapper.map( author ); + + assertThat( dto.getBooks() ).isNull(); + + author.setBooks( Arrays.asList( + new ConditionalMethodForCollectionMapper.Book( "Test" ), + new ConditionalMethodForCollectionMapper.Book( "Test Vol. 2" ) + ) ); + dto = mapper.map( author ); + + assertThat( dto.getBooks() ) + .extracting( ConditionalMethodForCollectionMapper.BookDto::getName ) + .containsExactly( "Test", "Test Vol. 2" ); + } + + @Test + @WithClasses({ + OptionalLikeConditionalMapper.class + }) + @IssueKey("2084") + public void optionalLikeConditional() { + OptionalLikeConditionalMapper mapper = OptionalLikeConditionalMapper.INSTANCE; + + OptionalLikeConditionalMapper.Target target = mapper.map( new OptionalLikeConditionalMapper.Source( + OptionalLikeConditionalMapper.Nullable.ofNullable( "test" ) ) ); + + assertThat( target.getValue() ).isEqualTo( "test" ); + + target = mapper.map( + new OptionalLikeConditionalMapper.Source( OptionalLikeConditionalMapper.Nullable.undefined() ) + ); + + assertThat( target.getValue() ).isEqualTo( "initial" ); + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodAndBeanPresenceCheckMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodAndBeanPresenceCheckMapper.java new file mode 100644 index 0000000000..ad37d5832b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodAndBeanPresenceCheckMapper.java @@ -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.conditional.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConditionalMethodAndBeanPresenceCheckMapper { + + ConditionalMethodAndBeanPresenceCheckMapper INSTANCE = Mappers.getMapper( + ConditionalMethodAndBeanPresenceCheckMapper.class ); + + BasicEmployee map(EmployeeDto employee); + + @Condition + default boolean isNotBlank(String value) { + return value != null && !value.trim().isEmpty(); + } + + class EmployeeDto { + + private final String name; + + public EmployeeDto(String name) { + this.name = name; + } + + public boolean hasName() { + return false; + } + + public String getName() { + return name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForCollectionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForCollectionMapper.java new file mode 100644 index 0000000000..a27a0aaf0a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForCollectionMapper.java @@ -0,0 +1,81 @@ +/* + * 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.basic; + +import java.util.Collection; +import java.util.List; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConditionalMethodForCollectionMapper { + + ConditionalMethodForCollectionMapper INSTANCE = Mappers.getMapper( ConditionalMethodForCollectionMapper.class ); + + AuthorDto map(Author author); + + @Condition + default boolean isNotEmpty(Collection collection) { + return collection != null && !collection.isEmpty(); + } + + class Author { + private List books; + + public List getBooks() { + return books; + } + + public boolean hasBooks() { + return false; + } + + public void setBooks(List books) { + this.books = books; + } + } + + class Book { + private final String name; + + public Book(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + class AuthorDto { + private List books; + + public List getBooks() { + return books; + } + + public void setBooks(List books) { + this.books = books; + } + } + + class BookDto { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInContextMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInContextMapper.java new file mode 100644 index 0000000000..26ad844146 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInContextMapper.java @@ -0,0 +1,29 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConditionalMethodInContextMapper { + + ConditionalMethodInContextMapper INSTANCE = Mappers.getMapper( ConditionalMethodInContextMapper.class ); + + BasicEmployee map(BasicEmployeeDto employee, @Context PresenceUtils utils); + + class PresenceUtils { + @Condition + public boolean isNotBlank(String value) { + return value != null && !value.trim().isEmpty(); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInMapper.java new file mode 100644 index 0000000000..256419f687 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInMapper.java @@ -0,0 +1,26 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConditionalMethodInMapper { + + ConditionalMethodInMapper INSTANCE = Mappers.getMapper( ConditionalMethodInMapper.class ); + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition + default boolean isNotBlank(String value) { + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInUsesMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInUsesMapper.java new file mode 100644 index 0000000000..fb30d19c80 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInUsesMapper.java @@ -0,0 +1,30 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = ConditionalMethodInUsesMapper.PresenceUtils.class) +public interface ConditionalMethodInUsesMapper { + + ConditionalMethodInUsesMapper INSTANCE = Mappers.getMapper( ConditionalMethodInUsesMapper.class ); + + BasicEmployee map(BasicEmployeeDto employee); + + class PresenceUtils { + + @Condition + public boolean isNotBlank(String value) { + return value != null && !value.trim().isEmpty(); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInUsesStaticMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInUsesStaticMapper.java new file mode 100644 index 0000000000..5e77d6f8fb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInUsesStaticMapper.java @@ -0,0 +1,30 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = ConditionalMethodInUsesStaticMapper.PresenceUtils.class) +public interface ConditionalMethodInUsesStaticMapper { + + ConditionalMethodInUsesStaticMapper INSTANCE = Mappers.getMapper( ConditionalMethodInUsesStaticMapper.class ); + + BasicEmployee map(BasicEmployeeDto employee); + + interface PresenceUtils { + + @Condition + static boolean isNotBlank(String value) { + return value != null && !value.trim().isEmpty(); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterAndValueMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterAndValueMapper.java new file mode 100644 index 0000000000..e39c5c88e8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterAndValueMapper.java @@ -0,0 +1,38 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConditionalMethodWithSourceParameterAndValueMapper { + + ConditionalMethodWithSourceParameterAndValueMapper INSTANCE = Mappers.getMapper( + ConditionalMethodWithSourceParameterAndValueMapper.class ); + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition + default boolean isPresent(BasicEmployeeDto source, String value) { + if ( value == null ) { + return false; + } + switch ( source.getStrategy() ) { + case "blank": + return !value.trim().isEmpty(); + case "empty": + return !value.isEmpty(); + default: + return true; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterMapper.java new file mode 100644 index 0000000000..9a499a6039 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterMapper.java @@ -0,0 +1,28 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConditionalMethodWithSourceParameterMapper { + + ConditionalMethodWithSourceParameterMapper INSTANCE = + Mappers.getMapper( ConditionalMethodWithSourceParameterMapper.class ); + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition + default boolean shouldMap(BasicEmployeeDto source) { + return "map".equals( source.getStrategy() ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousAmbiguousConditionalMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousAmbiguousConditionalMethodMapper.java new file mode 100644 index 0000000000..49e354c913 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousAmbiguousConditionalMethodMapper.java @@ -0,0 +1,28 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousAmbiguousConditionalMethodMapper { + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition + default boolean isNotBlank(String value) { + return value != null && !value.trim().isEmpty(); + } + + @Condition + default boolean isNotEmpty(String value) { + return value != null && value.isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/OptionalLikeConditionalMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/OptionalLikeConditionalMapper.java new file mode 100644 index 0000000000..4d097d7b5f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/OptionalLikeConditionalMapper.java @@ -0,0 +1,77 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface OptionalLikeConditionalMapper { + + OptionalLikeConditionalMapper INSTANCE = Mappers.getMapper( OptionalLikeConditionalMapper.class ); + + Target map(Source source); + + default T map(Nullable nullable) { + return nullable.value; + } + + @Condition + default boolean isPresent(Nullable nullable) { + return nullable.isPresent(); + } + + class Nullable { + + private final T value; + private final boolean present; + + private Nullable(T value, boolean present) { + this.value = value; + this.present = present; + } + + public boolean isPresent() { + return present; + } + + public static Nullable undefined() { + return new Nullable<>( null, false ); + } + + public static Nullable ofNullable(T value) { + return new Nullable<>( value, true ); + } + } + + class Source { + protected final Nullable value; + + public Source(Nullable value) { + this.value = value; + } + + public Nullable getValue() { + return value; + } + } + + class Target { + protected String value = "initial"; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java new file mode 100644 index 0000000000..cf38d85dd1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java @@ -0,0 +1,97 @@ +/* + * 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.expression; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.conditional.Employee; +import org.mapstruct.ap.test.conditional.EmployeeDto; +import org.mapstruct.ap.test.conditional.basic.BasicEmployee; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2051") +@WithClasses({ + Employee.class, + EmployeeDto.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class ConditionalExpressionTest { + + @Test + @WithClasses({ + ConditionalMethodsInUtilClassMapper.class + }) + public void conditionalExpressionInStaticClassMethod() { + ConditionalMethodsInUtilClassMapper mapper = ConditionalMethodsInUtilClassMapper.INSTANCE; + + EmployeeDto dto = new EmployeeDto(); + dto.setName( "Tester" ); + dto.setUniqueIdNumber( "SSID-001" ); + dto.setCountry( null ); + + Employee employee = mapper.map( dto ); + assertThat( employee.getNin() ).isNull(); + assertThat( employee.getSsid() ).isNull(); + + dto.setCountry( "UK" ); + employee = mapper.map( dto ); + assertThat( employee.getNin() ).isEqualTo( "SSID-001" ); + assertThat( employee.getSsid() ).isNull(); + + dto.setCountry( "US" ); + employee = mapper.map( dto ); + assertThat( employee.getNin() ).isNull(); + assertThat( employee.getSsid() ).isEqualTo( "SSID-001" ); + + dto.setCountry( "CH" ); + employee = mapper.map( dto ); + assertThat( employee.getNin() ).isNull(); + assertThat( employee.getSsid() ).isNull(); + } + + @Test + @WithClasses({ + SimpleConditionalExpressionMapper.class + }) + public void conditionalSimpleExpression() { + SimpleConditionalExpressionMapper mapper = SimpleConditionalExpressionMapper.INSTANCE; + + SimpleConditionalExpressionMapper.Target target = + mapper.map( new SimpleConditionalExpressionMapper.Source( 50 ) ); + assertThat( target.getValue() ).isEqualTo( 50 ); + + target = mapper.map( new SimpleConditionalExpressionMapper.Source( 101 ) ); + assertThat( target.getValue() ).isEqualTo( 0 ); + } + + @Test + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousConditionExpressionMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 19, + message = "Value for condition expression must be given in the form \"java()\"." + ) + } + ) + @WithClasses({ + BasicEmployee.class, + ErroneousConditionExpressionMapper.class + } ) + public void invalidJavaConditionExpression() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalMethodsInUtilClassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalMethodsInUtilClassMapper.java new file mode 100644 index 0000000000..3c49de5bf0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalMethodsInUtilClassMapper.java @@ -0,0 +1,38 @@ +/* + * 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.expression; + +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 Filip Hrisafov + */ +@Mapper(imports = ConditionalMethodsInUtilClassMapper.StaticUtil.class) +public interface ConditionalMethodsInUtilClassMapper { + + ConditionalMethodsInUtilClassMapper INSTANCE = Mappers.getMapper( ConditionalMethodsInUtilClassMapper.class ); + + @Mapping(target = "ssid", source = "uniqueIdNumber", + conditionExpression = "java(StaticUtil.isAmericanCitizen( employee ))") + @Mapping(target = "nin", source = "uniqueIdNumber", + conditionExpression = "java(StaticUtil.isBritishCitizen( employee ))") + Employee map(EmployeeDto employee); + + interface StaticUtil { + + static boolean isAmericanCitizen(EmployeeDto employeeDto) { + return "US".equals( employeeDto.getCountry() ); + } + + static boolean isBritishCitizen(EmployeeDto employeeDto) { + return "UK".equals( employeeDto.getCountry() ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ErroneousConditionExpressionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ErroneousConditionExpressionMapper.java new file mode 100644 index 0000000000..c74c552531 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ErroneousConditionExpressionMapper.java @@ -0,0 +1,21 @@ +/* + * 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.expression; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.conditional.EmployeeDto; +import org.mapstruct.ap.test.conditional.basic.BasicEmployee; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousConditionExpressionMapper { + + @Mapping(target = "name", conditionExpression = "!employee.getName().isEmpty()") + BasicEmployee map(EmployeeDto employee); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/SimpleConditionalExpressionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/SimpleConditionalExpressionMapper.java new file mode 100644 index 0000000000..861fa0c1e3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/SimpleConditionalExpressionMapper.java @@ -0,0 +1,46 @@ +/* + * 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.expression; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SimpleConditionalExpressionMapper { + + SimpleConditionalExpressionMapper INSTANCE = Mappers.getMapper( SimpleConditionalExpressionMapper.class ); + + @Mapping(target = "value", conditionExpression = "java(source.getValue() < 100)") + Target map(Source source); + + class Source { + private final int value; + + public Source(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + class Target { + private int value; + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithClassQualifiersMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithClassQualifiersMapper.java new file mode 100644 index 0000000000..7cbef82a5a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithClassQualifiersMapper.java @@ -0,0 +1,63 @@ +/* + * 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.qualifier; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.Qualifier; +import org.mapstruct.ap.test.conditional.Employee; +import org.mapstruct.ap.test.conditional.EmployeeDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = ConditionalMethodWithClassQualifiersMapper.StaticUtil.class) +public interface ConditionalMethodWithClassQualifiersMapper { + + ConditionalMethodWithClassQualifiersMapper INSTANCE = + Mappers.getMapper( ConditionalMethodWithClassQualifiersMapper.class ); + + @Mapping(target = "ssid", source = "uniqueIdNumber", + conditionQualifiedBy = UtilConditions.class, conditionQualifiedByName = "american") + @Mapping(target = "nin", source = "uniqueIdNumber", + conditionQualifiedBy = UtilConditions.class, conditionQualifiedByName = "british") + Employee map(EmployeeDto employee); + + @Condition + default boolean isNotBlank(String value) { + return value != null && !value.trim().isEmpty(); + } + + @UtilConditions + interface StaticUtil { + + @Condition + @Named("american") + static boolean isAmericanCitizen(EmployeeDto employerDto) { + return "US".equals( employerDto.getCountry() ); + } + + @Condition + @Named("british") + static boolean isBritishCitizen(EmployeeDto employeeDto) { + return "UK".equals( employeeDto.getCountry() ); + } + } + + @Qualifier + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.CLASS) + @interface UtilConditions { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceParameterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceParameterMapper.java new file mode 100644 index 0000000000..c04269d574 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceParameterMapper.java @@ -0,0 +1,48 @@ +/* + * 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.qualifier; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.ap.test.conditional.Employee; +import org.mapstruct.ap.test.conditional.EmployeeDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(uses = ConditionalMethodWithSourceParameterMapper.StaticUtil.class) +public interface ConditionalMethodWithSourceParameterMapper { + + ConditionalMethodWithSourceParameterMapper INSTANCE = + Mappers.getMapper( ConditionalMethodWithSourceParameterMapper.class ); + + @Mapping(target = "ssid", source = "uniqueIdNumber", conditionQualifiedByName = "isAmericanCitizen") + @Mapping(target = "nin", source = "uniqueIdNumber", conditionQualifiedByName = "isBritishCitizen") + Employee map(EmployeeDto employee); + + @Condition + default boolean isNotBlank(String value) { + return value != null && !value.trim().isEmpty(); + } + + @Condition + @Named("isAmericanCitizen") + default boolean isAmericanCitizen(EmployeeDto employerDto) { + return "US".equals( employerDto.getCountry() ); + } + + interface StaticUtil { + + @Condition + @Named("isBritishCitizen") + static boolean isBritishCitizen(EmployeeDto employeeDto) { + return "UK".equals( employeeDto.getCountry() ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java new file mode 100644 index 0000000000..3dc6d7c8aa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java @@ -0,0 +1,92 @@ +/* + * 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.qualifier; + +import org.junit.Test; +import org.junit.runner.RunWith; +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.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2051") +@WithClasses({ + Employee.class, + EmployeeDto.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class ConditionalQualifierTest { + + @Test + @WithClasses({ + ConditionalMethodWithSourceParameterMapper.class + }) + public void conditionalMethodWithSourceParameter() { + ConditionalMethodWithSourceParameterMapper mapper = ConditionalMethodWithSourceParameterMapper.INSTANCE; + + EmployeeDto dto = new EmployeeDto(); + dto.setName( "Tester" ); + dto.setUniqueIdNumber( "SSID-001" ); + dto.setCountry( null ); + + Employee employee = mapper.map( dto ); + assertThat( employee.getNin() ).isNull(); + assertThat( employee.getSsid() ).isNull(); + + dto.setCountry( "UK" ); + employee = mapper.map( dto ); + assertThat( employee.getNin() ).isEqualTo( "SSID-001" ); + assertThat( employee.getSsid() ).isNull(); + + dto.setCountry( "US" ); + employee = mapper.map( dto ); + assertThat( employee.getNin() ).isNull(); + assertThat( employee.getSsid() ).isEqualTo( "SSID-001" ); + + dto.setCountry( "CH" ); + employee = mapper.map( dto ); + assertThat( employee.getNin() ).isNull(); + assertThat( employee.getSsid() ).isNull(); + } + + @Test + @WithClasses({ + ConditionalMethodWithClassQualifiersMapper.class + }) + public void conditionalClassQualifiers() { + ConditionalMethodWithClassQualifiersMapper mapper = ConditionalMethodWithClassQualifiersMapper.INSTANCE; + + EmployeeDto dto = new EmployeeDto(); + dto.setName( "Tester" ); + dto.setUniqueIdNumber( "SSID-001" ); + dto.setCountry( null ); + + Employee employee = mapper.map( dto ); + assertThat( employee.getNin() ).isNull(); + assertThat( employee.getSsid() ).isNull(); + + dto.setCountry( "UK" ); + employee = mapper.map( dto ); + assertThat( employee.getNin() ).isEqualTo( "SSID-001" ); + assertThat( employee.getSsid() ).isNull(); + + dto.setCountry( "US" ); + employee = mapper.map( dto ); + assertThat( employee.getNin() ).isNull(); + assertThat( employee.getSsid() ).isEqualTo( "SSID-001" ); + + dto.setCountry( "CH" ); + employee = mapper.map( dto ); + assertThat( employee.getNin() ).isNull(); + assertThat( employee.getSsid() ).isNull(); + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInMapperImpl.java new file mode 100644 index 0000000000..8f07ed89ba --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conditional/basic/ConditionalMethodInMapperImpl.java @@ -0,0 +1,31 @@ +/* + * 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.basic; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-04-19T21:10:40+0200", + comments = "version: , compiler: javac, environment: Java 11.0.9.1 (AdoptOpenJDK)" +) +public class ConditionalMethodInMapperImpl implements ConditionalMethodInMapper { + + @Override + public BasicEmployee map(BasicEmployeeDto employee) { + if ( employee == null ) { + return null; + } + + BasicEmployee basicEmployee = new BasicEmployee(); + + if ( isNotBlank( employee.getName() ) ) { + basicEmployee.setName( employee.getName() ); + } + + return basicEmployee; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterAndValueMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterAndValueMapperImpl.java new file mode 100644 index 0000000000..ee629fb72a --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithSourceParameterAndValueMapperImpl.java @@ -0,0 +1,31 @@ +/* + * 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.basic; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-04-19T21:10:38+0200", + comments = "version: , compiler: javac, environment: Java 11.0.9.1 (AdoptOpenJDK)" +) +public class ConditionalMethodWithSourceParameterAndValueMapperImpl implements ConditionalMethodWithSourceParameterAndValueMapper { + + @Override + public BasicEmployee map(BasicEmployeeDto employee) { + if ( employee == null ) { + return null; + } + + BasicEmployee basicEmployee = new BasicEmployee(); + + if ( isPresent( employee, employee.getName() ) ) { + basicEmployee.setName( employee.getName() ); + } + + return basicEmployee; + } +} From 5bbd1a78ea67442cf8024fdcef7c9dff4356408d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 14 Feb 2021 21:37:09 +0100 Subject: [PATCH 158/597] Migrate the processor test infrastructure from JUnit 4 to JUnit Jupiter With JUnit Jupiter it is still not possible to set the ClassLoader for loading the test class. However, since 5.8 M1 there is a way to hook into the launcher discovery process and change the Current Thread ContextClassLoader which would load the classes with our customer ClassLoader. Once JUnit Jupiter 201 is resolved we can simplify this. The CompilationCache is stored in the GlobalCache with the CompilationRequest as key. This means that even when methods are not executed in some particular order if they have same WithClasses then they would reuse the cache. --- parent/pom.xml | 2 +- processor/pom.xml | 15 +- .../mapstruct/ap/testutil/ProcessorTest.java | 50 +++++ .../runner/AnnotationProcessorTestRunner.java | 160 -------------- .../testutil/runner/CompilationRequest.java | 9 +- .../ap/testutil/runner/Compiler.java | 21 +- .../CompilerLauncherDiscoveryListener.java | 29 +++ .../CompilerTestEnabledOnJreCondition.java | 39 ++++ ...Statement.java => CompilingExtension.java} | 172 +++++++-------- .../testutil/runner/DisabledOnCompiler.java | 26 --- ...nt.java => EclipseCompilingExtension.java} | 15 +- .../ap/testutil/runner/EnabledOnCompiler.java | 26 --- .../runner/FilteringParentClassLoader.java | 5 + .../ap/testutil/runner/GeneratedSource.java | 60 +++--- .../InnerAnnotationProcessorRunner.java | 145 ------------- .../runner/Jdk11CompilingStatement.java | 37 ---- ...tement.java => JdkCompilingExtension.java} | 30 +-- .../runner/ProcessorTestExtension.java | 38 ++++ .../ProcessorTestInvocationContext.java | 48 +++++ .../testutil/runner/ReplacableTestClass.java | 199 ------------------ .../testutil/runner/WithSingleCompiler.java | 29 --- ...latform.launcher.LauncherDiscoveryListener | 1 + 22 files changed, 373 insertions(+), 783 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/ProcessorTest.java delete mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilerLauncherDiscoveryListener.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilerTestEnabledOnJreCondition.java rename processor/src/test/java/org/mapstruct/ap/testutil/runner/{CompilingStatement.java => CompilingExtension.java} (81%) delete mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/DisabledOnCompiler.java rename processor/src/test/java/org/mapstruct/ap/testutil/runner/{EclipseCompilingStatement.java => EclipseCompilingExtension.java} (94%) delete mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/EnabledOnCompiler.java delete mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/InnerAnnotationProcessorRunner.java delete mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/Jdk11CompilingStatement.java rename processor/src/test/java/org/mapstruct/ap/testutil/runner/{JdkCompilingStatement.java => JdkCompilingExtension.java} (81%) create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/ProcessorTestExtension.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/ProcessorTestInvocationContext.java delete mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/ReplacableTestClass.java delete mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/runner/WithSingleCompiler.java create mode 100644 processor/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener diff --git a/parent/pom.xml b/parent/pom.xml index b6a619a90b..c7ac37a41d 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -28,7 +28,7 @@ 5.3.3 1.6.0 8.36.1 - 5.7.0 + 5.8.0-M1 1 3.17.2 diff --git a/processor/pom.xml b/processor/pom.xml index 2ff5d2d186..5aa6470ff9 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -49,8 +49,13 @@ - junit - junit + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine test @@ -104,6 +109,12 @@ test + + org.junit.platform + junit-platform-launcher + test + + joda-time diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/ProcessorTest.java b/processor/src/test/java/org/mapstruct/ap/testutil/ProcessorTest.java new file mode 100644 index 0000000000..cebacf4587 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/ProcessorTest.java @@ -0,0 +1,50 @@ +/* + * 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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mapstruct.ap.testutil.runner.Compiler; +import org.mapstruct.ap.testutil.runner.ProcessorTestExtension; + +/** + * JUnit Jupiter test template for the MapStruct Processor tests. + *

      + * Test classes are safe to be executed in parallel, but test methods are not safe to be executed in parallel. + *

      + * By default this template would generate tests for the JDK and Eclipse Compiler. + * If only a single compiler is needed then specify the compiler in the value. + *

      + * The classes to be compiled for a given test method must be specified via {@link WithClasses}. In addition the + * following things can be configured optionally : + *

        + *
      • Processor options to be considered during compilation via + * {@link org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption ProcessorOption}.
      • + *
      • The expected compilation outcome and expected diagnostics can be specified via + * {@link org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome ExpectedCompilationOutcome}. + * If no outcome is specified, a successful compilation is assumed.
      • + *
      + * + * @author Filip Hrisafov + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@TestTemplate +@ExtendWith(ProcessorTestExtension.class) +public @interface ProcessorTest { + + Compiler[] value() default { + Compiler.JDK, + Compiler.ECLIPSE + }; +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java deleted file mode 100644 index 2891121afb..0000000000 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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.testutil.runner; - -import java.lang.annotation.Annotation; -import java.util.Arrays; -import java.util.List; - -import org.junit.runner.Description; -import org.junit.runner.Runner; -import org.junit.runner.manipulation.Filter; -import org.junit.runner.manipulation.NoTestsRemainException; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.ParentRunner; -import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; - -/** - * A JUnit4 runner for Annotation Processor tests. - *

      - * Test classes are safe to be executed in parallel, but test methods are not safe to be executed in parallel. - *

      - * The classes to be compiled for a given test method must be specified via {@link WithClasses}. In addition the - * following things can be configured optionally : - *

        - *
      • Processor options to be considered during compilation via {@link ProcessorOption}.
      • - *
      • The expected compilation outcome and expected diagnostics can be specified via {@link ExpectedCompilationOutcome} - * . If no outcome is specified, a successful compilation is assumed.
      • - *
      - * - * @author Gunnar Morling - * @author Andreas Gudian - */ -public class AnnotationProcessorTestRunner extends ParentRunner { - - private static final boolean IS_AT_LEAST_JAVA_9 = isIsAtLeastJava9(); - - private static boolean isIsAtLeastJava9() { - try { - Runtime.class.getMethod( "version" ); - return true; - } - catch ( NoSuchMethodException e ) { - return false; - } - } - - private final List runners; - - /** - * @param klass the test class - * - * @throws Exception see {@link BlockJUnit4ClassRunner#BlockJUnit4ClassRunner(Class)} - */ - public AnnotationProcessorTestRunner(Class klass) throws Exception { - super( klass ); - - runners = createRunners( klass ); - } - - @SuppressWarnings("deprecation") - private List createRunners(Class klass) throws Exception { - WithSingleCompiler singleCompiler = klass.getAnnotation( WithSingleCompiler.class ); - - if (singleCompiler != null) { - return Arrays.asList( new InnerAnnotationProcessorRunner( klass, singleCompiler.value() ) ); - } - else if ( IS_AT_LEAST_JAVA_9 ) { - // Current tycho-compiler-jdt (0.26.0) is not compatible with Java 11 - // Updating to latest version 1.3.0 fails some tests - // Once https://github.com/mapstruct/mapstruct/pull/1587 is resolved we can remove this line - return Arrays.asList( - new InnerAnnotationProcessorRunner( klass, Compiler.JDK11 ), - new InnerAnnotationProcessorRunner( klass, Compiler.ECLIPSE11 ) - ); - } - - return Arrays.asList( - new InnerAnnotationProcessorRunner( klass, Compiler.JDK ), - new InnerAnnotationProcessorRunner( klass, Compiler.ECLIPSE ) - ); - } - - @Override - protected List getChildren() { - return runners; - } - - @Override - protected Description describeChild(Runner child) { - return child.getDescription(); - } - - @Override - protected void runChild(Runner child, RunNotifier notifier) { - child.run( notifier ); - } - - @Override - public void filter(Filter filter) throws NoTestsRemainException { - super.filter( new FilterDecorator( filter ) ); - } - - /** - * Allows to only execute selected methods, even if the executing framework is not aware of parameterized tests - * (e.g. some versions of IntelliJ, Netbeans, Eclipse). - */ - private static final class FilterDecorator extends Filter { - private final Filter delegate; - - private FilterDecorator(Filter delegate) { - this.delegate = delegate; - } - - @Override - public boolean shouldRun(Description description) { - boolean shouldRun = delegate.shouldRun( description ); - if ( !shouldRun ) { - return delegate.shouldRun( withoutParameterizedName( description ) ); - } - - return shouldRun; - } - - @Override - public String describe() { - return delegate.describe(); - } - - private Description withoutParameterizedName(Description description) { - String cleanDisplayName = removeParameter( description.getDisplayName() ); - Description cleanDescription = - Description.createSuiteDescription( - cleanDisplayName, - description.getAnnotations().toArray( new Annotation[description.getAnnotations().size()] ) ); - - for ( Description child : description.getChildren() ) { - cleanDescription.addChild( withoutParameterizedName( child ) ); - } - - return cleanDescription; - } - - private String removeParameter(String name) { - if ( name.startsWith( "[" ) ) { - return name; - } - - // remove "[compiler]" from "method[compiler](class)" - int open = name.indexOf( '[' ); - int close = name.indexOf( ']' ) + 1; - return name.substring( 0, open ) + name.substring( close ); - } - } -} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java index 46a112da2e..3f1599aad0 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java @@ -13,11 +13,14 @@ * Represents a compilation task for a number of sources with given processor options. */ public class CompilationRequest { + private final Compiler compiler; private final Set> sourceClasses; private final Map, Class> services; private final List processorOptions; - CompilationRequest(Set> sourceClasses, Map, Class> services, List processorOptions) { + CompilationRequest(Compiler compiler, Set> sourceClasses, Map, Class> services, + List processorOptions) { + this.compiler = compiler; this.sourceClasses = sourceClasses; this.services = services; this.processorOptions = processorOptions; @@ -27,6 +30,7 @@ public class CompilationRequest { public int hashCode() { final int prime = 31; int result = 1; + result = prime * result + ( ( compiler == null ) ? 0 : compiler.hashCode() ); result = prime * result + ( ( processorOptions == null ) ? 0 : processorOptions.hashCode() ); result = prime * result + ( ( services == null ) ? 0 : services.hashCode() ); result = prime * result + ( ( sourceClasses == null ) ? 0 : sourceClasses.hashCode() ); @@ -46,7 +50,8 @@ public boolean equals(Object obj) { } CompilationRequest other = (CompilationRequest) obj; - return processorOptions.equals( other.processorOptions ) + return compiler.equals( other.compiler ) + && processorOptions.equals( other.processorOptions ) && services.equals( other.services ) && sourceClasses.equals( other.sourceClasses ); } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/Compiler.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/Compiler.java index 4b24bb091b..e38ae28775 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/Compiler.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/Compiler.java @@ -5,10 +5,27 @@ */ package org.mapstruct.ap.testutil.runner; +import org.junit.jupiter.api.condition.JRE; + /** * @author Andreas Gudian - * + * @author Filip Hrisafov */ public enum Compiler { - JDK, JDK11, ECLIPSE, ECLIPSE11; + JDK, + ECLIPSE; + + private final JRE latestSupportedJre; + + Compiler() { + this( JRE.OTHER ); + } + + Compiler(JRE latestSupportedJre) { + this.latestSupportedJre = latestSupportedJre; + } + + public JRE latestSupportedJre() { + return latestSupportedJre; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilerLauncherDiscoveryListener.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilerLauncherDiscoveryListener.java new file mode 100644 index 0000000000..fde6d0d599 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilerLauncherDiscoveryListener.java @@ -0,0 +1,29 @@ +/* + * 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.testutil.runner; + +import org.junit.platform.engine.UniqueId; +import org.junit.platform.launcher.LauncherDiscoveryListener; + +/** + * @author Filip Hrisafov + */ +public class CompilerLauncherDiscoveryListener implements LauncherDiscoveryListener { + @Override + public void engineDiscoveryStarted(UniqueId engineId) { + // Currently JUnit Jupiter does not have an SPI for providing a ClassLoader for loading the class + // However, we can change the current context class loaded when the engine discovery starts. + // This would make sure that JUnit Jupiter uses our ClassLoader to correctly load the mappers + ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); + FilteringParentClassLoader filteringParentClassLoader = new FilteringParentClassLoader( + currentClassLoader, + "org.mapstruct.ap.test." + ); + ModifiableURLClassLoader newClassLoader = new ModifiableURLClassLoader( filteringParentClassLoader ); + newClassLoader.withOriginOf( getClass() ); + Thread.currentThread().setContextClassLoader( newClassLoader ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilerTestEnabledOnJreCondition.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilerTestEnabledOnJreCondition.java new file mode 100644 index 0000000000..29e3280e8b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilerTestEnabledOnJreCondition.java @@ -0,0 +1,39 @@ +/* + * 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.testutil.runner; + +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; + +/** + * Every compiler is registered with it's max supported JRE that it can run on. + * This condition is used to check if the test for a particular compiler can be run with the current JRE. + * + * @author Filip Hrisafov + */ +public class CompilerTestEnabledOnJreCondition implements ExecutionCondition { + + static final ConditionEvaluationResult ENABLED_ON_CURRENT_JRE = + ConditionEvaluationResult.enabled( "Enabled on JRE version: " + System.getProperty( "java.version" ) ); + + static final ConditionEvaluationResult DISABLED_ON_CURRENT_JRE = + ConditionEvaluationResult.disabled( "Disabled on JRE version: " + System.getProperty( "java.version" ) ); + + protected final Compiler compiler; + + public CompilerTestEnabledOnJreCondition(Compiler compiler) { + this.compiler = compiler; + } + + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + // If the max JRE is greater or equal to the current version the test is enabled + return compiler.latestSupportedJre().compareTo( JRE.currentVersion() ) >= 0 ? ENABLED_ON_CURRENT_JRE : + DISABLED_ON_CURRENT_JRE; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java similarity index 81% rename from processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java rename to processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java index 3e61d8bd9e..b372974461 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java @@ -5,16 +5,14 @@ */ package org.mapstruct.ap.testutil.runner; -import static org.assertj.core.api.Assertions.assertThat; - import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -26,34 +24,38 @@ import java.util.Set; import java.util.stream.Stream; +import com.puppycrawl.tools.checkstyle.Checker; +import com.puppycrawl.tools.checkstyle.ConfigurationLoader; +import com.puppycrawl.tools.checkstyle.DefaultLogger; +import com.puppycrawl.tools.checkstyle.PropertiesExpander; import com.puppycrawl.tools.checkstyle.api.AutomaticBean; import org.apache.commons.io.output.NullOutputStream; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; -import org.mapstruct.ap.testutil.WithServiceImplementations; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.DisableCheckstyle; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedNote; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; -import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOptions; import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor; import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor; import org.xml.sax.InputSource; -import com.puppycrawl.tools.checkstyle.Checker; -import com.puppycrawl.tools.checkstyle.ConfigurationLoader; -import com.puppycrawl.tools.checkstyle.DefaultLogger; -import com.puppycrawl.tools.checkstyle.PropertiesExpander; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation; +import static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations; /** - * A JUnit4 statement that performs source generation using the annotation processor and compiles those sources. + * A JUnit Jupiter Extension that performs source generation using the annotation processor and compiles those sources. * * @author Andreas Gudian + * @author Filip Hrisafov */ -abstract class CompilingStatement extends Statement { +abstract class CompilingExtension implements BeforeEachCallback { + + static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create( new Object() ); private static final String TARGET_COMPILATION_TESTS = "/target/compilation-tests/"; @@ -67,46 +69,20 @@ abstract class CompilingStatement extends Statement { protected static final List PROCESSOR_CLASSPATH = buildProcessorClasspath(); - private final FrameworkMethod method; - private final CompilationCache compilationCache; - private final boolean runCheckstyle; - private Statement next; - private String classOutputDir; private String sourceOutputDir; private String additionalCompilerClasspath; - private CompilationRequest compilationRequest; - - CompilingStatement(FrameworkMethod method, CompilationCache compilationCache) { - this.method = method; - this.compilationCache = compilationCache; - this.runCheckstyle = !method.getMethod().getDeclaringClass().isAnnotationPresent( DisableCheckstyle.class ); - - this.compilationRequest = new CompilationRequest( getTestClasses(), getServices(), getProcessorOptions() ); - } - - void setNextStatement(Statement next) { - this.next = next; - } - - @Override - public void evaluate() throws Throwable { - generateMapperImplementation(); - - GeneratedSource.setCompilingStatement( this ); - next.evaluate(); - GeneratedSource.clearCompilingStatement(); - } + private final Compiler compiler; - String getSourceOutputDir() { - return compilationCache.getLastSourceOutputDir(); + protected CompilingExtension(Compiler compiler) { + this.compiler = compiler; } - protected void setupDirectories() { + protected void setupDirectories(Method testMethod, Class testClass) { String compilationRoot = getBasePath() + TARGET_COMPILATION_TESTS - + method.getDeclaringClass().getName() - + "/" + method.getName() + + testClass.getName() + + "/" + testMethod.getName() + getPathSuffix(); classOutputDir = compilationRoot + "/classes"; @@ -118,7 +94,9 @@ protected void setupDirectories() { ( (ModifiableURLClassLoader) Thread.currentThread().getContextClassLoader() ).withPath( classOutputDir ); } - protected abstract String getPathSuffix(); + protected String getPathSuffix() { + return "_" + compiler.name().toLowerCase(); + } private static List buildTestCompilationClasspath() { String[] whitelist = @@ -169,14 +147,21 @@ private static boolean isWhitelisted(String path, String[] whitelist) { return Stream.of( whitelist ).anyMatch( path::contains ); } - protected void generateMapperImplementation() throws Exception { - CompilationOutcomeDescriptor actualResult = compile(); + @Override + public void beforeEach(ExtensionContext context) throws Exception { + CompilationOutcomeDescriptor actualResult = compile( context ); + assertResult( actualResult, context ); + } + + private void assertResult(CompilationOutcomeDescriptor actualResult, ExtensionContext context) throws Exception { + Method testMethod = context.getRequiredTestMethod(); + Class testClass = context.getRequiredTestClass(); CompilationOutcomeDescriptor expectedResult = CompilationOutcomeDescriptor.forExpectedCompilationResult( - method.getAnnotation( ExpectedCompilationOutcome.class ), - method.getAnnotation( ExpectedNote.ExpectedNotes.class ), - method.getAnnotation( ExpectedNote.class ) + findAnnotation( testMethod, ExpectedCompilationOutcome.class ).orElse( null ), + findAnnotation( testMethod, ExpectedNote.ExpectedNotes.class ).orElse( null ), + findAnnotation( testMethod, ExpectedNote.class ).orElse( null ) ); if ( expectedResult.getCompilationResult() == CompilationResult.SUCCEEDED ) { @@ -195,7 +180,7 @@ protected void generateMapperImplementation() throws Exception { assertDiagnostics( actualResult.getDiagnostics(), expectedResult.getDiagnostics() ); assertNotes( actualResult.getNotes(), expectedResult.getNotes() ); - if ( runCheckstyle ) { + if ( !findAnnotation( testClass, DisableCheckstyle.class ).isPresent() ) { assertCheckstyleRules(); } } @@ -349,18 +334,14 @@ protected List filterExpectedDiagnostics(List> getTestClasses() { + private Set> getTestClasses(Method testMethod, Class testClass) { Set> testClasses = new HashSet<>(); - WithClasses withClasses = method.getAnnotation( WithClasses.class ); - if ( withClasses != null ) { - testClasses.addAll( Arrays.asList( withClasses.value() ) ); - } + findAnnotation( testMethod, WithClasses.class ) + .ifPresent( withClasses -> testClasses.addAll( Arrays.asList( withClasses.value() ) ) ); - withClasses = method.getMethod().getDeclaringClass().getAnnotation( WithClasses.class ); - if ( withClasses != null ) { - testClasses.addAll( Arrays.asList( withClasses.value() ) ); - } + findAnnotation( testClass, WithClasses.class ) + .ifPresent( withClasses -> testClasses.addAll( Arrays.asList( withClasses.value() ) ) ); if ( testClasses.isEmpty() ) { throw new IllegalStateException( @@ -377,24 +358,19 @@ private Set> getTestClasses() { * @return A map containing the package were to look for a resource (key) and the resource (value) to be compiled * for this test */ - private Map, Class> getServices() { + private Map, Class> getServices(Method testMethod, Class testClass) { Map, Class> services = new HashMap<>(); - addServices( services, method.getAnnotation( WithServiceImplementations.class ) ); - addService( services, method.getAnnotation( WithServiceImplementation.class ) ); + addServices( services, findRepeatableAnnotations( testMethod, WithServiceImplementation.class ) ); - Class declaringClass = method.getMethod().getDeclaringClass(); - addServices( services, declaringClass.getAnnotation( WithServiceImplementations.class ) ); - addService( services, declaringClass.getAnnotation( WithServiceImplementation.class ) ); + addServices( services, findRepeatableAnnotations( testClass, WithServiceImplementation.class ) ); return services; } - private void addServices(Map, Class> services, WithServiceImplementations withImplementations) { - if ( withImplementations != null ) { - for ( WithServiceImplementation resource : withImplementations.value() ) { - addService( services, resource ); - } + private void addServices(Map, Class> services, List withImplementations) { + for ( WithServiceImplementation withImplementation : withImplementations ) { + addService( services, withImplementation ); } } @@ -425,17 +401,11 @@ private void addService(Map, Class> services, WithServiceImplementat * * @return A list containing the processor options to be used for this test */ - private List getProcessorOptions() { - List processorOptions = - getProcessorOptions( - method.getAnnotation( ProcessorOptions.class ), - method.getAnnotation( ProcessorOption.class ) ); + private List getProcessorOptions(Method testMethod, Class testClass) { + List processorOptions = findRepeatableAnnotations( testMethod, ProcessorOption.class ); if ( processorOptions.isEmpty() ) { - processorOptions = - getProcessorOptions( - method.getMethod().getDeclaringClass().getAnnotation( ProcessorOptions.class ), - method.getMethod().getDeclaringClass().getAnnotation( ProcessorOption.class ) ); + processorOptions = findRepeatableAnnotations( testClass, ProcessorOption.class ); } List result = new ArrayList<>( processorOptions.size() ); @@ -449,17 +419,6 @@ private List getProcessorOptions() { return result; } - private List getProcessorOptions(ProcessorOptions options, ProcessorOption option) { - if ( options != null ) { - return Arrays.asList( options.value() ); - } - else if ( option != null ) { - return Arrays.asList( option ); - } - - return Collections.emptyList(); - } - private String asOptionString(ProcessorOption processorOption) { return String.format( "-A%s=%s", processorOption.name(), processorOption.value() ); } @@ -479,17 +438,32 @@ protected static Set getSourceFiles(Collection> classes) { return sourceFiles; } - private CompilationOutcomeDescriptor compile() - throws Exception { + private CompilationOutcomeDescriptor compile(ExtensionContext context) { + Method testMethod = context.getRequiredTestMethod(); + Class testClass = context.getRequiredTestClass(); + + CompilationRequest compilationRequest = new CompilationRequest( + compiler, + getTestClasses( testMethod, testClass ), + getServices( testMethod, testClass ), + getProcessorOptions( testMethod, testClass ) + ); + + ExtensionContext.Store rootStore = context.getRoot().getStore( NAMESPACE ); + + // We need to put the compilation request in the store, so the GeneratedSource can use it + context.getStore( NAMESPACE ).put( context.getUniqueId() + "-compilationRequest", compilationRequest ); + CompilationCache compilationCache = rootStore + .getOrComputeIfAbsent( compilationRequest, request -> new CompilationCache(), CompilationCache.class ); - if ( !needsRecompilation() ) { + if ( !needsRecompilation( compilationRequest, compilationCache ) ) { return compilationCache.getLastResult(); } - setupDirectories(); + setupDirectories( testMethod, testClass ); compilationCache.setLastSourceOutputDir( sourceOutputDir ); - boolean needsAdditionalCompilerClasspath = prepareServices(); + boolean needsAdditionalCompilerClasspath = prepareServices( compilationRequest ); CompilationOutcomeDescriptor resultHolder; resultHolder = compileWithSpecificCompiler( @@ -517,7 +491,7 @@ protected abstract CompilationOutcomeDescriptor compileWithSpecificCompiler( String classOutputDir, String additionalCompilerClasspath); - boolean needsRecompilation() { + boolean needsRecompilation(CompilationRequest compilationRequest, CompilationCache compilationCache) { return !compilationRequest.equals( compilationCache.getLastRequest() ); } @@ -559,7 +533,7 @@ private void deleteDirectory(File path) { path.delete(); } - private boolean prepareServices() { + private boolean prepareServices(CompilationRequest compilationRequest) { if ( !compilationRequest.getServices().isEmpty() ) { String servicesDir = additionalCompilerClasspath + File.separator + "META-INF" + File.separator + "services"; diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/DisabledOnCompiler.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/DisabledOnCompiler.java deleted file mode 100644 index 6fdfa37268..0000000000 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/DisabledOnCompiler.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.testutil.runner; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This should be used with care. - * This is similar to the JUnit 5 DisabledOnJre (once we have JUnit 5 we can replace this one) - * - * @author Filip Hrisafov - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface DisabledOnCompiler { - /** - * @return The compiler to use. - */ - Compiler[] value(); -} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java similarity index 94% rename from processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingStatement.java rename to processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java index 4dac535389..6cab46cc7f 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java @@ -16,16 +16,16 @@ import org.codehaus.plexus.compiler.CompilerResult; import org.codehaus.plexus.logging.console.ConsoleLogger; import org.eclipse.tycho.compiler.jdt.JDTCompiler; -import org.junit.runners.model.FrameworkMethod; import org.mapstruct.ap.MappingProcessor; import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor; /** - * Statement that uses the Eclipse JDT compiler to compile. + * Extension that uses the Eclipse JDT compiler to compile. * * @author Andreas Gudian + * @author Filip Hrisafov */ -class EclipseCompilingStatement extends CompilingStatement { +class EclipseCompilingExtension extends CompilingExtension { private static final List ECLIPSE_COMPILER_CLASSPATH = buildEclipseCompilerClasspath(); @@ -35,8 +35,8 @@ class EclipseCompilingStatement extends CompilingStatement { .withPaths( PROCESSOR_CLASSPATH ) .withOriginOf( ClassLoaderExecutor.class ); - EclipseCompilingStatement(FrameworkMethod method, CompilationCache compilationCache) { - super( method, compilationCache ); + EclipseCompilingExtension() { + super( Compiler.ECLIPSE ); } @Override @@ -152,9 +152,4 @@ private static List buildEclipseCompilerClasspath() { return filterBootClassPath( whitelist ); } - - @Override - protected String getPathSuffix() { - return "_eclipse"; - } } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EnabledOnCompiler.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EnabledOnCompiler.java deleted file mode 100644 index 7c6d79e0fc..0000000000 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EnabledOnCompiler.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.testutil.runner; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This should be used with care. - * This is similar to the JUnit 5 EnabledOnJre (once we have JUnit 5 we can replace this one) - * - * @author Filip Hrisafov - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface EnabledOnCompiler { - /** - * @return The compiler to use. - */ - Compiler[] value(); -} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/FilteringParentClassLoader.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/FilteringParentClassLoader.java index 983b8867f1..c83b76c10b 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/FilteringParentClassLoader.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/FilteringParentClassLoader.java @@ -25,6 +25,11 @@ final class FilteringParentClassLoader extends ClassLoader { this.excludedPrefixes = new ArrayList<>( Arrays.asList( excludedPrefixes ) ); } + FilteringParentClassLoader(ClassLoader parent, String... excludedPrefixes) { + super( parent ); + this.excludedPrefixes = new ArrayList<>( Arrays.asList( excludedPrefixes ) ); + } + /** * @param classes The classes to hide (inner classes are hidden as well) * @return {@code this} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java index f5a98bfee7..c0993ea098 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java @@ -13,48 +13,60 @@ import java.util.Arrays; import java.util.List; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; +import org.junit.jupiter.api.extension.ExtensionContext; import org.mapstruct.ap.testutil.assertions.JavaFileAssert; import static org.assertj.core.api.Assertions.fail; +import static org.mapstruct.ap.testutil.runner.CompilingExtension.NAMESPACE; /** - * A {@link TestRule} to perform assertions on generated source files. + * A {@link org.junit.jupiter.api.extension.RegisterExtension RegisterExtension} to perform assertions on generated + * source files. *

      * To add it to the test, use: * *

      - * @Rule
      - * public GeneratedSource generatedSources = new GeneratedSource();
      + * @RegisterExtension
      + * final GeneratedSource generatedSources = new GeneratedSource();
        * 
      * * @author Andreas Gudian */ -public class GeneratedSource implements TestRule { +public class GeneratedSource implements BeforeTestExecutionCallback, AfterTestExecutionCallback { private static final String FIXTURES_ROOT = "fixtures/"; /** - * static ThreadLocal, as the {@link CompilingStatement} must inject itself statically for this rule to gain access - * to the statement's information. As test execution of different classes in parallel is supported. + * ThreadLocal, as the source dir must be injected for this extension to gain access + * to the compilation information. As test execution of different classes in parallel is supported. */ - private static ThreadLocal compilingStatement = new ThreadLocal<>(); + private ThreadLocal sourceOutputDir = new ThreadLocal<>(); private List> fixturesFor = new ArrayList<>(); @Override - public Statement apply(Statement base, Description description) { - return new GeneratedSourceStatement( base ); + public void beforeTestExecution(ExtensionContext context) throws Exception { + CompilationRequest compilationRequest = context.getStore( NAMESPACE ) + .get( context.getUniqueId() + "-compilationRequest", CompilationRequest.class ); + setSourceOutputDir( context.getStore( NAMESPACE ) + .get( compilationRequest, CompilationCache.class ) + .getLastSourceOutputDir() ); } - static void setCompilingStatement(CompilingStatement compilingStatement) { - GeneratedSource.compilingStatement.set( compilingStatement ); + @Override + public void afterTestExecution(ExtensionContext context) throws Exception { + handleFixtureComparison(); + clearSourceOutputDir(); + } + + private void setSourceOutputDir(String sourceOutputDir) { + this.sourceOutputDir.set( sourceOutputDir ); } - static void clearCompilingStatement() { - GeneratedSource.compilingStatement.remove(); + private void clearSourceOutputDir() { + this.sourceOutputDir.remove(); } /** @@ -101,21 +113,7 @@ public JavaFileAssert forDecoratedMapper(Class mapperClass) { * @return an assert for the file specified by the given path */ public JavaFileAssert forJavaFile(String path) { - return new JavaFileAssert( new File( compilingStatement.get().getSourceOutputDir() + "/" + path ) ); - } - - private class GeneratedSourceStatement extends Statement { - private final Statement next; - - private GeneratedSourceStatement(Statement next) { - this.next = next; - } - - @Override - public void evaluate() throws Throwable { - next.evaluate(); - handleFixtureComparison(); - } + return new JavaFileAssert( new File( sourceOutputDir.get() + "/" + path ) ); } private void handleFixtureComparison() throws UnsupportedEncodingException { diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/InnerAnnotationProcessorRunner.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/InnerAnnotationProcessorRunner.java deleted file mode 100644 index 50ab9c6eb5..0000000000 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/InnerAnnotationProcessorRunner.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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.testutil.runner; - -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; -import org.junit.runners.model.TestClass; - -/** - * Internal test runner that runs the tests of one class for one specific compiler implementation. - * - * @author Andreas Gudian - */ -class InnerAnnotationProcessorRunner extends BlockJUnit4ClassRunner { - static final ModifiableURLClassLoader TEST_CLASS_LOADER = new ModifiableURLClassLoader(); - private final Class klass; - private final Compiler compiler; - private final CompilationCache compilationCache; - private Class klassToUse; - private ReplacableTestClass replacableTestClass; - - /** - * @param klass the test class - * - * @throws Exception see {@link BlockJUnit4ClassRunner#BlockJUnit4ClassRunner(Class)} - */ - InnerAnnotationProcessorRunner(Class klass, Compiler compiler) throws Exception { - super( klass ); - this.klass = klass; - this.compiler = compiler; - this.compilationCache = new CompilationCache(); - } - - /** - * newly loads the class with the test class loader and sets that loader as context class loader of the thread - * - * @param klass the class to replace - * - * @return the class loaded with the test class loader - */ - private static Class replaceClassLoaderAndClass(Class klass) { - replaceContextClassLoader( klass ); - - try { - return Thread.currentThread().getContextClassLoader().loadClass( klass.getName() ); - } - catch ( ClassNotFoundException e ) { - throw new RuntimeException( e ); - } - - } - - private static void replaceContextClassLoader(Class klass) { - ModifiableURLClassLoader testClassLoader = new ModifiableURLClassLoader().withOriginOf( klass ); - - Thread.currentThread().setContextClassLoader( testClassLoader ); - } - - @Override - protected boolean isIgnored(FrameworkMethod child) { - return super.isIgnored( child ) || isIgnoredForCompiler( child ); - } - - protected boolean isIgnoredForCompiler(FrameworkMethod child) { - EnabledOnCompiler enabledOnCompiler = child.getAnnotation( EnabledOnCompiler.class ); - if ( enabledOnCompiler != null ) { - for ( Compiler value : enabledOnCompiler.value() ) { - if ( value != compiler ) { - return true; - } - } - } - - DisabledOnCompiler disabledOnCompiler = child.getAnnotation( DisabledOnCompiler.class ); - if ( disabledOnCompiler != null ) { - for ( Compiler value : disabledOnCompiler.value() ) { - if ( value == compiler ) { - return true; - } - } - } - - return false; - } - - @Override - protected TestClass createTestClass(final Class testClass) { - replacableTestClass = new ReplacableTestClass( testClass ); - return replacableTestClass; - } - - private FrameworkMethod replaceFrameworkMethod(FrameworkMethod m) { - try { - return new FrameworkMethod( - klassToUse.getDeclaredMethod( m.getName(), m.getMethod().getParameterTypes() ) ); - } - catch ( NoSuchMethodException e ) { - throw new RuntimeException( e ); - } - } - - @Override - protected Statement methodBlock(FrameworkMethod method) { - CompilingStatement statement = createCompilingStatement( method ); - if ( statement.needsRecompilation() ) { - klassToUse = replaceClassLoaderAndClass( klass ); - - replacableTestClass.replaceClass( klassToUse ); - } - - method = replaceFrameworkMethod( method ); - - Statement next = super.methodBlock( method ); - - statement.setNextStatement( next ); - - return statement; - } - - private CompilingStatement createCompilingStatement(FrameworkMethod method) { - if ( compiler == Compiler.JDK ) { - return new JdkCompilingStatement( method, compilationCache ); - } - else if ( compiler == Compiler.JDK11 ) { - return new Jdk11CompilingStatement( method, compilationCache ); - } - else { - return new EclipseCompilingStatement( method, compilationCache ); - } - } - - @Override - protected String getName() { - return "[" + compiler.name().toLowerCase() + "]"; - } - - @Override - protected String testName(FrameworkMethod method) { - return method.getName() + getName(); - } -} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/Jdk11CompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/Jdk11CompilingStatement.java deleted file mode 100644 index 30f12aead7..0000000000 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/Jdk11CompilingStatement.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.testutil.runner; - -import java.util.List; - -import org.junit.runners.model.FrameworkMethod; -import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor; - -/** - * Statement that uses the JDK compiler to compile. - * - * @author Filip Hrisafov - */ -class Jdk11CompilingStatement extends JdkCompilingStatement { - - Jdk11CompilingStatement(FrameworkMethod method, CompilationCache compilationCache) { - super( method, compilationCache ); - } - - - /** - * The JDK 11 compiler reports all ERROR diagnostics properly. Also when there are multiple per line. - */ - @Override - protected List filterExpectedDiagnostics(List expectedDiagnostics) { - return expectedDiagnostics; - } - - @Override - protected String getPathSuffix() { - return "_jdk"; - } -} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java similarity index 81% rename from processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingStatement.java rename to processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java index 454f2c3c3c..1761956678 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingStatement.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java @@ -11,7 +11,6 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; - import javax.annotation.processing.Processor; import javax.tools.Diagnostic.Kind; import javax.tools.DiagnosticCollector; @@ -22,17 +21,18 @@ import javax.tools.StandardLocation; import javax.tools.ToolProvider; -import org.junit.runners.model.FrameworkMethod; +import org.junit.jupiter.api.condition.JRE; import org.mapstruct.ap.MappingProcessor; import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor; import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor; /** - * Statement that uses the JDK compiler to compile. + * Extension that uses the JDK compiler to compile. * * @author Andreas Gudian + * @author Filip Hrisafov */ -class JdkCompilingStatement extends CompilingStatement { +class JdkCompilingExtension extends CompilingExtension { private static final List COMPILER_CLASSPATH_FILES = asFiles( TEST_COMPILATION_CLASSPATH ); @@ -40,8 +40,8 @@ class JdkCompilingStatement extends CompilingStatement { new ModifiableURLClassLoader( new FilteringParentClassLoader( "org.mapstruct." ) ) .withPaths( PROCESSOR_CLASSPATH ); - JdkCompilingStatement(FrameworkMethod method, CompilationCache compilationCache) { - super( method, compilationCache ); + JdkCompilingExtension() { + super( Compiler.JDK ); } @Override @@ -107,14 +107,20 @@ private static List asFiles(List paths) { } /** - * The JDK compiler only reports the first message of kind ERROR that is reported for one source file line, so we - * filter out the surplus diagnostics. The input list is already sorted by file name and line number, with the order - * for the diagnostics in the same line being kept at the order as given in the test. + * The JDK 8 compiler needs some special treatment for the diagnostics. + * See comment in the function. */ @Override protected List filterExpectedDiagnostics(List expectedDiagnostics) { - List filtered = new ArrayList<>( expectedDiagnostics.size() ); + if ( JRE.currentVersion() != JRE.JAVA_8 ) { + // The JDK 8+ compilers report all ERROR diagnostics properly. Also when there are multiple per line. + return expectedDiagnostics; + } + List filtered = new ArrayList( expectedDiagnostics.size() ); + // The JDK 8 compiler only reports the first message of kind ERROR that is reported for one source file line, + // so we filter out the surplus diagnostics. The input list is already sorted by file name and line number, + // with the order for the diagnostics in the same line being kept at the order as given in the test. DiagnosticDescriptor previous = null; for ( DiagnosticDescriptor diag : expectedDiagnostics ) { if ( diag.getKind() != Kind.ERROR @@ -129,8 +135,4 @@ protected List filterExpectedDiagnostics(List provideTestTemplateInvocationContexts(ExtensionContext context) { + Method testMethod = context.getRequiredTestMethod(); + ProcessorTest processorTest = AnnotationSupport.findAnnotation( testMethod, ProcessorTest.class ) + .orElseThrow( () -> new RuntimeException( "Failed to get CompilerTest on " + testMethod ) ); + + return Stream.of( processorTest.value() ) + .map( ProcessorTestInvocationContext::new ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/ProcessorTestInvocationContext.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/ProcessorTestInvocationContext.java new file mode 100644 index 0000000000..d557095dff --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/ProcessorTestInvocationContext.java @@ -0,0 +1,48 @@ +/* + * 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.testutil.runner; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; + +/** + * The template invocation processor responsible for providing the appropriate extensions for the different compilers. + * + * @author Filip Hrisafov + */ +public class ProcessorTestInvocationContext implements TestTemplateInvocationContext { + + protected Compiler compiler; + + public ProcessorTestInvocationContext(Compiler compiler) { + this.compiler = compiler; + } + + @Override + public String getDisplayName(int invocationIndex) { + return "[" + compiler.name().toLowerCase() + "]"; + } + + @Override + public List getAdditionalExtensions() { + List extensions = new ArrayList<>(); + extensions.add( new CompilerTestEnabledOnJreCondition( compiler ) ); + if ( compiler == Compiler.JDK ) { + extensions.add( new JdkCompilingExtension() ); + } + else if ( compiler == Compiler.ECLIPSE ) { + extensions.add( new EclipseCompilingExtension() ); + } + else { + throw new IllegalArgumentException( "Compiler [" + compiler + "] is not known" ); + } + + return extensions; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/ReplacableTestClass.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/ReplacableTestClass.java deleted file mode 100644 index 7141b78146..0000000000 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/ReplacableTestClass.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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.testutil.runner; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.util.List; - -import org.junit.runners.model.FrameworkField; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.TestClass; - -/** - * A {@link TestClass} where the wrapped Class can be replaced - * - * @author Andreas Gudian - */ -class ReplacableTestClass extends TestClass { - private TestClass delegate; - - /** - * @see TestClass#TestClass(Class) - */ - ReplacableTestClass(Class clazz) { - super( clazz ); - } - - /** - * @param clazz the new class - */ - void replaceClass(Class clazz) { - delegate = new TestClass( clazz ); - } - - @Override - public List getAnnotatedMethods() { - if ( null == delegate ) { - return super.getAnnotatedMethods(); - } - else { - return delegate.getAnnotatedMethods(); - } - } - - @Override - public List getAnnotatedMethods(Class annotationClass) { - if ( null == delegate ) { - return super.getAnnotatedMethods( annotationClass ); - } - else { - return delegate.getAnnotatedMethods( annotationClass ); - } - } - - @Override - public List getAnnotatedFields() { - if ( null == delegate ) { - return super.getAnnotatedFields(); - } - else { - return delegate.getAnnotatedFields(); - } - } - - @Override - public List getAnnotatedFields(Class annotationClass) { - if ( null == delegate ) { - return super.getAnnotatedFields( annotationClass ); - } - else { - return delegate.getAnnotatedFields( annotationClass ); - } - } - - @Override - public Class getJavaClass() { - if ( null == delegate ) { - return super.getJavaClass(); - } - else { - return delegate.getJavaClass(); - } - } - - @Override - public String getName() { - if ( null == delegate ) { - return super.getName(); - } - else { - return delegate.getName(); - } - } - - @Override - public Constructor getOnlyConstructor() { - if ( null == delegate ) { - return super.getOnlyConstructor(); - } - else { - return delegate.getOnlyConstructor(); - } - } - - @Override - public Annotation[] getAnnotations() { - if ( null == delegate ) { - return super.getAnnotations(); - } - else { - return delegate.getAnnotations(); - } - } - - @Override - public T getAnnotation(Class annotationType) { - if ( null == delegate ) { - return super.getAnnotation( annotationType ); - } - else { - return delegate.getAnnotation( annotationType ); - } - } - - @Override - public List getAnnotatedFieldValues(Object test, Class annotationClass, - Class valueClass) { - if ( null == delegate ) { - return super.getAnnotatedFieldValues( test, annotationClass, valueClass ); - } - else { - return delegate.getAnnotatedFieldValues( test, annotationClass, valueClass ); - } - } - - @Override - public List getAnnotatedMethodValues(Object test, Class annotationClass, - Class valueClass) { - if ( null == delegate ) { - return super.getAnnotatedMethodValues( test, annotationClass, valueClass ); - } - else { - return delegate.getAnnotatedMethodValues( test, annotationClass, valueClass ); - } - } - - @Override - public String toString() { - if ( null == delegate ) { - return super.toString(); - } - else { - return delegate.toString(); - } - } - - @Override - public boolean isPublic() { - if ( null == delegate ) { - return super.isPublic(); - } - else { - return delegate.isPublic(); - } - } - - @Override - public boolean isANonStaticInnerClass() { - if ( null == delegate ) { - return super.isANonStaticInnerClass(); - } - else { - return delegate.isANonStaticInnerClass(); - } - } - - @Override - public int hashCode() { - if ( null == delegate ) { - return super.hashCode(); - } - else { - return delegate.hashCode(); - } - } - - @Override - public boolean equals(Object obj) { - if ( null == delegate ) { - return super.equals( obj ); - } - else { - return delegate.equals( obj ); - } - } -} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/WithSingleCompiler.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/WithSingleCompiler.java deleted file mode 100644 index dc930f370a..0000000000 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/WithSingleCompiler.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.testutil.runner; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Temporarily only use the specified compiler for the test during debugging / implementation. - *

      - * Do not commit tests with this annotation present! - * - * @deprecated Do not commit tests with this annotation present. Tests are expected to work with all compilers. - * @author Andreas Gudian - */ -@Deprecated -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface WithSingleCompiler { - /** - * @return The compiler to use. - */ - Compiler value(); -} diff --git a/processor/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener b/processor/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener new file mode 100644 index 0000000000..8ffa996976 --- /dev/null +++ b/processor/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener @@ -0,0 +1 @@ +org.mapstruct.ap.testutil.runner.CompilerLauncherDiscoveryListener \ No newline at end of file From 2d66f08ee563bab15485f4da4ee48242d7fd96d2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 14 Feb 2021 21:37:09 +0100 Subject: [PATCH 159/597] Migrate process tests to use new JUnit Jupiter Infrastructure Update test annotations to be JUnit Jupiter compatible Replace all Test annotations from tests that are run with the AnnotationProcessorTestRunner with ProcessorTest. Replace JUnit 4 Test#expected with assertThatThrownBy from AssertJ. Replace Rule for GeneratedSource with RegisterExtension. Fix some tests that were not reverting the changes to the default Locale and TimeZone. Replace usage of org.junit.Assert with equivalent from org.junit.jupiter.api.Assertions or AssertJ. --- .../DateFormatValidatorFactoryTest.java | 2 +- .../common/DefaultConversionContextTest.java | 2 +- .../model/source/SelectionParametersTest.java | 2 +- .../ap/internal/util/NativeTypesTest.java | 7 +- .../ap/internal/util/StringsTest.java | 10 +-- .../ap/spi/util/IntrospectorUtilsTest.java | 4 +- .../test/abstractclass/AbstractClassTest.java | 15 ++-- .../generics/GenericsHierarchyTest.java | 25 +++---- .../test/accessibility/AccessibilityTest.java | 17 ++--- .../ReferencedAccessibilityTest.java | 23 +++--- .../ap/test/array/ArrayMappingTest.java | 53 +++++++------- .../ap/test/bool/BooleanMappingTest.java | 15 ++-- .../ap/test/bugs/_1005/Issue1005Test.java | 13 ++-- .../ap/test/bugs/_1029/Issue1029Test.java | 7 +- .../ap/test/bugs/_1061/Issue1061Test.java | 7 +- .../ap/test/bugs/_1111/Issue1111Test.java | 12 ++- .../ap/test/bugs/_1124/Issue1124Test.java | 11 +-- .../ap/test/bugs/_1130/Issue1130Test.java | 11 +-- .../ap/test/bugs/_1131/Issue1131Test.java | 10 +-- .../ap/test/bugs/_1148/Issue1148Test.java | 21 +++--- .../ap/test/bugs/_1153/Issue1153Test.java | 7 +- .../ap/test/bugs/_1155/Issue1155Test.java | 7 +- .../ap/test/bugs/_1159/Issue1159Test.java | 12 +-- .../ap/test/bugs/_1164/Issue1164Test.java | 7 +- .../ap/test/bugs/_1170/AdderTest.java | 13 ++-- .../ap/test/bugs/_1180/Issue1180Test.java | 7 +- .../ap/test/bugs/_1215/Issue1215Test.java | 7 +- .../ap/test/bugs/_1227/Issue1227Test.java | 7 +- .../ap/test/bugs/_1242/Issue1242Test.java | 13 ++-- .../ap/test/bugs/_1244/Issue1244Test.java | 7 +- .../ap/test/bugs/_1247/Issue1247Test.java | 11 +-- .../ap/test/bugs/_1255/Issue1255Test.java | 12 ++- .../ap/test/bugs/_1269/Issue1269Test.java | 7 +- .../ap/test/bugs/_1273/Issue1273Test.java | 13 ++-- .../ap/test/bugs/_1283/Issue1283Test.java | 9 +-- .../ap/test/bugs/_1320/Issue1320Test.java | 7 +- .../ap/test/bugs/_1338/Issue1338Test.java | 7 +- .../ap/test/bugs/_1339/Issue1339Test.java | 7 +- .../ap/test/bugs/_1340/Issue1340Test.java | 7 +- .../ap/test/bugs/_1345/Issue1345Test.java | 7 +- .../ap/test/bugs/_1353/Issue1353Test.java | 7 +- .../ap/test/bugs/_1359/Issue1359Test.java | 7 +- .../ap/test/bugs/_1375/Issue1375Test.java | 7 +- .../ap/test/bugs/_1395/Issue1395Test.java | 7 +- .../ap/test/bugs/_1425/Issue1425Test.java | 7 +- .../ap/test/bugs/_1435/Issue1435Test.java | 7 +- .../ap/test/bugs/_1453/Issue1453Test.java | 13 ++-- .../ap/test/bugs/_1457/Issue1457Test.java | 17 ++--- .../ap/test/bugs/_1460/Issue1460Test.java | 7 +- .../_1460/java8/Issue1460JavaTimeTest.java | 7 +- .../ap/test/bugs/_1482/Issue1482Test.java | 9 +-- .../test/bugs/_1523/java8/Issue1523Test.java | 15 ++-- .../ap/test/bugs/_1541/Issue1541Test.java | 21 +++--- .../ap/test/bugs/_1552/Issue1552Test.java | 7 +- .../test/bugs/_1558/java8/Issue1558Test.java | 7 +- .../ap/test/bugs/_1561/Issue1561Test.java | 13 ++-- .../ap/test/bugs/_1566/Issue1566Test.java | 7 +- .../test/bugs/_1569/java8/Issue1569Test.java | 7 +- .../test/bugs/_1576/java8/Issue1576Test.java | 13 ++-- .../ap/test/bugs/_1590/Issue1590Test.java | 7 +- .../ap/test/bugs/_1594/Issue1594Test.java | 7 +- .../ap/test/bugs/_1596/Issue1596Test.java | 7 +- .../ap/test/bugs/_1608/Issue1608Test.java | 7 +- .../ap/test/bugs/_1648/Issue1648Test.java | 7 +- .../ap/test/bugs/_1650/Issue1650Test.java | 7 +- .../ap/test/bugs/_1660/Issue1660Test.java | 7 +- .../ap/test/bugs/_1665/Issue1665Test.java | 13 ++-- .../ap/test/bugs/_1681/Issue1681Test.java | 9 +-- .../ap/test/bugs/_1685/Issue1685Test.java | 17 ++--- .../ap/test/bugs/_1698/Issue1698Test.java | 7 +- .../ap/test/bugs/_1707/Issue1707Test.java | 13 ++-- .../ap/test/bugs/_1714/Issue1714Test.java | 7 +- .../ap/test/bugs/_1719/Issue1719Test.java | 7 +- .../ap/test/bugs/_1738/Issue1738Test.java | 7 +- .../ap/test/bugs/_1742/Issue1742Test.java | 7 +- .../ap/test/bugs/_1751/Issue1751Test.java | 7 +- .../ap/test/bugs/_1772/Issue1772Test.java | 7 +- .../ap/test/bugs/_1788/Issue1788Test.java | 7 +- .../ap/test/bugs/_1790/Issue1790Test.java | 7 +- .../ap/test/bugs/_1797/Issue1797Test.java | 7 +- .../ap/test/bugs/_1799/Issue1799Test.java | 7 +- .../ap/test/bugs/_1801/Issue1801Test.java | 7 +- .../ap/test/bugs/_1821/Issue1821Test.java | 7 +- .../ap/test/bugs/_1826/Issue1826Test.java | 7 +- .../ap/test/bugs/_1828/Issue1828Test.java | 11 +-- .../ap/test/bugs/_1881/Issue1881Test.java | 7 +- .../ap/test/bugs/_1904/Issue1904Test.java | 7 +- .../ap/test/bugs/_1933/Issue1933Test.java | 7 +- .../ap/test/bugs/_1966/Issue1966Test.java | 7 +- .../ap/test/bugs/_2001/Issue2001Test.java | 7 +- .../ap/test/bugs/_2018/Issue2018Test.java | 7 +- .../ap/test/bugs/_2021/Issue2021Test.java | 7 +- .../ap/test/bugs/_2023/Issue2023Test.java | 7 +- .../ap/test/bugs/_2042/Issue2402Test.java | 7 +- .../ap/test/bugs/_2077/Issue2077Test.java | 7 +- .../ap/test/bugs/_2101/Issue2101Test.java | 13 ++-- .../ap/test/bugs/_2109/Issue2109Test.java | 7 +- .../ap/test/bugs/_2111/Issue2111Test.java | 7 +- .../ap/test/bugs/_2117/Issue2117Test.java | 7 +- .../ap/test/bugs/_2121/Issue2121Test.java | 7 +- .../ap/test/bugs/_2122/Issue2122Test.java | 12 +-- .../ap/test/bugs/_2124/Issue2124Test.java | 7 +- .../ap/test/bugs/_2125/Issue2125Test.java | 9 +-- .../ap/test/bugs/_2131/Issue2131Test.java | 7 +- .../ap/test/bugs/_2133/Issue2133Test.java | 7 +- .../ap/test/bugs/_2142/Issue2142Test.java | 13 ++-- .../ap/test/bugs/_2145/Issue2145Test.java | 7 +- .../ap/test/bugs/_2149/Issue2149Test.java | 7 +- .../ap/test/bugs/_2164/Issue2164Test.java | 7 +- .../ap/test/bugs/_2170/Issue2170Test.java | 7 +- .../ap/test/bugs/_2174/Issue2174Test.java | 7 +- .../ap/test/bugs/_2177/Issue2177Test.java | 7 +- .../ap/test/bugs/_2185/Issue2185Test.java | 7 +- .../ap/test/bugs/_2195/Issue2195Test.java | 7 +- .../ap/test/bugs/_2197/Issue2197Test.java | 13 ++-- .../ap/test/bugs/_2213/Issue2213Test.java | 13 ++-- .../ap/test/bugs/_2221/Issue2221Test.java | 7 +- .../ap/test/bugs/_2233/Issue2233Test.java | 7 +- .../ap/test/bugs/_2236/Issue2236Test.java | 7 +- .../ap/test/bugs/_2245/Issue2245Test.java | 13 ++-- .../ap/test/bugs/_2251/Issue2251Test.java | 7 +- .../ap/test/bugs/_2253/Issue2253Test.java | 7 +- .../ap/test/bugs/_2263/Issue2263Test.java | 7 +- .../ap/test/bugs/_2278/Issue2278Test.java | 11 +-- .../ap/test/bugs/_2301/Issue2301Test.java | 7 +- .../ap/test/bugs/_2318/Issue2318Test.java | 7 +- .../ap/test/bugs/_2347/Issue2347Test.java | 7 +- .../ap/test/bugs/_2352/Issue2352Test.java | 7 +- .../ap/test/bugs/_2356/Issue2356Test.java | 7 +- .../ap/test/bugs/_2393/Issue2393Test.java | 7 +- .../ap/test/bugs/_289/Issue289Test.java | 15 ++-- .../ap/test/bugs/_306/Issue306Test.java | 7 +- .../ap/test/bugs/_373/Issue373Test.java | 7 +- .../ap/test/bugs/_374/Issue374Test.java | 23 +++--- .../ap/test/bugs/_375/Issue375Test.java | 7 +- .../SameClassNameInDifferentPackageTest.java | 11 +-- .../ap/test/bugs/_405/Issue405Test.java | 7 +- .../ap/test/bugs/_513/Issue513Test.java | 27 ++++--- .../ap/test/bugs/_515/Issue515Test.java | 7 +- .../ap/test/bugs/_516/Issue516Test.java | 11 +-- .../ap/test/bugs/_537/Issue537Test.java | 7 +- .../ap/test/bugs/_543/Issue543Test.java | 13 ++-- .../ap/test/bugs/_577/Issue577Test.java | 11 +-- .../ap/test/bugs/_581/Issue581Test.java | 11 +-- .../ap/test/bugs/_590/Issue590Test.java | 7 +- .../ap/test/bugs/_611/Issue611Test.java | 11 +-- .../ap/test/bugs/_625/Issue625Test.java | 11 +-- .../ap/test/bugs/_631/Issue631Test.java | 7 +- .../test/bugs/_634/GenericContainerTest.java | 11 +-- .../IterableWithBoundedElementTypeTest.java | 13 ++-- .../mapstruct/ap/test/bugs/_843/Commit.java | 4 + .../ap/test/bugs/_843/GitlabTag.java | 4 + .../ap/test/bugs/_843/Issue843Test.java | 13 ++-- .../ap/test/bugs/_846/UpdateTest.java | 7 +- .../ap/test/bugs/_849/Issue849Test.java | 11 +-- .../ap/test/bugs/_855/OrderingBug855Test.java | 13 ++-- .../ap/test/bugs/_865/Issue865Test.java | 12 ++- .../ap/test/bugs/_880/Issue880Test.java | 13 ++-- .../ap/test/bugs/_891/Issue891Test.java | 7 +- .../ap/test/bugs/_892/Issue892Test.java | 11 +-- .../ap/test/bugs/_895/Issue895Test.java | 11 +-- .../ap/test/bugs/_909/Issue909Test.java | 11 +-- ...ssue913GetterMapperForCollectionsTest.java | 26 +++---- ...ssue913SetterMapperForCollectionsTest.java | 41 +++++------ .../ap/test/bugs/_931/Issue931Test.java | 11 +-- .../ap/test/bugs/_955/Issue955Test.java | 7 +- .../ap/test/bugs/_971/Issue971Test.java | 9 +-- .../abstractBuilder/AbstractBuilderTest.java | 9 +-- .../AbstractGenericTargetBuilderTest.java | 7 +- .../factory/BuilderFactoryMapperTest.java | 9 +-- .../builder/ignore/BuilderIgnoringTest.java | 11 +-- .../BuilderLifecycleCallbacksTest.java | 7 +- .../simple/BuilderInfoTargetTest.java | 19 ++--- .../multiple/MultipleBuilderMapperTest.java | 17 ++--- .../expanding/BuilderNestedPropertyTest.java | 7 +- .../flattening/BuilderNestedPropertyTest.java | 7 +- .../builder/noop/NoOpBuilderProviderTest.java | 7 +- .../SimpleNotRealyImmutableBuilderTest.java | 13 ++-- .../parentchild/ParentChildBuilderTest.java | 7 +- .../simple/SimpleImmutableBuilderTest.java | 15 ++-- .../ap/test/builtin/BuiltInTest.java | 50 ++++++------- .../ap/test/builtin/DatatypeFactoryTest.java | 15 ++-- .../test/builtin/jodatime/JodaTimeTest.java | 57 +++++++-------- .../ap/test/callbacks/CallbackMethodTest.java | 29 ++++---- .../MappingResultPostprocessorTest.java | 11 +-- .../CallbacksWithReturnValuesTest.java | 28 +++---- .../CallbackMethodTypeMatchingTest.java | 11 +-- .../collection/CollectionMappingTest.java | 67 ++++++++--------- .../ap/test/collection/adder/AdderTest.java | 54 +++++++------- .../DefaultCollectionImplementationTest.java | 45 ++++++------ .../NoSetterCollectionMappingTest.java | 13 ++-- .../ErroneousCollectionMappingTest.java | 25 +++---- .../forged/CollectionMappingTest.java | 28 +++---- .../immutabletarget/ImmutableProductTest.java | 13 ++-- .../IterableToNonIterableMappingTest.java | 13 ++-- .../test/collection/map/MapMappingTest.java | 25 +++---- .../collection/wildcard/WildCardTest.java | 30 ++++---- .../ap/test/complex/CarMapperTest.java | 37 +++++----- .../basic/ConditionalMappingTest.java | 31 ++++---- .../expression/ConditionalExpressionTest.java | 11 +-- .../qualifier/ConditionalQualifierTest.java | 9 +-- .../SimpleConstructorPropertiesTest.java | 11 +-- ...SimpleDefaultAnnotatedConstructorTest.java | 11 +-- .../erroneous/ErroneousConstructorTest.java | 9 +-- ...SourceArgumentsConstructorMappingTest.java | 7 +- .../ConstructorMixedWithSettersTest.java | 7 +- ...NestedSourcePropertiesConstructorTest.java | 17 ++--- ...NestedSourcePropertiesConstructorTest.java | 17 ++--- ...estedProductPropertiesConstructorTest.java | 15 ++-- .../simple/SimpleConstructorTest.java | 11 +-- .../unmapped/UnmappedConstructorTest.java | 7 +- .../visibility/ConstructorVisibilityTest.java | 11 +-- .../ContextParameterErroneousTest.java | 7 +- .../ap/test/context/ContextParameterTest.java | 15 ++-- .../ContextWithObjectFactoryTest.java | 11 +-- .../ap/test/conversion/ConversionTest.java | 19 ++--- .../bignumbers/BigNumbersConversionTest.java | 31 +++----- .../currency/CurrencyConversionTest.java | 9 +-- .../conversion/date/DateConversionTest.java | 67 +++++++---------- .../erroneous/InvalidDateFormatTest.java | 7 +- .../java8time/Java8TimeConversionTest.java | 71 +++++++++--------- ...eToXMLGregorianCalendarConversionTest.java | 22 +++--- ...eToXMLGregorianCalendarConversionTest.java | 14 ++-- .../jodatime/JodaConversionTest.java | 61 +++++++--------- .../conversion/lossy/LossyConversionTest.java | 23 +++--- .../nativetypes/BooleanConversionTest.java | 15 ++-- .../nativetypes/CharConversionTest.java | 15 ++-- .../nativetypes/NumberConversionTest.java | 35 ++++----- .../numbers/NumberFormatConversionTest.java | 25 +++---- .../conversion/precedence/ConversionTest.java | 11 +-- .../string/StringConversionTest.java | 17 ++--- .../ap/test/decorator/DecoratorTest.java | 24 +++--- .../decorator/jsr330/Jsr330DecoratorTest.java | 36 ++++----- .../constructor/SpringDecoratorTest.java | 21 +++--- .../spring/field/SpringDecoratorTest.java | 21 +++--- .../DefaultComponentModelMapperTest.java | 13 ++-- .../test/defaultvalue/DefaultValueTest.java | 21 +++--- .../ap/test/dependency/GraphAnalyzerTest.java | 6 +- .../ap/test/dependency/OrderingTest.java | 17 ++--- .../destination/DestinationClassNameTest.java | 25 +++---- .../DestinationPackageNameTest.java | 19 ++--- .../AmbiguousAnnotatedFactoryTest.java | 7 +- .../ambiguousfactorymethod/FactoryTest.java | 7 +- .../ambiguousmapping/AmbiguousMapperTest.java | 11 +-- .../AnnotationNotFoundTest.java | 7 +- .../ErroneousMappingsTest.java | 11 +-- .../MisbalancedBracesTest.java | 7 +- .../ErroneousPropertyMappingTest.java | 13 ++-- .../typemismatch/ErroneousMappingsTest.java | 7 +- .../ap/test/exceptions/ExceptionTest.java | 62 +++++++++------- .../ap/test/factories/FactoryTest.java | 13 ++-- .../ParameterAssigmentFactoryTest.java | 15 ++-- .../qualified/QualifiedFactoryTest.java | 15 ++-- .../targettype/ProductTypeFactoryTest.java | 13 ++-- .../ap/test/fields/FieldsMappingTest.java | 13 ++-- .../mapstruct/ap/test/gem/ConstantTest.java | 6 +- .../mapstruct/ap/test/gem/EnumGemsTest.java | 6 +- .../ap/test/generics/GenericsTest.java | 11 +-- .../MapperWithGenericSuperClassTest.java | 11 +-- .../ap/test/ignore/IgnorePropertyTest.java | 17 ++--- .../ignore/expand/IgnorePropertyTest.java | 11 +-- .../ignore/inherit/IgnorePropertyTest.java | 15 ++-- .../imports/ConflictingTypesNamesTest.java | 25 +++---- .../test/imports/InnerClassesImportsTest.java | 25 +++---- .../DecoratorInAnotherPackageTest.java | 11 +-- ...ontainsCollectionWithExtendsBoundTest.java | 21 ++---- .../ap/test/inheritance/InheritanceTest.java | 17 ++--- .../attribute/AttributeInheritanceTest.java | 13 ++-- .../complex/ComplexInheritanceTest.java | 16 ++-- .../InheritedMappingMethodTest.java | 17 ++--- .../InheritFromConfigTest.java | 43 +++++------ .../multiple/CarMapperTest.java | 15 ++-- ...30DefaultCompileOptionFieldMapperTest.java | 23 +++--- ...330CompileOptionConstructorMapperTest.java | 23 +++--- .../Jsr330ConstructorMapperTest.java | 23 +++--- .../jsr330/field/Jsr330FieldMapperTest.java | 23 +++--- .../_default/SpringDefaultMapperTest.java | 23 +++--- ...ingCompileOptionConstructorMapperTest.java | 31 ++++---- .../SpringConstructorMapperTest.java | 31 ++++---- .../spring/field/SpringFieldMapperTest.java | 23 +++--- .../test/java8stream/StreamMappingTest.java | 43 +++++------ .../ap/test/java8stream/base/StreamsTest.java | 17 ++--- .../context/StreamWithContextTest.java | 11 +-- .../DefaultStreamImplementationTest.java | 35 ++++----- .../NoSetterStreamMappingTest.java | 11 +-- .../erroneous/ErroneousStreamMappingTest.java | 23 +++--- .../forged/ForgedStreamMappingTest.java | 23 +++--- .../StreamToNonIterableMappingTest.java | 13 ++-- .../java8stream/wildcard/WildCardTest.java | 21 +++--- .../ap/test/mapperconfig/ConfigTest.java | 17 ++--- .../mappingcomposition/CompositionTest.java | 7 +- .../mappingcontrol/MappingControlTest.java | 27 +++---- .../SuggestMostSimilarNameTest.java | 13 ++-- .../ap/test/naming/VariableNamingTest.java | 13 ++-- .../naming/spi/CustomNamingStrategyTest.java | 11 +-- ...DisablingNestedSimpleBeansMappingTest.java | 9 +-- .../nestedbeans/DottedErrorMessageTest.java | 23 +++--- .../MultipleForgedMethodsTest.java | 24 +++--- .../NestedSimpleBeansMappingTest.java | 17 ++--- .../ap/test/nestedbeans/RecursionTest.java | 13 ++-- .../NestedMappingsWithExceptionTest.java | 7 +- .../exclusions/ErroneousJavaInternalTest.java | 7 +- .../custom/ErroneousCustomExclusionTest.java | 7 +- .../mixed/AutomappingAndNestedTest.java | 25 +++---- .../NestedMappingMethodInvocationTest.java | 24 +++--- .../simple/SimpleNestedPropertiesTest.java | 26 +++---- .../exceptions/NestedExceptionTest.java | 7 +- .../parameter/NormalizingTest.java | 11 +-- .../NestedSourcePropertiesTest.java | 33 ++++----- .../ReversingNestedSourcePropertiesTest.java | 24 +++--- .../NestedProductPropertiesTest.java | 19 ++--- .../nonvoidsetter/NonVoidSettersTest.java | 11 +-- .../ap/test/nullcheck/NullCheckTest.java | 23 +++--- .../strategy/NullValueCheckTest.java | 17 ++--- .../NullValueMappingTest.java | 33 ++++----- .../NullValuePropertyMappingTest.java | 27 +++---- .../mapstruct/ap/test/oneway/OnewayTest.java | 15 ++-- .../test/references/ReferencedMapperTest.java | 19 ++--- ...ferencedMappersWithSameSimpleNameTest.java | 13 ++-- .../test/references/statics/StaticsTest.java | 23 ++---- .../InheritInverseConfigurationTest.java | 15 ++-- .../selection/generics/ConversionTest.java | 17 ++--- .../jaxb/JaxbFactoryMethodSelectionTest.java | 11 +-- .../jaxb/UnderscoreSelectionTest.java | 11 +-- .../array/GenericArrayTest.java | 15 ++-- .../methodgenerics/bounds/BoundsTest.java | 11 +-- .../multiple/MultipleTypeVarTest.java | 17 ++--- .../nestedgenerics/NestedGenericsTest.java | 13 ++-- .../methodgenerics/plain/PlainTest.java | 17 ++--- .../targettype/TargetTypeTest.java | 13 ++-- .../wildcards/WildCardTest.java | 13 +--- .../PrimitiveVsWrappedSelectionTest.java | 15 ++-- .../selection/qualifier/QualifierTest.java | 15 ++-- .../qualifier/errors/QualfierMessageTest.java | 13 ++-- .../qualifier/hybrid/HybridTest.java | 11 +-- .../iterable/IterableAndQualifiersTest.java | 9 +-- .../selection/qualifier/named/NamedTest.java | 15 ++-- .../resulttype/InheritanceSelectionTest.java | 29 ++++---- .../twosteperror/TwoStepMappingTest.java | 11 +-- .../selection/typegenerics/WildCardTest.java | 11 +-- .../selection/wildcards/WildCardTest.java | 13 ++-- .../test/source/constants/ConstantsTest.java | 15 ++-- .../source/constants/SourceConstantsTest.java | 26 +++---- .../java/JavaDefaultExpressionTest.java | 21 +++--- .../expressions/java/JavaExpressionTest.java | 21 +++--- .../IgnoreUnmappedSourcePropertiesTest.java | 7 +- .../ManySourceArgumentsTest.java | 29 ++++---- .../SourceToManyTargetPropertiesTest.java | 13 ++-- .../PresenceCheckTest.java | 17 ++--- .../spi/PresenceCheckMappingTest.java | 13 ++-- .../spi/PresenceCheckNestedObjectsTest.java | 10 +-- .../presencecheck/spi/PresenceCheckTest.java | 31 ++++---- .../targetthis/TargetThisMappingTest.java | 17 ++--- .../template/InheritConfigurationTest.java | 24 +++--- .../unmappedsource/UnmappedSourceTest.java | 17 ++--- .../unmappedtarget/UnmappedProductTest.java | 15 ++-- .../test/updatemethods/UpdateMethodsTest.java | 25 +++---- .../ManyArgumentsUpdateMappingTest.java | 7 +- .../selection/ExternalSelectionTest.java | 19 ++--- .../enum2enum/EnumToEnumMappingTest.java | 39 +++++----- .../EnumToEnumThrowExceptionMappingTest.java | 15 ++-- .../enum2string/EnumToStringMappingTest.java | 11 +-- .../erroneous/ErroneousEnumMappingTest.java | 9 +-- ...omUnexpectedValueMappingExceptionTest.java | 13 ++-- .../EnumNameTransformationStrategyTest.java | 17 ++--- .../spi/CustomEnumMappingStrategyTest.java | 17 ++--- ...ustomErroneousEnumMappingStrategyTest.java | 9 +-- .../string2enum/StringToEnumMappingTest.java | 11 +-- .../ap/test/verbose/VerboseTest.java | 73 +++---------------- .../ap/test/versioninfo/VersionInfoTest.java | 21 ++---- 370 files changed, 2147 insertions(+), 3298 deletions(-) diff --git a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactoryTest.java index 7bbc80e51b..646f898063 100755 --- a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactoryTest.java @@ -20,7 +20,7 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVisitor; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.ap.internal.util.JodaTimeConstants; import org.mapstruct.ap.testutil.IssueKey; diff --git a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java index f8cceee9c0..ce2f895e0f 100755 --- a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java @@ -20,7 +20,7 @@ import javax.lang.model.type.TypeVisitor; import javax.tools.Diagnostic; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.testutil.IssueKey; diff --git a/processor/src/test/java/org/mapstruct/ap/internal/model/source/SelectionParametersTest.java b/processor/src/test/java/org/mapstruct/ap/internal/model/source/SelectionParametersTest.java index 19e194a764..4c6e78e0c1 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/model/source/SelectionParametersTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/model/source/SelectionParametersTest.java @@ -24,7 +24,7 @@ import javax.lang.model.type.TypeVisitor; import javax.lang.model.type.WildcardType; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.ap.internal.util.TypeUtils; import static org.assertj.core.api.Assertions.assertThat; diff --git a/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java b/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java index 74272cbae3..9c68a1de45 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java @@ -6,12 +6,13 @@ package org.mapstruct.ap.internal.util; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.math.BigInteger; -import org.junit.Test; + +import org.junit.jupiter.api.Test; /** * @author Ciaran Liedeman diff --git a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java index 9d6dc88655..44fbb565ba 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java @@ -11,9 +11,9 @@ import java.util.Arrays; import java.util.Locale; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * @author Filip Hrisafov @@ -23,12 +23,12 @@ public class StringsTest { private static final Locale TURKEY_LOCALE = getTurkeyLocale(); private Locale defaultLocale; - @Before + @BeforeEach public void before() { defaultLocale = Locale.getDefault(); } - @After + @AfterEach public void after() { Locale.setDefault( defaultLocale ); } diff --git a/processor/src/test/java/org/mapstruct/ap/spi/util/IntrospectorUtilsTest.java b/processor/src/test/java/org/mapstruct/ap/spi/util/IntrospectorUtilsTest.java index 7114f7b9f8..84469f3f64 100644 --- a/processor/src/test/java/org/mapstruct/ap/spi/util/IntrospectorUtilsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/spi/util/IntrospectorUtilsTest.java @@ -5,9 +5,9 @@ */ package org.mapstruct.ap.spi.util; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Saheb Preet Singh diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractClassTest.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractClassTest.java index f510fd32e9..4e931b487b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractClassTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractClassTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.abstractclass; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for the generation of implementation of abstract base classes. @@ -31,10 +29,9 @@ Measurable.class, Holder.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class AbstractClassTest { - @Test + @ProcessorTest @IssueKey("64") public void shouldCreateImplementationOfAbstractMethod() { Source source = new Source(); @@ -42,7 +39,7 @@ public void shouldCreateImplementationOfAbstractMethod() { assertResult( SourceTargetMapper.INSTANCE.sourceToTarget( source ) ); } - @Test + @ProcessorTest @IssueKey("165") public void shouldCreateImplementationOfMethodFromSuper() { Source source = new Source(); @@ -50,7 +47,7 @@ public void shouldCreateImplementationOfMethodFromSuper() { assertResult( SourceTargetMapper.INSTANCE.sourceToTargetFromBaseMapper( source ) ); } - @Test + @ProcessorTest @IssueKey("165") public void shouldCreateImplementationOfMethodFromInterface() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/generics/GenericsHierarchyTest.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/generics/GenericsHierarchyTest.java index 282a8e8efb..fa483c9037 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/generics/GenericsHierarchyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/generics/GenericsHierarchyTest.java @@ -5,21 +5,17 @@ */ package org.mapstruct.ap.test.abstractclass.generics; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.Compiler; -import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Andreas Gudian * */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("644,687,688") @WithClasses({ AbstractAnimal.class, @@ -35,14 +31,11 @@ }) public class GenericsHierarchyTest { - @Test - // Disabled due to a bug in the Eclipse compiler (https://bugs.eclipse.org/bugs/show_bug.cgi?id=540101) + // Running only with the JDK compiler due to a bug in the Eclipse compiler + // (https://bugs.eclipse.org/bugs/show_bug.cgi?id=540101) // See https://github.com/mapstruct/mapstruct/issues/1553 and https://github.com/mapstruct/mapstruct/pull/1587 // for more information - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) public void determinesAnimalKeyGetter() { AbstractAnimal source = new Elephant(); @@ -56,7 +49,7 @@ public void determinesAnimalKeyGetter() { assertThat( target.getAnimalKey().typeParameterIsResolvedToKeyOfAllBeings() ).isFalse(); } - @Test + @ProcessorTest public void determinesKeyOfAllBeingsGetter() { AbstractHuman source = new Child(); @@ -69,7 +62,7 @@ public void determinesKeyOfAllBeingsGetter() { assertThat( target.getKeyOfAllBeings().typeParameterIsResolvedToKeyOfAllBeings() ).isTrue(); } - @Test + @ProcessorTest public void determinesItemCSourceSetter() { Target target = new Target(); @@ -81,7 +74,7 @@ public void determinesItemCSourceSetter() { assertThat( source.getKey().typeParameterIsResolvedToAnimalKey() ).isTrue(); } - @Test + @ProcessorTest public void determinesItemBSourceSetter() { Target target = new Target(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/AccessibilityTest.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/AccessibilityTest.java index 0ee789750c..d338cadedd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/AccessibilityTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/AccessibilityTest.java @@ -5,16 +5,14 @@ */ package org.mapstruct.ap.test.accessibility; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + import static java.lang.reflect.Modifier.isPrivate; import static java.lang.reflect.Modifier.isProtected; import static java.lang.reflect.Modifier.isPublic; -import static org.junit.Assert.assertTrue; - -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 static org.junit.jupiter.api.Assertions.assertTrue; /** * Test for different accessibility modifiers @@ -22,10 +20,9 @@ * @author Andreas Gudian */ @WithClasses({ Source.class, Target.class, DefaultSourceTargetMapperAbstr.class, DefaultSourceTargetMapperIfc.class }) -@RunWith( AnnotationProcessorTestRunner.class ) public class AccessibilityTest { - @Test + @ProcessorTest @IssueKey("103") public void testGeneratedModifiersFromAbstractClassAreCorrect() throws Exception { Class defaultFromAbstract = loadForMapper( DefaultSourceTargetMapperAbstr.class ); @@ -37,7 +34,7 @@ public void testGeneratedModifiersFromAbstractClassAreCorrect() throws Exception assertTrue( isDefault( modifiersFor( defaultFromAbstract, "defaultSourceToTarget" ) ) ); } - @Test + @ProcessorTest @IssueKey("103") public void testGeneratedModifiersFromInterfaceAreCorrect() throws Exception { Class defaultFromIfc = loadForMapper( DefaultSourceTargetMapperIfc.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java index 44aeb033fa..3cd62a8b4f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java @@ -5,16 +5,14 @@ */ package org.mapstruct.ap.test.accessibility.referenced; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.accessibility.referenced.a.ReferencedMapperDefaultOther; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; /** @@ -23,13 +21,12 @@ * @author Sjaak Derksen */ @WithClasses( { Source.class, Target.class, ReferencedSource.class, ReferencedTarget.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class ReferencedAccessibilityTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @IssueKey("206") @WithClasses({ SourceTargetMapperPrivate.class, ReferencedMapperPrivate.class }) @ExpectedCompilationOutcome( @@ -46,17 +43,17 @@ public void shouldNotBeAbleToAccessPrivateMethodInReferenced() { generatedSource.addComparisonToFixtureFor( SourceTargetMapperPrivate.class ); } - @Test + @ProcessorTest @IssueKey( "206" ) @WithClasses( { SourceTargetMapperDefaultSame.class, ReferencedMapperDefaultSame.class } ) public void shouldBeAbleToAccessDefaultMethodInReferencedInSamePackage() { } - @Test + @ProcessorTest @IssueKey( "206" ) @WithClasses( { SourceTargetMapperProtected.class, ReferencedMapperProtected.class } ) public void shouldBeAbleToAccessProtectedMethodInReferencedInSamePackage() { } - @Test + @ProcessorTest @IssueKey("206") @WithClasses({ SourceTargetMapperDefaultOther.class, ReferencedMapperDefaultOther.class }) @ExpectedCompilationOutcome( @@ -73,12 +70,12 @@ public void shouldNotBeAbleToAccessDefaultMethodInReferencedInOtherPackage() { generatedSource.addComparisonToFixtureFor( SourceTargetMapperDefaultOther.class ); } - @Test + @ProcessorTest @IssueKey( "206" ) @WithClasses( { AbstractSourceTargetMapperProtected.class, SourceTargetmapperProtectedBase.class } ) public void shouldBeAbleToAccessProtectedMethodInBase() { } - @Test + @ProcessorTest @IssueKey("206") @WithClasses({ AbstractSourceTargetMapperPrivate.class, SourceTargetmapperPrivateBase.class }) @ExpectedCompilationOutcome( diff --git a/processor/src/test/java/org/mapstruct/ap/test/array/ArrayMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/array/ArrayMappingTest.java index 39adff4eb7..07113f7cf4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/array/ArrayMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/array/ArrayMappingTest.java @@ -5,31 +5,28 @@ */ package org.mapstruct.ap.test.array; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; import java.util.List; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.array._target.ScientistDto; import org.mapstruct.ap.test.array.source.Scientist; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; + @WithClasses( { Scientist.class, ScientistDto.class, ScienceMapper.class } ) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("108") public class ArrayMappingTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource() + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() .addComparisonToFixtureFor( ScienceMapper.class ); - @Test + @ProcessorTest public void shouldCopyArraysInBean() { Scientist source = new Scientist("Bob"); @@ -44,7 +41,7 @@ public void shouldCopyArraysInBean() { assertThat( dto.publicPublications ).containsOnly( "public the Lancet", "public Nature" ); } - @Test + @ProcessorTest public void shouldForgeMappingForIntToString() { Scientist source = new Scientist("Bob"); @@ -58,7 +55,7 @@ public void shouldForgeMappingForIntToString() { assertThat( dto.publicPublicationYears ).containsOnly( 1994, 1998 ); } - @Test + @ProcessorTest public void shouldMapArrayToArray() { ScientistDto[] dtos = ScienceMapper.INSTANCE .scientistsToDtos( new Scientist[]{ new Scientist( "Bob" ), new Scientist( "Larry" ) } ); @@ -67,7 +64,7 @@ public void shouldMapArrayToArray() { assertThat( dtos ).extracting( "name" ).containsOnly( "Bob", "Larry" ); } - @Test + @ProcessorTest public void shouldMapListToArray() { ScientistDto[] dtos = ScienceMapper.INSTANCE .scientistsToDtos( Arrays.asList( new Scientist( "Bob" ), new Scientist( "Larry" ) ) ); @@ -76,7 +73,7 @@ public void shouldMapListToArray() { assertThat( dtos ).extracting( "name" ).containsOnly( "Bob", "Larry" ); } - @Test + @ProcessorTest public void shouldMapArrayToList() { List dtos = ScienceMapper.INSTANCE .scientistsToDtosAsList( new Scientist[]{ new Scientist( "Bob" ), new Scientist( "Larry" ) } ); @@ -85,7 +82,7 @@ public void shouldMapArrayToList() { assertThat( dtos ).extracting( "name" ).containsOnly( "Bob", "Larry" ); } - @Test + @ProcessorTest public void shouldMapArrayToArrayExistingSmallerSizedTarget() { ScientistDto[] existingTarget = new ScientistDto[]{ new ScientistDto( "Jim" ) }; @@ -98,7 +95,7 @@ public void shouldMapArrayToArrayExistingSmallerSizedTarget() { assertThat( target ).extracting( "name" ).containsOnly( "Bob" ); } - @Test + @ProcessorTest public void shouldMapArrayToArrayExistingEqualSizedTarget() { ScientistDto[] existingTarget = new ScientistDto[]{ new ScientistDto( "Jim" ), new ScientistDto( "Bart" ) }; @@ -111,7 +108,7 @@ public void shouldMapArrayToArrayExistingEqualSizedTarget() { assertThat( target ).extracting( "name" ).containsOnly( "Bob", "Larry" ); } - @Test + @ProcessorTest public void shouldMapArrayToArrayExistingLargerSizedTarget() { ScientistDto[] existingTarget = @@ -125,7 +122,7 @@ public void shouldMapArrayToArrayExistingLargerSizedTarget() { assertThat( target ).extracting( "name" ).containsOnly( "Bob", "Larry", "John" ); } - @Test + @ProcessorTest public void shouldMapTargetToNullWhenNullSource() { // TODO: What about existing target? @@ -139,7 +136,7 @@ public void shouldMapTargetToNullWhenNullSource() { } @IssueKey("534") - @Test + @ProcessorTest public void shouldMapBooleanWhenReturnDefault() { boolean[] existingTarget = new boolean[]{true}; @@ -151,7 +148,7 @@ public void shouldMapBooleanWhenReturnDefault() { assertThat( ScienceMapper.INSTANCE.nvmMapping( null ) ).isEmpty(); } - @Test + @ProcessorTest public void shouldMapShortWhenReturnDefault() { short[] existingTarget = new short[]{ 5 }; short[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); @@ -160,7 +157,7 @@ public void shouldMapShortWhenReturnDefault() { assertThat( existingTarget ).containsOnly( new short[] { 0 } ); } - @Test + @ProcessorTest public void shouldMapCharWhenReturnDefault() { char[] existingTarget = new char[]{ 'a' }; char[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); @@ -169,7 +166,7 @@ public void shouldMapCharWhenReturnDefault() { assertThat( existingTarget ).containsOnly( new char[] { 0 } ); } - @Test + @ProcessorTest public void shouldMapIntWhenReturnDefault() { int[] existingTarget = new int[]{ 5 }; int[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); @@ -178,7 +175,7 @@ public void shouldMapIntWhenReturnDefault() { assertThat( existingTarget ).containsOnly( 0 ); } - @Test + @ProcessorTest public void shouldMapLongWhenReturnDefault() { long[] existingTarget = new long[]{ 5L }; long[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); @@ -187,7 +184,7 @@ public void shouldMapLongWhenReturnDefault() { assertThat( existingTarget ).containsOnly( 0L ); } - @Test + @ProcessorTest public void shouldMapFloatWhenReturnDefault() { float[] existingTarget = new float[]{ 3.1f }; float[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); @@ -196,7 +193,7 @@ public void shouldMapFloatWhenReturnDefault() { assertThat( existingTarget ).containsOnly( 0.0f ); } - @Test + @ProcessorTest public void shouldMapDoubleWhenReturnDefault() { double[] existingTarget = new double[]{ 5.0d }; double[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); @@ -205,21 +202,21 @@ public void shouldMapDoubleWhenReturnDefault() { assertThat( existingTarget ).containsOnly( 0.0d ); } - @Test + @ProcessorTest public void shouldVoidMapIntWhenReturnNull() { long[] existingTarget = new long[]{ 5L }; ScienceMapper.INSTANCE.nvmMappingVoidReturnNull( null, existingTarget ); assertThat( existingTarget ).containsOnly( 5L ); } - @Test + @ProcessorTest public void shouldVoidMapIntWhenReturnDefault() { long[] existingTarget = new long[]{ 5L }; ScienceMapper.INSTANCE.nvmMappingVoidReturnDefault( null, existingTarget ); assertThat( existingTarget ).containsOnly( 0L ); } - @Test + @ProcessorTest @IssueKey( "999" ) public void shouldNotContainFQNForStringArray() { generatedSource.forMapper( ScienceMapper.class ).content().doesNotContain( "java.lang.String[]" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bool/BooleanMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/bool/BooleanMappingTest.java index 6ca327cce4..9efb09b1e6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bool/BooleanMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bool/BooleanMappingTest.java @@ -5,12 +5,10 @@ */ package org.mapstruct.ap.test.bool; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ Person.class, @@ -19,10 +17,9 @@ PersonMapper.class, YesNoMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class BooleanMappingTest { - @Test + @ProcessorTest public void shouldMapBooleanPropertyWithIsPrefixedGetter() { //given Person person = new Person(); @@ -35,7 +32,7 @@ public void shouldMapBooleanPropertyWithIsPrefixedGetter() { assertThat( personDto.getMarried() ).isEqualTo( "true" ); } - @Test + @ProcessorTest public void shouldMapBooleanPropertyPreferringGetPrefixedGetterOverIsPrefixedGetter() { //given Person person = new Person(); @@ -48,7 +45,7 @@ public void shouldMapBooleanPropertyPreferringGetPrefixedGetterOverIsPrefixedGet assertThat( personDto.getEngaged() ).isEqualTo( "true" ); } - @Test + @ProcessorTest public void shouldMapBooleanPropertyWithPropertyMappingMethod() { // given Person person = new Person(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java index 95509dc0ce..5198394eb1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1005/Issue1005Test.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.bugs._1005; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("1005") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ AbstractEntity.class, HasKey.class, @@ -29,7 +26,7 @@ public class Issue1005Test { @WithClasses(Issue1005ErroneousAbstractResultTypeMapper.class) - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = Issue1005ErroneousAbstractResultTypeMapper.class, @@ -41,7 +38,7 @@ public void shouldFailDueToAbstractResultType() { } @WithClasses(Issue1005ErroneousAbstractReturnTypeMapper.class) - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = Issue1005ErroneousAbstractReturnTypeMapper.class, @@ -54,7 +51,7 @@ public void shouldFailDueToAbstractReturnType() { } @WithClasses(Issue1005ErroneousInterfaceResultTypeMapper.class) - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = Issue1005ErroneousInterfaceResultTypeMapper.class, @@ -66,7 +63,7 @@ public void shouldFailDueToInterfaceResultType() { } @WithClasses(Issue1005ErroneousInterfaceReturnTypeMapper.class) - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = Issue1005ErroneousInterfaceReturnTypeMapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java index 037ac64069..c2a87b769a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1029/Issue1029Test.java @@ -7,26 +7,23 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Verifies that read-only properties can be explicitly mentioned as {@code ignored=true} without raising an error. * * @author Andreas Gudian */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(ErroneousIssue1029Mapper.class) @IssueKey("1029") public class Issue1029Test { - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(kind = Kind.WARNING, line = 26, type = ErroneousIssue1029Mapper.class, message = "Unmapped target properties: \"knownProp, lastUpdated, computedMapping\"."), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1061/Issue1061Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1061/Issue1061Test.java index fe0f463c4e..b060d9727f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1061/Issue1061Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1061/Issue1061Test.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.bugs._1061; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("1061") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(SourceTargetMapper.class) public class Issue1061Test { - @Test + @ProcessorTest public void shouldCompile() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Test.java index 623be34f04..5619b64175 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Test.java @@ -5,17 +5,16 @@ */ package org.mapstruct.ap.test.bugs._1111; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; + import org.mapstruct.ap.test.bugs._1111.Issue1111Mapper.Source; import org.mapstruct.ap.test.bugs._1111.Issue1111Mapper.Target; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -23,10 +22,9 @@ */ @IssueKey( "1111") @WithClasses({Issue1111Mapper.class}) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue1111Test { - @Test + @ProcessorTest public void shouldCompile() { List> source = Arrays.asList( Arrays.asList( new Source() ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1124/Issue1124Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1124/Issue1124Test.java index 1516c66496..aecc0a3a15 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1124/Issue1124Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1124/Issue1124Test.java @@ -5,26 +5,23 @@ */ package org.mapstruct.ap.test.bugs._1124; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._1124.Issue1124Mapper.DTO; import org.mapstruct.ap.test.bugs._1124.Issue1124Mapper.Entity; import org.mapstruct.ap.test.bugs._1124.Issue1124Mapper.MappingContext; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Andreas Gudian */ @IssueKey("1124") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(Issue1124Mapper.class) public class Issue1124Test { - @Test + @ProcessorTest public void nestedPropertyWithContextCompiles() { Entity entity = new Entity(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1130/Issue1130Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1130/Issue1130Test.java index 2024a83933..d0f8a1d3a4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1130/Issue1130Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1130/Issue1130Test.java @@ -5,19 +5,17 @@ */ package org.mapstruct.ap.test.bugs._1130; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.TargetType; import org.mapstruct.ap.test.bugs._1130.Issue1130Mapper.ADto; import org.mapstruct.ap.test.bugs._1130.Issue1130Mapper.AEntity; import org.mapstruct.ap.test.bugs._1130.Issue1130Mapper.BEntity; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; +import static org.assertj.core.api.Assertions.assertThat; + /** * Tests that when calling an update method for a previously null property, the factory method is called even if that * factory method has a {@link TargetType} annotation. @@ -25,10 +23,9 @@ * @author Andreas Gudian */ @IssueKey("1130") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(Issue1130Mapper.class) public class Issue1130Test { - @Test + @ProcessorTest public void factoryMethodWithTargetTypeInUpdateMethods() { AEntity aEntity = new AEntity(); aEntity.setB( new BEntity() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Test.java index 04cfa25d44..7a8c2797d3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Test.java @@ -7,18 +7,15 @@ import java.util.ArrayList; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1131") @WithClasses({ Issue1131Mapper.class, @@ -28,7 +25,7 @@ }) public class Issue1131Test { - @Test + @ProcessorTest public void shouldUseCreateWithSourceNested() { Source source = new Source(); @@ -38,6 +35,7 @@ public void shouldUseCreateWithSourceNested() { Target target = new Target(); + Issue1131Mapper.CALLED_METHODS.clear(); Issue1131Mapper.INSTANCE.merge( source, target ); assertThat( target.getNested() ).isNotNull(); @@ -49,7 +47,7 @@ public void shouldUseCreateWithSourceNested() { ); } - @Test + @ProcessorTest public void shouldUseContextObjectFactory() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1148/Issue1148Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1148/Issue1148Test.java index 1c6ff15e53..a698cfbfc6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1148/Issue1148Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1148/Issue1148Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1148; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -20,11 +18,10 @@ Entity.class, Issue1148Mapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1148") public class Issue1148Test { - @Test + @ProcessorTest public void shouldNotUseSameMethodForDifferentMappingsNestedSource() { Entity.Dto dto = new Entity.Dto(); dto.nestedDto = new Entity.NestedDto( 30 ); @@ -35,7 +32,7 @@ public void shouldNotUseSameMethodForDifferentMappingsNestedSource() { assertThat( entity.getId2() ).isEqualTo( 40 ); } - @Test + @ProcessorTest public void shouldNotUseSameMethodForDifferentMappingsNestedTarget() { Entity.Dto dto = new Entity.Dto(); dto.recipientId = 10; @@ -51,7 +48,7 @@ public void shouldNotUseSameMethodForDifferentMappingsNestedTarget() { assertThat( entity.getSender().nestedClient.id ).isEqualTo( 20 ); } - @Test + @ProcessorTest public void shouldNotUseSameMethodForDifferentMappingsSymmetric() { Entity.Dto dto = new Entity.Dto(); dto.sameLevel = new Entity.ClientDto(new Entity.NestedDto( 30 )); @@ -67,7 +64,7 @@ public void shouldNotUseSameMethodForDifferentMappingsSymmetric() { assertThat( entity.client2.nestedClient.id ).isEqualTo( 40 ); } - @Test + @ProcessorTest public void shouldNotUseSameMethodForDifferentMappingsHalfSymmetric() { Entity.Dto dto = new Entity.Dto(); dto.level = new Entity.ClientDto(new Entity.NestedDto( 80 )); @@ -81,7 +78,7 @@ public void shouldNotUseSameMethodForDifferentMappingsHalfSymmetric() { assertThat( entity.nested2.id ).isEqualTo( 90 ); } - @Test + @ProcessorTest public void shouldNotUseSameMethodForDifferentMappingsNestedSourceMultiple() { Entity.Dto dto1 = new Entity.Dto(); dto1.nestedDto = new Entity.NestedDto( 30 ); @@ -93,7 +90,7 @@ public void shouldNotUseSameMethodForDifferentMappingsNestedSourceMultiple() { assertThat( entity.getId2() ).isEqualTo( 40 ); } - @Test + @ProcessorTest public void shouldNotUseSameMethodForDifferentMappingsNestedTargetMultiple() { Entity.Dto dto1 = new Entity.Dto(); dto1.recipientId = 10; @@ -110,7 +107,7 @@ public void shouldNotUseSameMethodForDifferentMappingsNestedTargetMultiple() { assertThat( entity.getSender().nestedClient.id ).isEqualTo( 20 ); } - @Test + @ProcessorTest public void shouldNotUseSameMethodForDifferentMappingsSymmetricMultiple() { Entity.Dto dto1 = new Entity.Dto(); dto1.sameLevel = new Entity.ClientDto(new Entity.NestedDto( 30 )); @@ -127,7 +124,7 @@ public void shouldNotUseSameMethodForDifferentMappingsSymmetricMultiple() { assertThat( entity.client2.nestedClient.id ).isEqualTo( 40 ); } - @Test + @ProcessorTest public void shouldNotUseSameMethodForDifferentMappingsHalfSymmetricMultiple() { Entity.Dto dto1 = new Entity.Dto(); dto1.level = new Entity.ClientDto(new Entity.NestedDto( 80 )); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java index 015268b53e..5ceb1bb715 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1153/Issue1153Test.java @@ -5,19 +5,16 @@ */ package org.mapstruct.ap.test.bugs._1153; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(ErroneousIssue1153Mapper.class) @IssueKey("1153") public class Issue1153Test { @@ -40,7 +37,7 @@ public class Issue1153Test { message = "Unknown property \"writable2\" in type ErroneousIssue1153Mapper.Target.NestedTarget " + "for target name \"nestedTarget2.writable2\". Did you mean \"nestedTarget2.writable\"?") }) - @Test + @ProcessorTest public void shouldReportErrorsCorrectly() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1155/Issue1155Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1155/Issue1155Test.java index 553323b32b..9a6bb40ed1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1155/Issue1155Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1155/Issue1155Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1155; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -20,11 +18,10 @@ Entity.class, Issue1155Mapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1155") public class Issue1155Test { - @Test + @ProcessorTest public void shouldCompile() { Entity.Dto dto = new Entity.Dto(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java index b93cd6d069..7740662632 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1159/Issue1159Test.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.bugs._1159; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.Compiler; -import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; import static org.assertj.core.api.Assertions.assertThat; @@ -24,7 +21,6 @@ * @author Filip Hrisafov */ @IssueKey("1159") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1159Mapper.class, }) @@ -34,12 +30,8 @@ ) public class Issue1159Test { - @Test + @ProcessorTest(Compiler.JDK) // The warning is not present in the Eclipse compilation for some reason - @DisabledOnCompiler({ - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - }) @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { @Diagnostic( kind = javax.tools.Diagnostic.Kind.WARNING, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1164/Issue1164Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1164/Issue1164Test.java index 7fa5a343ad..6db73113b6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1164/Issue1164Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1164/Issue1164Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1164; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -20,11 +18,10 @@ GenericHolder.class, SourceTargetMapper.class } ) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey( "1164" ) public class Issue1164Test { - @Test + @ProcessorTest public void shouldCompile() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1170/AdderTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1170/AdderTest.java index b68884e0e3..88576ba711 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1170/AdderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1170/AdderTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.bugs._1170; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; import org.assertj.core.api.ListAssert; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._1170._target.Target; import org.mapstruct.ap.test.bugs._1170.source.Source; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Cornelius Dirmeier @@ -27,11 +25,10 @@ AdderSourceTargetMapper.class, PetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class AdderTest { @IssueKey("1170") - @Test + @ProcessorTest public void testWildcardAdder() { Source source = new Source(); source.addWithoutWildcard( "mouse" ); @@ -51,7 +48,7 @@ public void testWildcardAdder() { } @IssueKey("1170") - @Test + @ProcessorTest public void testWildcardAdderTargetToSource() { Target target = new Target(); target.addWithoutWildcard( 2L ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1180/Issue1180Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1180/Issue1180Test.java index 32de67728e..865816d83e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1180/Issue1180Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1180/Issue1180Test.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.bugs._1180; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Sjaak Derksen @@ -23,11 +21,10 @@ SharedConfig.class, ErroneousIssue1180Mapper.class } ) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey( "1180" ) public class Issue1180Test { - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = ErroneousIssue1180Mapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1215/Issue1215Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1215/Issue1215Test.java index c18a858d47..e3c08488a0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1215/Issue1215Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1215/Issue1215Test.java @@ -5,16 +5,14 @@ */ package org.mapstruct.ap.test.bugs._1215; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._1215.dto.EntityDTO; import org.mapstruct.ap.test.bugs._1215.entity.AnotherTag; import org.mapstruct.ap.test.bugs._1215.entity.Entity; import org.mapstruct.ap.test.bugs._1215.entity.Tag; import org.mapstruct.ap.test.bugs._1215.mapper.Issue1215Mapper; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -27,10 +25,9 @@ Issue1215Mapper.class } ) @IssueKey( "1215" ) -@RunWith( AnnotationProcessorTestRunner.class ) public class Issue1215Test { - @Test + @ProcessorTest public void shouldCompile() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1227/Issue1227Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1227/Issue1227Test.java index e4612e9d87..f98f6de398 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1227/Issue1227Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1227/Issue1227Test.java @@ -5,24 +5,21 @@ */ package org.mapstruct.ap.test.bugs._1227; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("1227") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1227Mapper.class, ThreadDto.class }) public class Issue1227Test { - @Test + @ProcessorTest public void shouldCompile() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java index c7e9c42f95..f27051670c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.bugs._1242; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; +import static org.assertj.core.api.Assertions.assertThat; + /** * Tests that if multiple factory methods are applicable but only one of them has a source parameter, the one with the * source param is chosen. @@ -24,7 +22,6 @@ * @author Andreas Gudian */ @IssueKey("1242") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1242Mapper.class, SourceA.class, @@ -34,7 +31,7 @@ TargetFactories.class }) public class Issue1242Test { - @Test + @ProcessorTest public void factoryMethodWithSourceParamIsChosen() { SourceA sourceA = new SourceA(); sourceA.setB( new SourceB() ); @@ -51,7 +48,7 @@ public void factoryMethodWithSourceParamIsChosen() { assertThat( targetA.getB().getPassedViaConstructor() ).isEqualTo( "created by factory" ); } - @Test + @ProcessorTest @WithClasses(ErroneousIssue1242MapperMultipleSources.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1244/Issue1244Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1244/Issue1244Test.java index ebb27fc202..231ff67334 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1244/Issue1244Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1244/Issue1244Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1244; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; import static org.assertj.core.api.Assertions.assertThat; @@ -18,11 +16,10 @@ * @author Filip Hrisafov */ @IssueKey("1244") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses( SizeMapper.class ) public class Issue1244Test { - @Test + @ProcessorTest public void properlyCreatesMapperWithSizeAsParameterName() { SizeMapper.SizeHolder sizeHolder = new SizeMapper.SizeHolder(); sizeHolder.setSize( "size" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1247/Issue1247Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1247/Issue1247Test.java index 884fb7fdb2..1dd12d6e23 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1247/Issue1247Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1247/Issue1247Test.java @@ -8,11 +8,9 @@ import java.util.Arrays; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -20,7 +18,6 @@ * @author Filip Hrisafov */ @IssueKey("1247") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1247Mapper.class, DtoIn.class, @@ -33,7 +30,7 @@ }) public class Issue1247Test { - @Test + @ProcessorTest public void shouldCorrectlyUseMappings() { DtoIn in = new DtoIn( "data", "data2" ); @@ -48,7 +45,7 @@ public void shouldCorrectlyUseMappings() { assertThat( out.getInternal().getInternalData().getList() ).containsExactly( "first", "second" ); } - @Test + @ProcessorTest public void shouldCorrectlyUseMappingsWithConstantsExpressionsAndDefaults() { DtoIn in = new DtoIn( "data", "data2" ); @@ -68,7 +65,7 @@ public void shouldCorrectlyUseMappingsWithConstantsExpressionsAndDefaults() { assertThat( out.getInternal().getInternalData().getDefaultValue() ).isEqualTo( "data2" ); } - @Test + @ProcessorTest public void shouldCorrectlyUseMappingsWithConstantsExpressionsAndUseDefault() { DtoIn in = new DtoIn( "data", null ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1255/Issue1255Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1255/Issue1255Test.java index 4168a75b79..9d27fdf681 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1255/Issue1255Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1255/Issue1255Test.java @@ -5,19 +5,17 @@ */ package org.mapstruct.ap.test.bugs._1255; -import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * * @author Sjaak Derksen */ @IssueKey("1255") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ AbstractA.class, SomeA.class, @@ -26,7 +24,7 @@ SomeMapperConfig.class}) public class Issue1255Test { - @Test + @ProcessorTest public void shouldMapSomeBToSomeAWithoutField1() { SomeB someB = new SomeB(); someB.setField1( "value1" ); @@ -40,7 +38,7 @@ public void shouldMapSomeBToSomeAWithoutField1() { assertThat( someA.getField2() ).isEqualTo( someB.getField2() ); } - @Test + @ProcessorTest public void shouldMapSomeAToSomeB() { SomeA someA = new SomeA(); someA.setField1( "value1" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1269/Issue1269Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1269/Issue1269Test.java index 7f44ea012b..668b9e5110 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1269/Issue1269Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1269/Issue1269Test.java @@ -8,8 +8,6 @@ import java.util.Arrays; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._1269.dto.VehicleDto; import org.mapstruct.ap.test.bugs._1269.dto.VehicleImageDto; import org.mapstruct.ap.test.bugs._1269.dto.VehicleInfoDto; @@ -18,8 +16,8 @@ import org.mapstruct.ap.test.bugs._1269.model.VehicleImage; import org.mapstruct.ap.test.bugs._1269.model.VehicleTypeInfo; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -27,7 +25,6 @@ * @author Filip Hrisafov */ @IssueKey( "1269" ) -@RunWith( AnnotationProcessorTestRunner.class ) @WithClasses( { VehicleDto.class, VehicleImageDto.class, @@ -39,7 +36,7 @@ } ) public class Issue1269Test { - @Test + @ProcessorTest public void shouldMapNestedPropertiesCorrectly() { VehicleTypeInfo sourceTypeInfo = new VehicleTypeInfo( "Opel", "Corsa", 3 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/Issue1273Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/Issue1273Test.java index e12edd50a2..9874b33436 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/Issue1273Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/Issue1273Test.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.bugs._1273; -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; +import static org.assertj.core.api.Assertions.assertThat; + @IssueKey( "1273" ) -@RunWith( AnnotationProcessorTestRunner.class ) @WithClasses( { EntityMapperReturnDefault.class, EntityMapperReturnNull.class, Dto.class, Entity.class } ) public class Issue1273Test { - @Test + @ProcessorTest public void shouldCorrectlyMapCollectionWithNullValueMappingStrategyReturnDefault() { EntityMapperReturnDefault entityMapper = Mappers.getMapper( EntityMapperReturnDefault.class ); @@ -29,7 +26,7 @@ public void shouldCorrectlyMapCollectionWithNullValueMappingStrategyReturnDefaul assertThat( dto.getLongs() ).isNotNull(); } - @Test + @ProcessorTest public void shouldCorrectlyMapCollectionWithNullValueMappingStrategyReturnNull() { EntityMapperReturnNull entityMapper = Mappers.getMapper( EntityMapperReturnNull.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java index 0d4fef255a..1879c9398b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1283/Issue1283Test.java @@ -5,27 +5,24 @@ */ package org.mapstruct.ap.test.bugs._1283; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("1283") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Source.class, Target.class }) public class Issue1283Test { - @Test + @ProcessorTest @WithClasses(ErroneousInverseTargetHasNoSuitableConstructorMapper.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -40,7 +37,7 @@ public class Issue1283Test { public void inheritInverseConfigurationReturnTypeHasNoSuitableConstructor() { } - @Test + @ProcessorTest @WithClasses(ErroneousTargetHasNoSuitableConstructorMapper.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1320/Issue1320Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1320/Issue1320Test.java index 2b6b918bb3..b84fe2d8e6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1320/Issue1320Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1320/Issue1320Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1320; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,14 +15,13 @@ * @author Filip Hrisafov */ @IssueKey("1320") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1320Mapper.class, Target.class }) public class Issue1320Test { - @Test + @ProcessorTest public void shouldCreateDeepNestedConstantsCorrectly() { Target target = Issue1320Mapper.INSTANCE.map( 10 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1338/Issue1338Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1338/Issue1338Test.java index 83c5212d23..924f5e8c86 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1338/Issue1338Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1338/Issue1338Test.java @@ -7,18 +7,15 @@ import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1338") @WithClasses({ Issue1338Mapper.class, @@ -27,7 +24,7 @@ }) public class Issue1338Test { - @Test + @ProcessorTest public void shouldCorrectlyUseAdder() { Source source = new Source(); source.setProperties( Arrays.asList( "first", "second" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1339/Issue1339Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1339/Issue1339Test.java index 5846860098..fa1caff745 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1339/Issue1339Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1339/Issue1339Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1339; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -20,11 +18,10 @@ Issue1339Mapper.class, Callback.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1339") public class Issue1339Test { - @Test + @ProcessorTest public void shouldCompile() { Issue1339Mapper.Source source = new Issue1339Mapper.Source(); source.field = "test"; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1340/Issue1340Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1340/Issue1340Test.java index d4fdac257d..3135dfff67 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1340/Issue1340Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1340/Issue1340Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1340; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -18,10 +16,9 @@ Issue1340Mapper.class }) @IssueKey("1340") -@RunWith(AnnotationProcessorTestRunner.class) public class Issue1340Test { - @Test + @ProcessorTest public void shouldCompile() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1345/Issue1345Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1345/Issue1345Test.java index c5ad1c3030..7be9b0edc1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1345/Issue1345Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1345/Issue1345Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1345; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Sjaak Derksen @@ -18,10 +16,9 @@ Issue1345Mapper.class }) @IssueKey("1345") -@RunWith(AnnotationProcessorTestRunner.class) public class Issue1345Test { - @Test + @ProcessorTest public void shouldCompile() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1353/Issue1353Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1353/Issue1353Test.java index 953ecf8d17..4398d6ebcd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1353/Issue1353Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1353/Issue1353Test.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.bugs._1353; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -20,7 +18,6 @@ * @author Jeffrey Smyth */ @IssueKey ("1353") -@RunWith (AnnotationProcessorTestRunner.class) @WithClasses ({ Issue1353Mapper.class, Source.class, @@ -28,7 +25,7 @@ }) public class Issue1353Test { - @Test + @ProcessorTest @ExpectedCompilationOutcome ( value = CompilationResult.SUCCEEDED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1359/Issue1359Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1359/Issue1359Test.java index aa49cc59fe..a412cd19f6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1359/Issue1359Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1359/Issue1359Test.java @@ -8,11 +8,9 @@ import java.util.HashSet; import java.util.Set; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.InstanceOfAssertFactories.ITERABLE; @@ -25,11 +23,10 @@ Source.class, Target.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) @IssueKey( "1359" ) public class Issue1359Test { - @Test + @ProcessorTest public void shouldCompile() { Target target = new Target(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1375/Issue1375Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1375/Issue1375Test.java index d08d8d3d36..9dcb9655f4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1375/Issue1375Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1375/Issue1375Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1375; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -21,11 +19,10 @@ Source.class, Issue1375Mapper.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) @IssueKey( "1375" ) public class Issue1375Test { - @Test + @ProcessorTest public void shouldGenerateCorrectMapperWhenIntermediaryReadAccessorIsMissing() { Target target = Issue1375Mapper.INSTANCE.map( new Source( "test value" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java index 21d2f5015b..757436a0f4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1395; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -20,11 +18,10 @@ Source.class, Target.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) @IssueKey( "1395" ) public class Issue1395Test { - @Test + @ProcessorTest public void shouldGenerateValidCode() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1425/Issue1425Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1425/Issue1425Test.java index 25a148e474..8c5700772f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1425/Issue1425Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1425/Issue1425Test.java @@ -6,11 +6,9 @@ package org.mapstruct.ap.test.bugs._1425; import org.joda.time.LocalDate; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -22,11 +20,10 @@ Source.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1425") public class Issue1425Test { - @Test + @ProcessorTest public void shouldTestMappingLocalDates() { Source source = new Source(); source.setValue( LocalDate.parse( "2018-04-18" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1435/Issue1435Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1435/Issue1435Test.java index ade951f2b1..2fa173cac1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1435/Issue1435Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1435/Issue1435Test.java @@ -5,15 +5,12 @@ */ package org.mapstruct.ap.test.bugs._1435; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1435") @WithClasses({ Config.class, @@ -22,7 +19,7 @@ OutObject.class, }) public class Issue1435Test { - @Test + @ProcessorTest public void mustNotSetListToNull() { InObject source = new InObject( "Rainbow Dash" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Test.java index 9ba0a41cf3..d112df0355 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1453/Issue1453Test.java @@ -7,12 +7,10 @@ import java.util.Arrays; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -21,7 +19,6 @@ * @author Filip Hrisafov */ @IssueKey("1453") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Auction.class, AuctionDto.class, @@ -31,10 +28,10 @@ }) public class Issue1453Test { - @Rule - public GeneratedSource source = new GeneratedSource().addComparisonToFixtureFor( Issue1453Mapper.class ); + @RegisterExtension + final GeneratedSource source = new GeneratedSource().addComparisonToFixtureFor( Issue1453Mapper.class ); - @Test + @ProcessorTest public void shouldGenerateCorrectCode() { AuctionDto target = Issue1453Mapper.INSTANCE.map( new Auction( diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1457/Issue1457Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1457/Issue1457Test.java index 910a1e7280..de1160771a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1457/Issue1457Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1457/Issue1457Test.java @@ -5,15 +5,13 @@ */ package org.mapstruct.ap.test.bugs._1457; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -21,7 +19,6 @@ SourceBook.class, TargetBook.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1457") public class Issue1457Test { @@ -29,7 +26,7 @@ public class Issue1457Test { private String authorFirstName; private String authorLastName; - @Before + @BeforeEach public void setup() { sourceBook = new SourceBook(); sourceBook.setIsbn( "3453146972" ); @@ -39,7 +36,7 @@ public void setup() { authorLastName = "Adams"; } - @Test + @ProcessorTest @WithClasses({ BookMapper.class }) @@ -67,7 +64,7 @@ public void testMapperWithMatchingParameterNames() { assertThat( targetBook.isAfterMappingWithDifferentVariableName() ).isFalse(); } - @Test + @ProcessorTest @WithClasses({ DifferentOrderingBookMapper.class }) @@ -81,7 +78,7 @@ public void testMapperWithMatchingParameterNamesAndDifferentOrdering() { assertTargetBookMatchesSourceBook( targetBook ); } - @Test + @ProcessorTest @WithClasses({ ObjectFactoryBookMapper.class }) @@ -102,7 +99,7 @@ private void assertTargetBookMatchesSourceBook(TargetBook targetBook) { assertThat( authorLastName ).isEqualTo( targetBook.getAuthorLastName() ); } - @Test + @ProcessorTest @WithClasses({ ErroneousBookMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Test.java index b436899147..077e66df76 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Test.java @@ -9,11 +9,9 @@ import java.util.Locale; import org.joda.time.DateTime; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -25,11 +23,10 @@ Source.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1460") public class Issue1460Test { - @Test + @ProcessorTest public void shouldTestMappingLocalDates() { long dateInMs = 1524693600000L; String dateAsString = "2018-04-26"; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/java8/Issue1460JavaTimeTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/java8/Issue1460JavaTimeTest.java index ff3ea2d1c7..829402efaa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/java8/Issue1460JavaTimeTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/java8/Issue1460JavaTimeTest.java @@ -7,11 +7,9 @@ import java.time.LocalDate; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -23,11 +21,10 @@ Source.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1460") public class Issue1460JavaTimeTest { - @Test + @ProcessorTest public void shouldTestMappingLocalDates() { String dateAsString = "2018-04-26"; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/Issue1482Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/Issue1482Test.java index 4b77eb2c34..d59b58dbda 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/Issue1482Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1482/Issue1482Test.java @@ -7,11 +7,9 @@ import java.math.BigDecimal; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -24,10 +22,9 @@ ValueWrapper.class }) @IssueKey(value = "1482") -@RunWith(AnnotationProcessorTestRunner.class) public class Issue1482Test { - @Test + @ProcessorTest @WithClasses( SourceTargetMapper.class ) public void testForward() { @@ -43,7 +40,7 @@ public void testForward() { } - @Test + @ProcessorTest @WithClasses( TargetSourceMapper.class ) public void testReverse() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java index d1f859db6f..f528432c18 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java @@ -9,13 +9,11 @@ import java.util.Calendar; import java.util.TimeZone; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -32,13 +30,12 @@ Source.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1523") public class Issue1523Test { private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault(); - @BeforeClass + @BeforeAll public static void before() { // we want to test that the timezone will correctly be used in mapped XMLGregorianCalendar and not the // default one, so we must ensure that we use a different timezone than the default one -> set the default @@ -46,13 +43,13 @@ public static void before() { TimeZone.setDefault( TimeZone.getTimeZone( "UTC" ) ); } - @AfterClass + @AfterAll public static void after() { // revert the changed default TZ TimeZone.setDefault( DEFAULT_TIMEZONE ); } - @Test + @ProcessorTest public void testThatCorrectTimeZoneWillBeUsedInTarget() { Source source = new Source(); // default one was explicitly set to UTC, thus +01:00 is a different one diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1541/Issue1541Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1541/Issue1541Test.java index 92a7f99ca7..01f7ed92a9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1541/Issue1541Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1541/Issue1541Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1541; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -21,11 +19,10 @@ Issue1541Mapper.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1541") public class Issue1541Test { - @Test + @ProcessorTest public void testMappingWithVarArgs() { Target target = Issue1541Mapper.INSTANCE.mapWithVarArgs( "code", "1", "2" ); @@ -38,7 +35,7 @@ public void testMappingWithVarArgs() { assertThat( target.isAfterMappingContextWithVarArgsAsArrayCalled() ).isFalse(); } - @Test + @ProcessorTest public void testMappingWithArray() { Target target = Issue1541Mapper.INSTANCE.mapWithArray( "code", new String[] { "1", "2" } ); @@ -52,7 +49,7 @@ public void testMappingWithArray() { assertThat( target.isAfterMappingContextWithVarArgsAsArrayCalled() ).isFalse(); } - @Test + @ProcessorTest public void testMappingWithVarArgsReassignment() { Target target = Issue1541Mapper.INSTANCE.mapWithReassigningVarArgs( "code", "1", "2" ); @@ -66,7 +63,7 @@ public void testMappingWithVarArgsReassignment() { assertThat( target.isAfterMappingContextWithVarArgsAsArrayCalled() ).isFalse(); } - @Test + @ProcessorTest public void testMappingWithArrayAndVarArgs() { Target target = Issue1541Mapper.INSTANCE.mapWithArrayAndVarArgs( "code", new String[] { "1", "2" }, "3", "4" ); @@ -80,7 +77,7 @@ public void testMappingWithArrayAndVarArgs() { assertThat( target.isAfterMappingContextWithVarArgsAsArrayCalled() ).isFalse(); } - @Test + @ProcessorTest public void testVarArgsInAfterMappingAsArray() { Target target = Issue1541Mapper.INSTANCE.mapParametersAsArrayInAfterMapping( "code", "1", "2" ); @@ -94,7 +91,7 @@ public void testVarArgsInAfterMappingAsArray() { assertThat( target.isAfterMappingContextWithVarArgsAsArrayCalled() ).isFalse(); } - @Test + @ProcessorTest public void testVarArgsInAfterMappingAsVarArgs() { Target target = Issue1541Mapper.INSTANCE.mapParametersAsVarArgsInAfterMapping( "code", "1", "2" ); @@ -108,7 +105,7 @@ public void testVarArgsInAfterMappingAsVarArgs() { assertThat( target.isAfterMappingContextWithVarArgsAsArrayCalled() ).isFalse(); } - @Test + @ProcessorTest public void testVarArgsInContextWithVarArgsAfterMapping() { Target target = Issue1541Mapper.INSTANCE.mapContextWithVarArgsInAfterMappingWithVarArgs( "code", @@ -127,7 +124,7 @@ public void testVarArgsInContextWithVarArgsAfterMapping() { assertThat( target.isAfterMappingContextWithVarArgsAsArrayCalled() ).isFalse(); } - @Test + @ProcessorTest public void testVarArgsInContextWithArrayAfterMapping() { Target target = Issue1541Mapper.INSTANCE.mapContextWithVarArgsInAfterMappingWithArray( "code", diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1552/Issue1552Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1552/Issue1552Test.java index c06a00bb05..2dcaf182c6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1552/Issue1552Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1552/Issue1552Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1552; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -20,11 +18,10 @@ Issue1552Mapper.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1552") public class Issue1552Test { - @Test + @ProcessorTest public void shouldCompile() { Target target = Issue1552Mapper.INSTANCE.twoArgsWithConstant( "first", "second" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1558/java8/Issue1558Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1558/java8/Issue1558Test.java index 9840716429..38bafc1101 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1558/java8/Issue1558Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1558/java8/Issue1558Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1558.java8; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Sjaak Derksen @@ -20,11 +18,10 @@ Car.class, Car2.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1558") public class Issue1558Test { - @Test + @ProcessorTest public void testShouldCompile() { Car2 car = new Car2(); Car target = CarMapper.INSTANCE.toCar( car ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1561/Issue1561Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1561/Issue1561Test.java index 4b8e5704ee..3e17e1c605 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1561/Issue1561Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1561/Issue1561Test.java @@ -5,12 +5,10 @@ */ package org.mapstruct.ap.test.bugs._1561; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -18,7 +16,6 @@ /** * @author Sebastian Haberey */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1561") @WithClasses({ Issue1561Mapper.class, @@ -28,12 +25,12 @@ }) public class Issue1561Test { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( Issue1561Mapper.class ); - @Test + @ProcessorTest public void shouldCorrectlyUseAdder() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1566/Issue1566Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1566/Issue1566Test.java index 667f90d1ca..5de2af6ab3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1566/Issue1566Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1566/Issue1566Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1566; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -23,10 +21,9 @@ Target.class }) @IssueKey("1566") -@RunWith(AnnotationProcessorTestRunner.class) public class Issue1566Test { - @Test + @ProcessorTest public void genericMapperIsCorrectlyUsed() { Source source = new Source(); source.setId( "id-123" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1569/java8/Issue1569Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1569/java8/Issue1569Test.java index 079e792705..1bc7f4ca8d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1569/java8/Issue1569Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1569/java8/Issue1569Test.java @@ -10,11 +10,9 @@ import java.time.ZoneOffset; import java.util.Date; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -26,11 +24,10 @@ Source.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1569") public class Issue1569Test { - @Test + @ProcessorTest public void shouldGenerateCorrectMapping() { Source source = new Source(); Date date = Date.from( LocalDate.of( 2018, Month.AUGUST, 5 ).atTime( 20, 30 ).toInstant( ZoneOffset.UTC ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1576/java8/Issue1576Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1576/java8/Issue1576Test.java index 4b367a15a9..36f10eb5ad 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1576/java8/Issue1576Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1576/java8/Issue1576Test.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.bugs._1576.java8; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; @IssueKey("1576") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses( { Issue1576Mapper.class, Source.class, Target.class }) public class Issue1576Test { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( Issue1576Mapper.class ); - @Test + @ProcessorTest public void testLocalDateTimeIsImported() { Issue1576Mapper.INSTANCE.map( new Source() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Issue1590Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Issue1590Test.java index 30613d1a27..d77add25de 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Issue1590Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Issue1590Test.java @@ -7,11 +7,9 @@ import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -24,11 +22,10 @@ Book.class, BookShelf.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1590") public class Issue1590Test { - @Test + @ProcessorTest public void shouldTestMappingLocalDates() { BookShelf source = new BookShelf(); source.setBooks( Arrays.asList( new Book() ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1594/Issue1594Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1594/Issue1594Test.java index b498884b29..0c1fed523e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1594/Issue1594Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1594/Issue1594Test.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.bugs._1594; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1594") @WithClasses({ Issue1594Mapper.class }) public class Issue1594Test { - @Test + @ProcessorTest public void shouldGenerateCorrectMapping() { Issue1594Mapper.Dto dto = new Issue1594Mapper.Dto(); dto.setFullAddress( "Switzerland-Zurich" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1596/Issue1596Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1596/Issue1596Test.java index 0d75feea03..96286aa1a1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1596/Issue1596Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1596/Issue1596Test.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.bugs._1596; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.spi.AccessorNamingStrategy; import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; @@ -15,10 +13,10 @@ import org.mapstruct.ap.test.bugs._1596.dto.ImmutableItemDTO; import org.mapstruct.ap.test.bugs._1596.dto.ItemDTO; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.WithServiceImplementations; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -32,7 +30,6 @@ ItemDTO.class, ImmutableItemDTO.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1596") @WithServiceImplementations( { @WithServiceImplementation( provides = BuilderProvider.class, value = Issue1569BuilderProvider.class), @@ -40,7 +37,7 @@ }) public class Issue1596Test { - @Test + @ProcessorTest public void shouldIncludeBuildType() { ItemDTO item = ImmutableItemDTO.builder().id( "test" ).build(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1608/Issue1608Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1608/Issue1608Test.java index 44abfd69fa..528bc9b91b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1608/Issue1608Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1608/Issue1608Test.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.bugs._1608; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1608") @WithClasses({ Issue1608Mapper.class, @@ -25,7 +22,7 @@ }) public class Issue1608Test { - @Test + @ProcessorTest public void shouldCorrectlyUseFluentSettersStartingWithIs() { Book book = new Book(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1648/Issue1648Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1648/Issue1648Test.java index 03ba02d4b0..5dfe65d77e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1648/Issue1648Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1648/Issue1648Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1648; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("1648") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1648Mapper.class, Source.class, @@ -25,7 +22,7 @@ }) public class Issue1648Test { - @Test + @ProcessorTest public void shouldCorrectlyMarkSourceAsUsed() { Source source = new Source(); source.setSourceValue( "value" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/Issue1650Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/Issue1650Test.java index 9dd6f2dba6..2d1437eb77 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/Issue1650Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/Issue1650Test.java @@ -5,16 +5,13 @@ */ package org.mapstruct.ap.test.bugs._1650; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @IssueKey("1650") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ AMapper.class, A.class, @@ -25,7 +22,7 @@ }) public class Issue1650Test { - @Test + @ProcessorTest public void shouldCompile() { A a = new A(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1660/Issue1660Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1660/Issue1660Test.java index 838f705c20..7de37e6b43 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1660/Issue1660Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1660/Issue1660Test.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.bugs._1660; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1660") @WithClasses({ Issue1660Mapper.class, @@ -25,7 +22,7 @@ }) public class Issue1660Test { - @Test + @ProcessorTest public void shouldNotUseStaticMethods() { Source source = new Source(); source.setValue( "source" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1665/Issue1665Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1665/Issue1665Test.java index 4690d5d5a2..169e01029a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1665/Issue1665Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1665/Issue1665Test.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.bugs._1665; -import org.junit.Test; -import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.List; + import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; -import java.util.ArrayList; -import java.util.List; - /** * @author Arne Seime */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1665") @WithClasses({ Issue1665Mapper.class, @@ -28,7 +25,7 @@ }) public class Issue1665Test { - @Test + @ProcessorTest public void shouldBoxIntPrimitive() { Source source = new Source(); List values = new ArrayList<>(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1681/Issue1681Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1681/Issue1681Test.java index 812400be04..901794dea0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1681/Issue1681Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1681/Issue1681Test.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.bugs._1681; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1681") @WithClasses({ Issue1681Mapper.class, @@ -25,7 +22,7 @@ }) public class Issue1681Test { - @Test + @ProcessorTest public void shouldCompile() { Target target = new Target( "before" ); Source source = new Source(); @@ -37,7 +34,7 @@ public void shouldCompile() { assertThat( updatedTarget.getValue() ).isEqualTo( "after" ); } - @Test + @ProcessorTest public void shouldCompileWithBuilder() { Target.Builder targetBuilder = Target.builder(); targetBuilder.builderValue( "before" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1685/Issue1685Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1685/Issue1685Test.java index 5bff62f4c5..eccef6df2f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1685/Issue1685Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1685/Issue1685Test.java @@ -5,17 +5,14 @@ */ package org.mapstruct.ap.test.bugs._1685; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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, @@ -25,12 +22,12 @@ }) public class Issue1685Test { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( UserMapper.class ); - @Test + @ProcessorTest public void testSetToNullWhenNVPMSSetToNull() { User target = new User(); @@ -58,7 +55,7 @@ public void testSetToNullWhenNVPMSSetToNull() { assertThat( target.getSettings() ).isNull(); } - @Test + @ProcessorTest public void testIgnoreWhenNVPMSIgnore() { User target = new User(); @@ -86,7 +83,7 @@ public void testIgnoreWhenNVPMSIgnore() { assertThat( target.getSettings() ).containsExactly( "test" ); } - @Test + @ProcessorTest public void testSetToDefaultWhenNVPMSSetToDefault() { User target = new User(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java index a7a3b58e75..c5af6d923f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1698/Issue1698Test.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.bugs._1698; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1698") @WithClasses(Erroneous1698Mapper.class) public class Issue1698Test { - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = Erroneous1698Mapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1707/Issue1707Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1707/Issue1707Test.java index 5d2083312b..9798ea1bb6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1707/Issue1707Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1707/Issue1707Test.java @@ -5,27 +5,24 @@ */ package org.mapstruct.ap.test.bugs._1707; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1707") @WithClasses({ Converter.class }) public class Issue1707Test { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( Converter.class ); - @Test + @ProcessorTest public void codeShouldBeGeneratedCorrectly() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1714/Issue1714Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1714/Issue1714Test.java index 88a7848c84..4a2e42ad61 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1714/Issue1714Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1714/Issue1714Test.java @@ -5,22 +5,19 @@ */ package org.mapstruct.ap.test.bugs._1714; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1714") @WithClasses({ Issue1714Mapper.class }) public class Issue1714Test { - @Test + @ProcessorTest public void codeShouldBeGeneratedCorrectly() { Issue1714Mapper.OnDemand source = new Issue1714Mapper.OnDemand(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1719/Issue1719Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1719/Issue1719Test.java index e34c790322..182e1849be 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1719/Issue1719Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1719/Issue1719Test.java @@ -5,16 +5,13 @@ */ package org.mapstruct.ap.test.bugs._1719; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1719") @WithClasses({ Source.class, @@ -29,7 +26,7 @@ public class Issue1719Test { * from the child-parent relation. It cannot even assume that the the collection can be cleared at forehand. * Therefore the only sensible choice is for MapStruct to create a create method for the target elements. */ - @Test + @ProcessorTest @WithClasses(Issue1719Mapper.class) public void testShouldGiveNoErrorMessage() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1738/Issue1738Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1738/Issue1738Test.java index b9d28ff27b..e0755312fa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1738/Issue1738Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1738/Issue1738Test.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.bugs._1738; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1738") @WithClasses({ Issue1738Mapper.class, @@ -25,7 +22,7 @@ }) public class Issue1738Test { - @Test + @ProcessorTest public void shouldGenerateCorrectSourceNestedMapping() { Source source = new Source(); Source.Nested nested = new Source.Nested<>(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1742/Issue1742Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1742/Issue1742Test.java index e68f4657ff..6c64e0c261 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1742/Issue1742Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1742/Issue1742Test.java @@ -5,17 +5,14 @@ */ package org.mapstruct.ap.test.bugs._1742; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("1742") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses( { Issue1742Mapper.class, NestedSource.class, @@ -25,7 +22,7 @@ } ) public class Issue1742Test { - @Test + @ProcessorTest public void shouldCompile() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1751/Issue1751Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1751/Issue1751Test.java index 5061ba598f..e35424761a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1751/Issue1751Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1751/Issue1751Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1751; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("1772") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Holder.class, Issue1751Mapper.class, @@ -26,7 +23,7 @@ }) public class Issue1751Test { - @Test + @ProcessorTest public void name() { Source source = new Source(); source.setValue( "some value" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1772/Issue1772Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1772/Issue1772Test.java index b77eb34ec0..e05938e2f9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1772/Issue1772Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1772/Issue1772Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1772; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Sjaak Derksen */ @IssueKey("1772") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1772Mapper.class, Source.class, @@ -25,7 +22,7 @@ }) public class Issue1772Test { - @Test + @ProcessorTest public void shouldCorrectlyMarkSourceAsUsed() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1788/Issue1788Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1788/Issue1788Test.java index d9bf1c256c..bd5d687ae0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1788/Issue1788Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1788/Issue1788Test.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.bugs._1788; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @IssueKey( "1788" ) -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses( Issue1788Mapper.class ) public class Issue1788Test { - @Test + @ProcessorTest public void shouldCompile() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1790/Issue1790Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1790/Issue1790Test.java index 8ea3c4c026..c92d240cbf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1790/Issue1790Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1790/Issue1790Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1790; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("1790") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1790Config.class, Issue1790Mapper.class, @@ -26,7 +23,7 @@ }) public class Issue1790Test { - @Test + @ProcessorTest public void shouldProperlyApplyNullValuePropertyMappingStrategyWhenInheriting() { Target target = new Target(); target.setName( "My name is set" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1797/Issue1797Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1797/Issue1797Test.java index ecb531721d..bd9fcb92e0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1797/Issue1797Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1797/Issue1797Test.java @@ -7,11 +7,9 @@ import java.util.EnumSet; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -19,7 +17,6 @@ * @author Filip Hrisafov */ @IssueKey("1797") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Customer.class, CustomerDto.class, @@ -27,7 +24,7 @@ }) public class Issue1797Test { - @Test + @ProcessorTest public void shouldCorrectlyMapEnumSetToEnumSet() { Customer customer = new Customer( EnumSet.of( Customer.Type.ONE ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1799/Issue1799Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1799/Issue1799Test.java index 742c72f717..b3234cbcfc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1799/Issue1799Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1799/Issue1799Test.java @@ -7,11 +7,9 @@ import java.util.Date; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -24,10 +22,9 @@ Target.class, }) @IssueKey("1799") -@RunWith(AnnotationProcessorTestRunner.class) public class Issue1799Test { - @Test + @ProcessorTest public void fluentJavaBeanStyleSettersShouldWork() { Target target = Issue1799Mapper.INSTANCE.map( new Source( new Date( 150 ), "Switzerland" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/Issue1801Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/Issue1801Test.java index e407c331f3..ee6076f823 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/Issue1801Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/Issue1801Test.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.bugs._1801; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.spi.AccessorNamingStrategy; import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; @@ -15,10 +13,10 @@ import org.mapstruct.ap.test.bugs._1801.dto.ImmutableItemDTO; import org.mapstruct.ap.test.bugs._1801.dto.ItemDTO; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.WithServiceImplementations; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -32,7 +30,6 @@ ItemDTO.class, ImmutableItemDTO.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1801") @WithServiceImplementations( { @WithServiceImplementation( provides = BuilderProvider.class, value = Issue1801BuilderProvider.class), @@ -40,7 +37,7 @@ }) public class Issue1801Test { - @Test + @ProcessorTest public void shouldIncludeBuildeType() { ItemDTO item = ImmutableItemDTO.builder().id( "test" ).build(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Test.java index aa309e7fce..4481ed43e2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Test.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.bugs._1821; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @IssueKey("1797") -@RunWith( AnnotationProcessorTestRunner.class) @WithClasses( Issue1821Mapper.class ) public class Issue1821Test { - @Test + @ProcessorTest public void shouldNotGiveNullPtr() { Issue1821Mapper.INSTANCE.map( new Issue1821Mapper.Source() ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1826/Issue1826Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1826/Issue1826Test.java index 66f6cb14f3..be22569e5b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1826/Issue1826Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1826/Issue1826Test.java @@ -5,15 +5,12 @@ */ package org.mapstruct.ap.test.bugs._1826; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1826") @WithClasses({ SourceParent.class, @@ -23,7 +20,7 @@ }) public class Issue1826Test { - @Test + @ProcessorTest public void testNestedPropertyMappingChecksForNull() { SourceParent sourceParent = new SourceParent(); sourceParent.setSourceChild( null ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1828/Issue1828Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1828/Issue1828Test.java index 0d99cf9036..a3aa5b3581 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1828/Issue1828Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1828/Issue1828Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1828; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("1828") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ CompleteAddress.class, Employee.class, @@ -28,7 +25,7 @@ }) public class Issue1828Test { - @Test + @ProcessorTest public void testMapSpecialAndGeneralAddressSet() { Employee employee = new Employee(); @@ -55,7 +52,7 @@ public void testMapSpecialAndGeneralAddressSet() { assertThat( completeAddress.getCountry() ).isEqualTo( "Seven Kingdom" ); } - @Test + @ProcessorTest public void testMapGeneralAddressNull() { Employee employee = new Employee(); @@ -77,7 +74,7 @@ public void testMapGeneralAddressNull() { assertThat( completeAddress.getCountry() ).isNull(); } - @Test + @ProcessorTest public void testMapSpecialAddressNull() { Employee employee = new Employee(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/Issue1881Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/Issue1881Test.java index 6e947a1066..21bab07e52 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/Issue1881Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1881/Issue1881Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1881; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,13 +15,12 @@ * @author Filip Hrisafov */ @IssueKey("1881") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ VehicleDtoMapper.class, }) public class Issue1881Test { - @Test + @ProcessorTest public void shouldCompileCorrectly() { VehicleDtoMapper.VehicleDto vehicle = VehicleDtoMapper.INSTANCE.map( new VehicleDtoMapper.Vehicle( "Test", diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1904/Issue1904Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1904/Issue1904Test.java index b6e0b03cf7..b369765433 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1904/Issue1904Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1904/Issue1904Test.java @@ -5,22 +5,19 @@ */ package org.mapstruct.ap.test.bugs._1904; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("1904") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1904Mapper.class, }) @@ -30,7 +27,7 @@ ) public class Issue1904Test { - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic( type = Issue1904Mapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Test.java index ae4ecb2e88..5d1ca351f9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1933/Issue1933Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1933; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,14 +15,13 @@ * @author Sjaak Derksen */ @IssueKey("1933") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1933Config.class, Issue1933Mapper.class }) public class Issue1933Test { - @Test + @ProcessorTest public void shouldIgnoreIdAndMapUpdateCount() { Issue1933Config.Dto dto = new Issue1933Config.Dto(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Test.java index 9ad37958d0..528f45594a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1966/Issue1966Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._1966; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,13 +15,12 @@ * @author Sjaak Derksen */ @IssueKey("1966") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue1966Mapper.class }) public class Issue1966Test { - @Test + @ProcessorTest public void shouldSelectDefaultExpressionEvenWhenSourceInMappingIsNotSpecified() { Issue1966Mapper.AnimalRecord dto = new Issue1966Mapper.AnimalRecord(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Test.java index 7cb89f79e9..52525e9e9f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2001/Issue2001Test.java @@ -5,17 +5,14 @@ */ package org.mapstruct.ap.test.bugs._2001; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("2001") -@RunWith( AnnotationProcessorTestRunner.class ) @WithClasses( { Entity.class, EntityExtra.class, @@ -25,7 +22,7 @@ } ) public class Issue2001Test { - @Test + @ProcessorTest public void shouldCompile() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Test.java index 21893e0676..68719b9ff0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2018/Issue2018Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2018; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("2018") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue2018Mapper.class, Source.class, @@ -25,7 +22,7 @@ }) public class Issue2018Test { - @Test + @ProcessorTest public void shouldGenerateCorrectCode() { Source source = new Source(); source.setSomeValue( "value" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Test.java index f111799d53..bb6ef326b6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2021/Issue2021Test.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.bugs._2021; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("2021") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue2021Mapper.class }) public class Issue2021Test { - @Test + @ProcessorTest public void shouldCompile() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Test.java index ffc3df5e07..99c97d9eb5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2023/Issue2023Test.java @@ -7,11 +7,9 @@ import java.util.UUID; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -19,7 +17,6 @@ * @author Filip Hrisafov */ @IssueKey("2023") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue2023Mapper.class, NewPersonRequest.class, @@ -27,7 +24,7 @@ }) public class Issue2023Test { - @Test + @ProcessorTest public void shouldUseDefaultExpressionCorrectly() { PersonDto person = new PersonDto(); person.setName( "John" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Test.java index 26e585c23d..9d1d18dac6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2042/Issue2402Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2042; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,13 +15,12 @@ * @author Filip Hrisafov */ @IssueKey("2402") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue2402Mapper.class }) public class Issue2402Test { - @Test + @ProcessorTest public void shouldCompile() { Issue2402Mapper.Target target = Issue2402Mapper.INSTANCE. map( diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java index 332c3034dc..607c1137f8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2077/Issue2077Test.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.bugs._2077; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static javax.tools.Diagnostic.Kind.ERROR; @@ -20,10 +18,9 @@ * @author Sjaak Derksen */ @IssueKey("2077") -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2077Test { - @Test + @ProcessorTest @WithClasses(Issue2077ErroneousMapper.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java index 9148184b25..96c3f31b12 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2101/Issue2101Test.java @@ -5,19 +5,16 @@ */ package org.mapstruct.ap.test.bugs._2101; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @IssueKey("2101") -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2101Test { - @Test + @ProcessorTest @WithClasses(Issue2101Mapper.class) public void shouldMap() { @@ -34,7 +31,7 @@ public void shouldMap() { } - @Test + @ProcessorTest @WithClasses(Issue2101AdditionalMapper.class) public void shouldMapSomeAdditionalTests1() { Issue2101AdditionalMapper.Source source = new Issue2101AdditionalMapper.Source(); @@ -49,7 +46,7 @@ public void shouldMapSomeAdditionalTests1() { assertThat( target.value3 ).isEqualTo( "value3" ); } - @Test + @ProcessorTest @WithClasses(Issue2101AdditionalMapper.class) public void shouldMapSomeAdditionalTests2() { Issue2101AdditionalMapper.Source source = new Issue2101AdditionalMapper.Source(); @@ -65,7 +62,7 @@ public void shouldMapSomeAdditionalTests2() { } - @Test + @ProcessorTest @WithClasses(Issue2102IgnoreAllButMapper.class) public void shouldApplyIgnoreAllButTemplateOfMethod1() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Test.java index 6ee44acb97..bc1deed110 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2109/Issue2109Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2109; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("2109") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue2109Mapper.class, Source.class, @@ -25,7 +22,7 @@ }) public class Issue2109Test { - @Test + @ProcessorTest public void shouldCorrectlyMapArrayInConstructorMapping() { Target target = Issue2109Mapper.INSTANCE.map( new Source( 100L, new byte[] { 100, 120, 40, 40 } ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Test.java index 0d6b9ef771..a3006ceb9c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2111/Issue2111Test.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.bugs._2111; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Sjaak Derksen */ @IssueKey("2111") -@RunWith( AnnotationProcessorTestRunner.class ) @WithClasses( Issue2111Mapper.class ) public class Issue2111Test { - @Test + @ProcessorTest public void shouldCompile() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Test.java index fd51fd9384..984aa2daa9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2117/Issue2117Test.java @@ -7,11 +7,9 @@ import java.nio.file.AccessMode; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -19,13 +17,12 @@ * @author Filip Hrisafov */ @IssueKey("2117") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue2117Mapper.class }) public class Issue2117Test { - @Test + @ProcessorTest public void shouldCompile() { Issue2117Mapper.Target target = Issue2117Mapper.INSTANCE.toTarget( AccessMode.READ, null ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Test.java index 3d6f6dfca1..24bb88450b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2121/Issue2121Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2121; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,11 +15,10 @@ * @author Filip Hrisafov */ @IssueKey("2121") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(Issue2121Mapper.class) public class Issue2121Test { - @Test + @ProcessorTest public void shouldCompile() { Issue2121Mapper mapper = Issue2121Mapper.INSTANCE; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Test.java index 0d42a8f125..f0a82d8f0a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2122/Issue2122Test.java @@ -7,11 +7,9 @@ import java.util.Collections; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -19,11 +17,9 @@ * @author Christian Bandowski */ @IssueKey("2122") -@RunWith(AnnotationProcessorTestRunner.class) - public class Issue2122Test { - @Test + @ProcessorTest @WithClasses( Issue2122Method2MethodMapper.class ) public void shouldMapMethod2Method() { Issue2122Method2MethodMapper.Source source = new Issue2122Method2MethodMapper.Source(); @@ -47,7 +43,7 @@ public void shouldMapMethod2Method() { .extracting( Issue2122Method2MethodMapper.EmbeddedTarget::getValue ).isEqualTo( "value" ); } - @Test + @ProcessorTest @WithClasses( Issue2122TypeConversion2MethodMapper.class ) public void shouldMapTypeConversion2Method() { Issue2122TypeConversion2MethodMapper.Source source = new Issue2122TypeConversion2MethodMapper.Source(); @@ -63,7 +59,7 @@ public void shouldMapTypeConversion2Method() { .isEqualTo( "5" ); } - @Test + @ProcessorTest @WithClasses( Issue2122Method2TypeConversionMapper.class ) public void shouldMapMethod2TypeConversion() { Issue2122Method2TypeConversionMapper.Source source = new Issue2122Method2TypeConversionMapper.Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Test.java index 5671c78877..f2f9873fdb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2124/Issue2124Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2124; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,14 +15,13 @@ * @author Filip Hrisafov */ @IssueKey("2124") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ CommitComment.class, Issue2124Mapper.class }) public class Issue2124Test { - @Test + @ProcessorTest public void shouldCompile() { CommitComment clone = Issue2124Mapper.INSTANCE.clone( new CommitComment( 100 ), null ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java index e98afe7d52..69a564edf0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2125/Issue2125Test.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.bugs._2125; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -21,10 +19,9 @@ Comment.class, Repository.class, }) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2125Test { - @Test + @ProcessorTest @WithClasses({ Issue2125Mapper.class }) @@ -55,7 +52,7 @@ public void shouldSelectProperMethod() { assertThat( comment.getIssueId() ).isEqualTo( 1001 ); } - @Test + @ProcessorTest @WithClasses({ Issue2125ErroneousMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Test.java index 80cde5c5bf..6f7a3ec8a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2131/Issue2131Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2131; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,11 +15,10 @@ * @author Filip Hrisafov */ @IssueKey("2131") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(Issue2131Mapper.class) public class Issue2131Test { - @Test + @ProcessorTest public void shouldCompile() { Issue2131Mapper mapper = Issue2131Mapper.INSTANCE; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Test.java index 8a8e8eebbe..5f78b3fdde 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2133/Issue2133Test.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.bugs._2133; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @IssueKey("2133") @WithClasses( Issue2133Mapper.class ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2133Test { - @Test + @ProcessorTest public void shouldCompile() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Test.java index 8cfff232e5..287c39c175 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2142/Issue2142Test.java @@ -5,26 +5,23 @@ */ package org.mapstruct.ap.test.bugs._2142; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @IssueKey("2142") @WithClasses(Issue2142Mapper.class) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2142Test { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource() + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() .addComparisonToFixtureFor( Issue2142Mapper.class ); - @Test + @ProcessorTest public void underscorePrefixShouldBeStrippedFromGeneratedLocalVariables() { Issue2142Mapper._Target target = Issue2142Mapper.INSTANCE.map( new Issue2142Mapper.Source( "value1" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java index 02ad2ba901..ec8bb4603c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.bugs._2145; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @IssueKey("2145") @WithClasses(Issue2145Mapper.class) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2145Test { - @Test + @ProcessorTest public void test() { Issue2145Mapper.Source source = new Issue2145Mapper.Source(); source.setValue( "test" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java index 80cfbcc7db..8c19e2e090 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java @@ -5,26 +5,23 @@ */ package org.mapstruct.ap.test.bugs._2149; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("2149") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Erroneous2149Mapper.class }) public class Issue2149Test { - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic( diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Test.java index 3ed5cf1654..5f7dc7f57f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2164/Issue2164Test.java @@ -7,20 +7,17 @@ import java.math.BigDecimal; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @IssueKey("2164") @WithClasses(Issue2164Mapper.class) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2164Test { - @Test + @ProcessorTest public void shouldSelectProperMethod() { Issue2164Mapper.Target target = Issue2164Mapper.INSTANCE.map( new BigDecimal( "1234" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/Issue2170Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/Issue2170Test.java index 779cc7fd03..b5c10d120a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/Issue2170Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2170/Issue2170Test.java @@ -7,8 +7,6 @@ import java.util.Collections; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._2170.dto.AddressDto; import org.mapstruct.ap.test.bugs._2170.dto.PersonDto; import org.mapstruct.ap.test.bugs._2170.entity.Address; @@ -17,8 +15,8 @@ import org.mapstruct.ap.test.bugs._2170.mapper.EntityMapper; import org.mapstruct.ap.test.bugs._2170.mapper.PersonMapper; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -26,7 +24,6 @@ * @author Filip Hrisafov */ @IssueKey("2170") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Address.class, Person.class, @@ -38,7 +35,7 @@ }) public class Issue2170Test { - @Test + @ProcessorTest public void shouldGenerateCodeThatCompiles() { AddressDto addressDto = AddressMapper.INSTANCE.toDto( new Address( diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/Issue2174Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/Issue2174Test.java index 89359957c9..b6ec1f08d2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/Issue2174Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2174/Issue2174Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2174; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -18,11 +16,10 @@ * @author Filip Hrisafov */ @IssueKey("2174") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(UserMapper.class) public class Issue2174Test { - @Test + @ProcessorTest public void shouldNotWrapCheckedException() throws Exception { UserMapper.User user = UserMapper.INSTANCE.map( new UserMapper.UserDto( "Test City", "10000" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java index 50c1d9f69b..5c1c16b27f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2177; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,13 +15,12 @@ * @author Filip Hrisafov */ @IssueKey("2177") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue2177Mapper.class }) public class Issue2177Test { - @Test + @ProcessorTest public void shouldCorrectlyUseGenericClassesWithConstructorMapping() { Issue2177Mapper.Target target = Issue2177Mapper.INSTANCE.map( new Issue2177Mapper.Source( "test" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/Issue2185Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/Issue2185Test.java index 3b26842c0a..7280d26e31 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/Issue2185Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2185/Issue2185Test.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.bugs._2185; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -20,13 +18,12 @@ * @author Filip Hrisafov */ @IssueKey("2185") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ TodoMapper.class }) public class Issue2185Test { - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { @Diagnostic(type = TodoMapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Test.java index 8d8a754d7f..5ee3c35d86 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2195/Issue2195Test.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.bugs._2195; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._2195.dto.Source; import org.mapstruct.ap.test.bugs._2195.dto.Target; import org.mapstruct.ap.test.bugs._2195.dto.TargetBase; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @IssueKey("2195") @WithClasses( { Source.class, Target.class, TargetBase.class } ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2195Test { - @Test + @ProcessorTest @WithClasses( Issue2195Mapper.class ) public void test() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Test.java index 52be67564c..774d1f396f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2197/Issue2197Test.java @@ -5,26 +5,23 @@ */ package org.mapstruct.ap.test.bugs._2197; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @IssueKey("2197") @WithClasses(Issue2197Mapper.class) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2197Test { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource() + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() .addComparisonToFixtureFor( Issue2197Mapper.class ); - @Test + @ProcessorTest public void underscoreAndDigitPrefixShouldBeStrippedFromGeneratedLocalVariables() { Issue2197Mapper._0Target target = Issue2197Mapper.INSTANCE.map( new Issue2197Mapper.Source( "value1" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Issue2213Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Issue2213Test.java index 613b981d04..2f8f951668 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Issue2213Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2213/Issue2213Test.java @@ -5,12 +5,10 @@ */ package org.mapstruct.ap.test.bugs._2213; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -24,15 +22,14 @@ Car.class, Car2.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("2213") public class Issue2213Test { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource() + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() .addComparisonToFixtureFor( CarMapper.class ); - @Test + @ProcessorTest public void testShouldNotGenerateIntermediatePrimitiveMappingMethod() { Car2 car = new Car2(); int[] sourceInt = { 1, 2, 3 }; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/Issue2221Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/Issue2221Test.java index 34514de929..ef7f2f4ba6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/Issue2221Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2221/Issue2221Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2221; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("2221") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ RestConfig.class, RestSiteDto.class, @@ -26,7 +23,7 @@ }) public class Issue2221Test { - @Test + @ProcessorTest public void multiSourceInheritConfigurationShouldWork() { SiteDto site = RestSiteMapper.INSTANCE.convert( new RestSiteDto( "restTenant", "restSite", "restCti" ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Issue2233Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Issue2233Test.java index 645de0d24b..66911d936d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Issue2233Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2233/Issue2233Test.java @@ -8,11 +8,9 @@ import java.util.Collections; import java.util.Optional; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; @@ -21,7 +19,6 @@ * @author Filip Hrisafov */ @IssueKey("2233") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses( { Program.class, ProgramAggregate.class, @@ -31,7 +28,7 @@ } ) public class Issue2233Test { - @Test + @ProcessorTest public void shouldCorrectlyMapFromOptionalToCollection() { ProgramResponseDto response = ProgramMapper.INSTANCE.map( new ProgramAggregate( Collections.singleton( new Program( diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Issue2236Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Issue2236Test.java index 17f42c8dc3..a1bbea638b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Issue2236Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2236/Issue2236Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2236; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("2236") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Car.class, CarDto.class, @@ -27,7 +24,7 @@ }) public class Issue2236Test { - @Test + @ProcessorTest public void shouldCorrectlyMapSameTypesWithDifferentNestedMappings() { Vehicle vehicle = new Vehicle(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/Issue2245Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/Issue2245Test.java index 5aa899d055..69af6fef8a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/Issue2245Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2245/Issue2245Test.java @@ -5,12 +5,10 @@ */ package org.mapstruct.ap.test.bugs._2245; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -19,17 +17,16 @@ * @author Filip Hrisafov */ @IssueKey("2245") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ TestMapper.class }) public class Issue2245Test { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource() + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() .addComparisonToFixtureFor( TestMapper.class ); - @Test + @ProcessorTest public void shouldGenerateSourceGetMethodOnce() { TestMapper.Tenant tenant = diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Test.java index d08a5f7d74..1c430b00ca 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2251/Issue2251Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2251; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("2251") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue2251Mapper.class, Source.class, @@ -25,7 +22,7 @@ }) public class Issue2251Test { - @Test + @ProcessorTest public void shouldGenerateCorrectCode() { Target target = Issue2251Mapper.INSTANCE.map( new Source( "source" ), "test" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/Issue2253Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/Issue2253Test.java index fc0e007daa..ae1c1eea79 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/Issue2253Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2253/Issue2253Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2253; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,13 +15,12 @@ * @author Filip Hrisafov */ @IssueKey("2253") -@RunWith( AnnotationProcessorTestRunner.class ) @WithClasses( { TestMapper.class, } ) public class Issue2253Test { - @Test + @ProcessorTest public void shouldNotTreatMatchedSourceParameterAsBean() { TestMapper.Person person = TestMapper.INSTANCE.map( new TestMapper.PersonDto( 20 ), "Tester" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Issue2263Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Issue2263Test.java index 03e545b210..53fd5d88a4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Issue2263Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2263/Issue2263Test.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.bugs._2263; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("2263") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Erroneous2263Mapper.class, Source.class, @@ -26,7 +23,7 @@ }) public class Issue2263Test { - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = Erroneous2263Mapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278Test.java index eefebfa870..8c8a4cf01f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2278/Issue2278Test.java @@ -7,17 +7,14 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @IssueKey("2278") -@RunWith( AnnotationProcessorTestRunner.class) public class Issue2278Test { - @Test + @ProcessorTest @WithClasses( Issue2278ReferenceMapper.class ) public void testReferenceMergeBehaviour() { @@ -37,7 +34,7 @@ public void testReferenceMergeBehaviour() { } - @Test + @ProcessorTest @WithClasses( Issue2278MapperA.class ) public void shouldBehaveJustAsTestReferenceMergeBehaviour() { @@ -57,7 +54,7 @@ public void shouldBehaveJustAsTestReferenceMergeBehaviour() { } - @Test + @ProcessorTest @WithClasses( Issue2278MapperB.class ) public void shouldOverrideDetailsMappingWithRedefined() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Test.java index 8c237a2d30..3986ab1066 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2301/Issue2301Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2301; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("2301") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Artifact.class, ArtifactDto.class, @@ -25,7 +22,7 @@ }) public class Issue2301Test { - @Test + @ProcessorTest public void shouldCorrectlyIgnoreProperties() { Artifact artifact = Issue2301Mapper.INSTANCE.map( new ArtifactDto( "mapstruct" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Test.java index a1f1ad250b..a9a06ea470 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2318/Issue2318Test.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.bugs._2318; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._2318.Issue2318Mapper.SourceChild; import org.mapstruct.ap.test.bugs._2318.Issue2318Mapper.TargetChild; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @IssueKey("2318") -@RunWith(AnnotationProcessorTestRunner.class) public class Issue2318Test { - @Test + @ProcessorTest @WithClasses( Issue2318Mapper.class ) public void shouldMap() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/Issue2347Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/Issue2347Test.java index f5e04fc776..e54816d576 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/Issue2347Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2347/Issue2347Test.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.bugs._2347; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("2347") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ ErroneousClassWithPrivateMapper.class }) @@ -42,7 +39,7 @@ public class Issue2347Test { ) } ) - @Test + @ProcessorTest public void shouldGenerateCompileErrorWhenMapperIsPrivate() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/Issue2352Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/Issue2352Test.java index ebf6ac1e1c..fa9edf0bf4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/Issue2352Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2352/Issue2352Test.java @@ -7,16 +7,14 @@ import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._2352.dto.TheDto; import org.mapstruct.ap.test.bugs._2352.dto.TheModel; import org.mapstruct.ap.test.bugs._2352.dto.TheModels; import org.mapstruct.ap.test.bugs._2352.mapper.TheModelMapper; import org.mapstruct.ap.test.bugs._2352.mapper.TheModelsMapper; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -24,7 +22,6 @@ * @author Filip Hrisafov */ @IssueKey("2352") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ TheDto.class, TheModel.class, @@ -34,7 +31,7 @@ }) public class Issue2352Test { - @Test + @ProcessorTest public void shouldGenerateValidCode() { TheModels theModels = new TheModels(); theModels.add( new TheModel( "1" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Test.java index e37f36528c..8efc38abd2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2356/Issue2356Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2356; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,13 +15,12 @@ * @author Filip Hrisafov */ @IssueKey("2356") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue2356Mapper.class }) public class Issue2356Test { - @Test + @ProcessorTest public void shouldCompile() { Issue2356Mapper.Car car = new Issue2356Mapper.Car(); car.brand = "Tesla"; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Issue2393Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Issue2393Test.java index 0bb88e5e59..8595d4c7f1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Issue2393Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2393/Issue2393Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._2393; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Filip Hrisafov */ @IssueKey("2393") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Address.class, AddressDto.class, @@ -26,7 +23,7 @@ }) public class Issue2393Test { - @Test + @ProcessorTest public void shouldUseCorrectImport() { AddressDto dto = AddressDto.Converter.INSTANCE.convert( new Address( "Zurich", diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_289/Issue289Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_289/Issue289Test.java index fb9d16112a..4b95513fe2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_289/Issue289Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_289/Issue289Test.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.bugs._289; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/289. @@ -27,10 +25,9 @@ SourceElement.class, TargetElement.class } ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue289Test { - @Test + @ProcessorTest public void shouldLeaveEmptyTargetSetWhenSourceIsNullAndGetterOnlyForCreateMethod() { Source source = new Source(); @@ -41,7 +38,7 @@ public void shouldLeaveEmptyTargetSetWhenSourceIsNullAndGetterOnlyForCreateMetho assertThat( target.getCollection() ).isEmpty(); } - @Test + @ProcessorTest public void shouldLeaveEmptyTargetSetWhenSourceIsNullAndGetterOnlyForUpdateMethod() { Source source = new Source(); @@ -54,7 +51,7 @@ public void shouldLeaveEmptyTargetSetWhenSourceIsNullAndGetterOnlyForUpdateMetho assertThat( target.getCollection() ).isEmpty(); } - @Test + @ProcessorTest public void shouldLeaveNullTargetSetWhenSourceIsNullForCreateMethod() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_306/Issue306Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_306/Issue306Test.java index 712e563ca8..5d4f0c84b2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_306/Issue306Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_306/Issue306Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._306; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/306. @@ -17,10 +15,9 @@ * @author Sjaak Derksen */ @IssueKey( "306" ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue306Test { - @Test + @ProcessorTest @WithClasses( { Issue306Mapper.class, Source.class, Target.class } ) public void shouldForgeNewIterableMappingMethod() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_373/Issue373Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_373/Issue373Test.java index d860591fff..91c01ac7c2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_373/Issue373Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_373/Issue373Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._373; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/373. @@ -17,10 +15,9 @@ * @author Sjaak Derksen */ @IssueKey( "373" ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue373Test { - @Test + @ProcessorTest @WithClasses( { Issue373Mapper.class, Branch.class, BranchLocation.class, Country.class, ResultDto.class } ) public void shouldForgeCorrectEntityBranchLocationCountry() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_374/Issue374Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_374/Issue374Test.java index 7c1fa96719..7f174732ff 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_374/Issue374Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_374/Issue374Test.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.bugs._374; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/306. @@ -24,10 +22,9 @@ * @author Sjaak Derksen */ @IssueKey( "306" ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue374Test { - @Test + @ProcessorTest @WithClasses( { Issue374Mapper.class, Source.class, Target.class } ) public void shouldMapExistingTargetToDefault() { @@ -38,7 +35,7 @@ public void shouldMapExistingTargetToDefault() { assertThat( result.getConstant() ).isEqualTo( "test" ); } - @Test + @ProcessorTest @WithClasses( { Issue374Mapper.class, Source.class, Target.class } ) public void shouldMapExistingTargetWithConstantToDefault() { @@ -49,7 +46,7 @@ public void shouldMapExistingTargetWithConstantToDefault() { assertThat( target2.getConstant() ).isNull(); } - @Test + @ProcessorTest @WithClasses( { Issue374Mapper.class, Source.class, Target.class } ) public void shouldMapExistingIterableTargetToDefault() { @@ -60,7 +57,7 @@ public void shouldMapExistingIterableTargetToDefault() { assertThat( targetList ).isEmpty(); } - @Test + @ProcessorTest @WithClasses( { Issue374Mapper.class, Source.class, Target.class } ) public void shouldMapExistingMapTargetToDefault() { @@ -71,7 +68,7 @@ public void shouldMapExistingMapTargetToDefault() { assertThat( resultMap ).isEqualTo( resultMap ); } - @Test + @ProcessorTest @WithClasses( { Issue374VoidMapper.class, Source.class, Target.class } ) public void shouldMapExistingTargetVoidReturnToDefault() { @@ -81,7 +78,7 @@ public void shouldMapExistingTargetVoidReturnToDefault() { assertThat( target.getConstant() ).isEqualTo( "test" ); } - @Test + @ProcessorTest @WithClasses( { Issue374VoidMapper.class, Source.class, Target.class } ) public void shouldMapExistingIterableTargetVoidReturnToDefault() { @@ -91,7 +88,7 @@ public void shouldMapExistingIterableTargetVoidReturnToDefault() { assertThat( targetList ).isEmpty(); } - @Test + @ProcessorTest @WithClasses( { Issue374VoidMapper.class, Source.class, Target.class } ) public void shouldMapExistingMapTargetVoidReturnToDefault() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_375/Issue375Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_375/Issue375Test.java index e02c56a1d8..3cbd2fcad1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_375/Issue375Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_375/Issue375Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._375; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/375. @@ -17,10 +15,9 @@ * @author Sjaak Derksen */ @IssueKey( "375" ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue375Test { - @Test + @ProcessorTest @WithClasses( { Issue375Mapper.class, Source.class, Target.class, Int.class, Case.class } ) public void shouldForgeNewMappings() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_394/SameClassNameInDifferentPackageTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_394/SameClassNameInDifferentPackageTest.java index ac9dbf93c1..78cbc6177e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_394/SameClassNameInDifferentPackageTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_394/SameClassNameInDifferentPackageTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.bugs._394; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.HashMap; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._394.source.AnotherCar; import org.mapstruct.ap.test.bugs._394.source.Cars; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses( { SameNameForSourceAndTargetCarsMapper.class, @@ -26,10 +24,9 @@ org.mapstruct.ap.test.bugs._394._target.AnotherCar.class } ) @IssueKey("394") -@RunWith(AnnotationProcessorTestRunner.class) public class SameClassNameInDifferentPackageTest { - @Test + @ProcessorTest public void shouldCreateMapMethodImplementation() { Map values = new HashMap(); //given diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_405/Issue405Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_405/Issue405Test.java index 75f6e6952e..f1f34ee394 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_405/Issue405Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_405/Issue405Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._405; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/405. @@ -17,10 +15,9 @@ * @author Sjaak Derksen */ @IssueKey( "405" ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue405Test { - @Test + @ProcessorTest @WithClasses( { EntityFactory.class, Person.class, People.class, PersonMapper.class } ) public void shouldGenerateFactoryCorrectMethodForIterables() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/Issue513Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/Issue513Test.java index 0b75d8681e..9bc5be096e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/Issue513Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_513/Issue513Test.java @@ -8,11 +8,11 @@ import java.util.Arrays; import java.util.HashMap; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/513. @@ -34,10 +34,9 @@ MappingKeyException.class, MappingValueException.class } ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue513Test { - @Test( expected = MappingException.class ) + @ProcessorTest public void shouldThrowMappingException() throws Exception { Source source = new Source(); @@ -45,11 +44,12 @@ public void shouldThrowMappingException() throws Exception { sourceElement.setValue( "test" ); source.setCollection( Arrays.asList( sourceElement ) ); - Issue513Mapper.INSTANCE.map( source ); + assertThatThrownBy( () -> Issue513Mapper.INSTANCE.map( source ) ) + .isInstanceOf( MappingException.class ); } - @Test( expected = MappingKeyException.class ) + @ProcessorTest public void shouldThrowMappingKeyException() throws Exception { Source source = new Source(); @@ -60,11 +60,12 @@ public void shouldThrowMappingKeyException() throws Exception { map.put( sourceKey, sourceValue ); source.setMap( map ); - Issue513Mapper.INSTANCE.map( source ); + assertThatThrownBy( () -> Issue513Mapper.INSTANCE.map( source ) ) + .isInstanceOf( MappingKeyException.class ); } - @Test( expected = MappingValueException.class ) + @ProcessorTest public void shouldThrowMappingValueException() throws Exception { Source source = new Source(); @@ -75,11 +76,12 @@ public void shouldThrowMappingValueException() throws Exception { map.put( sourceKey, sourceValue ); source.setMap( map ); - Issue513Mapper.INSTANCE.map( source ); + assertThatThrownBy( () -> Issue513Mapper.INSTANCE.map( source ) ) + .isInstanceOf( MappingValueException.class ); } - @Test( expected = MappingException.class ) + @ProcessorTest public void shouldThrowMappingCommonException() throws Exception { Source source = new Source(); @@ -90,7 +92,8 @@ public void shouldThrowMappingCommonException() throws Exception { map.put( sourceKey, sourceValue ); source.setMap( map ); - Issue513Mapper.INSTANCE.map( source ); + assertThatThrownBy( () -> Issue513Mapper.INSTANCE.map( source ) ) + .isInstanceOf( MappingException.class ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_515/Issue515Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_515/Issue515Test.java index e322963db7..eb32a41a85 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_515/Issue515Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_515/Issue515Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._515; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/515. @@ -17,10 +15,9 @@ * @author Sjaak Derksen */ @IssueKey( "515" ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue515Test { - @Test + @ProcessorTest @WithClasses( { Issue515Mapper.class, Source.class, Target.class } ) public void shouldIgnoreParanthesesOpenInStringDefinition() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_516/Issue516Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_516/Issue516Test.java index 5b64aca1dc..226a7f8157 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_516/Issue516Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_516/Issue516Test.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.bugs._516; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/516. @@ -19,10 +17,9 @@ * @author Sjaak Derksen */ @IssueKey( "516" ) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue516Test { - @Test + @ProcessorTest @WithClasses( { SourceTargetMapper.class, Source.class, Target.class } ) public void shouldAddNullPtrCheckAroundSourceForAdder() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_537/Issue537Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_537/Issue537Test.java index ce5fb5dcbb..6d60aaa79e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_537/Issue537Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_537/Issue537Test.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.bugs._537; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Christian Bandowski */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("537") @WithClasses({ Issue537Mapper.class, @@ -27,7 +24,7 @@ }) public class Issue537Test { - @Test + @ProcessorTest public void testThatReferencedMapperWillBeUsed() { Target target = Issue537Mapper.INSTANCE.mapDto( new Source( "abc" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_543/Issue543Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_543/Issue543Test.java index 0773f7d271..4a6f0176d1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_543/Issue543Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_543/Issue543Test.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.bugs._543; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.bugs._543.dto.Source; import org.mapstruct.ap.test.bugs._543.dto.Target; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("543") @WithClasses({ Issue543Mapper.class, @@ -28,10 +25,10 @@ }) public class Issue543Test { - @Rule - public GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest public void shouldCompile() { generatedSource.forMapper( Issue543Mapper.class ).containsImportFor( Source.class ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_577/Issue577Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_577/Issue577Test.java index 9b6672a539..198231f410 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_577/Issue577Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_577/Issue577Test.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.bugs._577; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @IssueKey( "577" ) @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue577Test { - @Test + @ProcessorTest public void shouldMapTwoArraysToCollections() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_581/Issue581Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_581/Issue581Test.java index cf50b344e1..7c1dfb266e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_581/Issue581Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_581/Issue581Test.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.bugs._581; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._581.source.Car; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @IssueKey( "581" ) @WithClasses({ Car.class, org.mapstruct.ap.test.bugs._581._target.Car.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue581Test { - @Test + @ProcessorTest public void shouldMapSourceAndTargetWithTheSameClassName() { Car source = new Car(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java index 60c3910c0a..0bf8553e15 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_590/Issue590Test.java @@ -7,22 +7,19 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Andreas Gudian */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(ErroneousSourceTargetMapper.class) public class Issue590Test { - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @Diagnostic(type = ErroneousSourceTargetMapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java index 1a503a74b2..79a6e045ec 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.bugs._611; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +15,6 @@ * @author Tillmann Gaida */ @IssueKey("611") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ SomeClass.class, SomeOtherClass.class @@ -26,7 +23,7 @@ public class Issue611Test { /** * Checks if an implementation of a nested mapper can be loaded at all. */ - @Test + @ProcessorTest public void mapperIsFound() { assertThat( SomeClass.InnerMapper.INSTANCE ).isNotNull(); } @@ -35,7 +32,7 @@ public void mapperIsFound() { * Checks if an implementation of a nested mapper can be loaded which is nested into an already * nested class. */ - @Test + @ProcessorTest public void mapperNestedInsideNestedClassIsFound() { assertThat( SomeClass.SomeInnerClass.InnerMapper.INSTANCE ).isNotNull(); } @@ -44,7 +41,7 @@ public void mapperNestedInsideNestedClassIsFound() { * Checks if it is possible to load two mapper implementations which have equal simple names * in the same package. */ - @Test + @ProcessorTest public void rightMapperIsFound() { SomeClass.InnerMapper.Source source1 = new SomeClass.InnerMapper.Source(); SomeOtherClass.InnerMapper.Source source2 = new SomeOtherClass.InnerMapper.Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_625/Issue625Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_625/Issue625Test.java index 32fc27d56c..81ff528da5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_625/Issue625Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_625/Issue625Test.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.bugs._625; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Andreas Gudian * */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ ObjectFactory.class, Source.class, @@ -27,7 +24,7 @@ }) @IssueKey("625") public class Issue625Test { - @Test + @ProcessorTest public void ignoresFactoryMethods() { Source s = new Source(); s.setProp( "works" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_631/Issue631Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_631/Issue631Test.java index 5d582d1c1f..57a221c6d3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_631/Issue631Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_631/Issue631Test.java @@ -7,22 +7,19 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) public class Issue631Test { - @Test + @ProcessorTest @IssueKey("631") @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_634/GenericContainerTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_634/GenericContainerTest.java index f16a9e6f6a..99d609408e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_634/GenericContainerTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_634/GenericContainerTest.java @@ -5,16 +5,14 @@ */ package org.mapstruct.ap.test.bugs._634; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Gunnar Morling @@ -26,10 +24,9 @@ Target.class, SourceTargetMapper.class, }) -@RunWith(AnnotationProcessorTestRunner.class) public class GenericContainerTest { - @Test + @ProcessorTest @IssueKey("634") public void canMapGenericSourceTypeToGenericTargetType() { List items = Arrays.asList( new Foo( "42" ), new Foo( "84" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_775/IterableWithBoundedElementTypeTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_775/IterableWithBoundedElementTypeTest.java index a3fe3dacd3..db2f376416 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_775/IterableWithBoundedElementTypeTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_775/IterableWithBoundedElementTypeTest.java @@ -5,16 +5,14 @@ */ package org.mapstruct.ap.test.bugs._775; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; import org.assertj.core.api.IterableAssert; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Verifies: @@ -26,7 +24,6 @@ * * @author Andreas Gudian */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("775") @WithClasses({ MapperWithForgedIterableMapping.class, @@ -36,7 +33,7 @@ }) public class IterableWithBoundedElementTypeTest { - @Test + @ProcessorTest public void createsForgedMethodForIterableLowerBoundInteger() { ListContainer source = new ListContainer(); @@ -47,7 +44,7 @@ public void createsForgedMethodForIterableLowerBoundInteger() { .contains( 42, 47 ); } - @Test + @ProcessorTest public void usesListIntegerMethodForIterableLowerBoundInteger() { ListContainer source = new ListContainer(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/Commit.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/Commit.java index b0d74bae26..431a999c45 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/Commit.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/Commit.java @@ -30,4 +30,8 @@ public static int getCallCounter() { return callCounter; } + public static void resetCallCounter() { + callCounter = 0; + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/GitlabTag.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/GitlabTag.java index ff298ad8a2..28df16ee08 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/GitlabTag.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/GitlabTag.java @@ -28,4 +28,8 @@ public static int getCallCounter() { return callCounter; } + public static void resetCallCounter() { + callCounter = 0; + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/Issue843Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/Issue843Test.java index 1540df73d6..4937f6c3d4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/Issue843Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_843/Issue843Test.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.bugs._843; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Date; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Commit.class, TagInfo.class, @@ -29,7 +26,7 @@ @IssueKey("843") public class Issue843Test { - @Test + @ProcessorTest public void testMapperCreation() { Commit commit = new Commit(); @@ -37,6 +34,8 @@ public void testMapperCreation() { GitlabTag gitlabTag = new GitlabTag(); gitlabTag.setCommit( commit ); + Commit.resetCallCounter(); + GitlabTag.resetCallCounter(); TagMapper.INSTANCE.gitlabTagToTagInfo( gitlabTag ); assertThat( Commit.getCallCounter() ).isEqualTo( 1 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_846/UpdateTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_846/UpdateTest.java index 9bac413c0b..776f3f70e0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_846/UpdateTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_846/UpdateTest.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.bugs._846; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("846") @WithClasses({ Mapper846.class }) public class UpdateTest { - @Test + @ProcessorTest public void shouldProduceMapper() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_849/Issue849Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_849/Issue849Test.java index 5b9e33c353..22ec5bd309 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_849/Issue849Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_849/Issue849Test.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.bugs._849; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Andreas Gudian * */ @WithClasses({ Source.class, Target.class, Issue849Mapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class Issue849Test { - @Test + @ProcessorTest @IssueKey("849") public void shouldCompileWithAllImportsDeclared() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_855/OrderingBug855Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_855/OrderingBug855Test.java index eaeba688d0..54ee84a89c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_855/OrderingBug855Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_855/OrderingBug855Test.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.bugs._855; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Andreas Gudian * */ @WithClasses({ OrderedSource.class, OrderedTarget.class, OrderDemoMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class OrderingBug855Test { - @Test + @ProcessorTest @IssueKey("855") public void shouldApplyCorrectOrderingWithDependsOn() { OrderedSource source = new OrderedSource(); @@ -31,7 +28,7 @@ public void shouldApplyCorrectOrderingWithDependsOn() { assertThat( target.getOrder() ).containsExactly( "field2", "field0", "field1", "field3", "field4" ); } - @Test + @ProcessorTest public void shouldRetainDefaultOrderWithoutDependsOn() { OrderedSource source = new OrderedSource(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_865/Issue865Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_865/Issue865Test.java index d1352cceb4..f5196ec7b7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_865/Issue865Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_865/Issue865Test.java @@ -5,17 +5,15 @@ */ package org.mapstruct.ap.test.bugs._865; -import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses( { ProjectDto.class, ProjectEntity.class, @@ -26,7 +24,7 @@ } ) public class Issue865Test { - @Test + @ProcessorTest public void shouldGenerateNpeCheckBeforeCallingAddAllWhenInUpdateMethods() { ProjectDto dto = new ProjectDto(); @@ -41,7 +39,7 @@ public void shouldGenerateNpeCheckBeforeCallingAddAllWhenInUpdateMethods() { assertThat( entity.getCoreUsers() ).isNull(); } - @Test + @ProcessorTest public void shouldGenerateNpeCheckBeforeCallingAddAllWhenInUpdateMethodsAndTargetWithoutSetter() { ProjectDto dto = new ProjectDto(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java index 4d491b1b2c..d05f01dc91 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java @@ -7,9 +7,8 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.MappingConstants; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; @@ -17,14 +16,12 @@ import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOptions; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.stereotype.Component; /** * @author Andreas Gudian */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ UsesConfigFromAnnotationMapper.class, DefaultsToProcessorOptionsMapper.class, @@ -34,10 +31,10 @@ @ProcessorOption(name = "mapstruct.defaultComponentModel", value = MappingConstants.ComponentModel.SPRING), @ProcessorOption(name = "mapstruct.unmappedTargetPolicy", value = "ignore") }) public class Issue880Test { - @Rule - public GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, diagnostics = @Diagnostic(kind = Kind.WARNING, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_891/Issue891Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_891/Issue891Test.java index a520a055d4..5fae478f2e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_891/Issue891Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_891/Issue891Test.java @@ -5,19 +5,16 @@ */ package org.mapstruct.ap.test.bugs._891; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({BuggyMapper.class, Dest.class, Src.class, SrcNested.class}) public class Issue891Test { - @Test + @ProcessorTest public void shouldNotThrowNPE() { BuggyMapper.INSTANCE.convert( new Src() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_892/Issue892Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_892/Issue892Test.java index 6291a56233..0064a96338 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_892/Issue892Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_892/Issue892Test.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.bugs._892; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._892.Issue892Mapper.Source; import org.mapstruct.ap.test.bugs._892.Issue892Mapper.Target; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; +import static org.assertj.core.api.Assertions.assertThat; + /** * When having two setter methods with the same name, choose the one with the argument type matching the getter method. * * @author Andreas Gudian */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Issue892Mapper.class }) public class Issue892Test { - @Test + @ProcessorTest public void compiles() { Source src = new Source(); src.setType( 42 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_895/Issue895Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_895/Issue895Test.java index d9f25f5fec..320c7d1306 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_895/Issue895Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_895/Issue895Test.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.bugs._895; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._895.MultiArrayMapper.WithArrayOfByteArray; import org.mapstruct.ap.test.bugs._895.MultiArrayMapper.WithListOfByteArray; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; +import static org.assertj.core.api.Assertions.assertThat; + /** * Verifies that forged iterable mapping methods for multi-dimensional arrays are generated properly. * * @author Andreas Gudian */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(MultiArrayMapper.class) public class Issue895Test { - @Test + @ProcessorTest public void properlyMapsMultiDimensionalArrays() { WithArrayOfByteArray arrayOfByteArray = new WithArrayOfByteArray(); arrayOfByteArray.setBytes( new byte[][] { new byte[] { 0, 1 }, new byte[] { 1, 2 } } ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_909/Issue909Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_909/Issue909Test.java index da74677ee2..20710488f7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_909/Issue909Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_909/Issue909Test.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.bugs._909; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._909.ValuesMapper.ValuesHolder; import org.mapstruct.ap.test.bugs._909.ValuesMapper.ValuesHolderDto; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; +import static org.assertj.core.api.Assertions.assertThat; + /** * Verifies that forged iterable mapping methods for multi-dimensional arrays are generated properly. * * @author Andreas Gudian */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses(ValuesMapper.class) public class Issue909Test { - @Test + @ProcessorTest public void properlyCreatesMapperWithValuesAsParameterName() { ValuesHolder valuesHolder = new ValuesHolder(); valuesHolder.setValues( "values" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913GetterMapperForCollectionsTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913GetterMapperForCollectionsTest.java index e63d91b5e7..4e10a4895f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913GetterMapperForCollectionsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913GetterMapperForCollectionsTest.java @@ -5,12 +5,11 @@ */ package org.mapstruct.ap.test.bugs._913; -import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * All these test cases test the possible combinations in the GetterMapperForCollections. @@ -19,7 +18,6 @@ * * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ DomainWithoutSetter.class, Dto.class, @@ -36,7 +34,7 @@ public class Issue913GetterMapperForCollectionsTest { * conversion from string to long that return null in the entire mapper, so also for the forged * mapper. Note the default NVMS is RETURN_NULL. */ - @Test + @ProcessorTest public void shouldReturnNullForNvmsReturnNullForCreate() { Dto dto = new Dto(); @@ -52,7 +50,7 @@ public void shouldReturnNullForNvmsReturnNullForCreate() { * conversion from string to long that return null in the entire mapper, so also for the forged * mapper. Note the default NVMS is RETURN_NULL. */ - @Test + @ProcessorTest public void shouldReturnNullForNvmsReturnNullForUpdate() { Dto dto = new Dto(); @@ -70,7 +68,7 @@ public void shouldReturnNullForNvmsReturnNullForUpdate() { * conversion from string to long that return null in the entire mapper, so also for the forged * mapper. Note the default NVMS is RETURN_NULL. */ - @Test + @ProcessorTest public void shouldReturnNullForNvmsReturnNullForUpdateWithReturn() { Dto dto = new Dto(); @@ -92,7 +90,7 @@ public void shouldReturnNullForNvmsReturnNullForUpdateWithReturn() { * * However, for plain mappings (strings to strings) the result will be null. */ - @Test + @ProcessorTest public void shouldReturnDefaultForNvmsReturnDefaultForCreate() { Dto dto = new Dto(); @@ -110,7 +108,7 @@ public void shouldReturnDefaultForNvmsReturnDefaultForCreate() { * * However, for plain mappings (strings to strings) the result will be null. */ - @Test + @ProcessorTest public void shouldReturnDefaultForNvmsReturnDefaultForUpdate() { Dto dto = new Dto(); @@ -131,7 +129,7 @@ public void shouldReturnDefaultForNvmsReturnDefaultForUpdate() { * However, for plain mappings (strings to strings) the result will be null. * */ - @Test + @ProcessorTest public void shouldReturnDefaultForNvmsReturnDefaultForUpdateWithReturn() { Dto dto = new Dto(); @@ -151,7 +149,7 @@ public void shouldReturnDefaultForNvmsReturnDefaultForUpdateWithReturn() { * Test create method ICW presence checker * */ - @Test + @ProcessorTest public void shouldReturnNullForCreateWithPresenceChecker() { DtoWithPresenceCheck dto = new DtoWithPresenceCheck(); @@ -166,7 +164,7 @@ public void shouldReturnNullForCreateWithPresenceChecker() { * Test update method ICW presence checker * */ - @Test + @ProcessorTest public void shouldReturnNullForUpdateWithPresenceChecker() { DtoWithPresenceCheck dto = new DtoWithPresenceCheck(); @@ -182,7 +180,7 @@ public void shouldReturnNullForUpdateWithPresenceChecker() { * Test update with return method ICW presence checker * */ - @Test + @ProcessorTest public void shouldReturnNullForUpdateWithReturnWithPresenceChecker() { DtoWithPresenceCheck dto = new DtoWithPresenceCheck(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913SetterMapperForCollectionsTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913SetterMapperForCollectionsTest.java index 8a3ce7fb64..a549011446 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913SetterMapperForCollectionsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/Issue913SetterMapperForCollectionsTest.java @@ -5,19 +5,17 @@ */ package org.mapstruct.ap.test.bugs._913; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.HashSet; import java.util.Set; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; + /** * All these test cases test the possible combinations in the SetterMapperForCollections. * @@ -25,7 +23,6 @@ * * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Domain.class, Dto.class, @@ -38,8 +35,8 @@ @IssueKey( "913" ) public class Issue913SetterMapperForCollectionsTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( DomainDtoWithNvmsNullMapper.class, DomainDtoWithNvmsDefaultMapper.class, DomainDtoWithPresenceCheckMapper.class, @@ -55,7 +52,7 @@ public class Issue913SetterMapperForCollectionsTest { * variable for setting {@code strings} as a direct assignment and the copy constructor is used, in case the * assignment is {@code null} then {@code null} should be set for {@code string} */ - @Test + @ProcessorTest public void shouldReturnNullForNvmsReturnNullForCreate() { Dto dto = new Dto(); @@ -71,7 +68,7 @@ public void shouldReturnNullForNvmsReturnNullForCreate() { * conversion from string to long that return null in the entire mapper, so also for the forged * mapper. Note the default NVMS is RETURN_NULL. */ - @Test + @ProcessorTest public void shouldReturnNullForNvmsReturnNullForUpdate() { Dto dto = new Dto(); @@ -94,7 +91,7 @@ public void shouldReturnNullForNvmsReturnNullForUpdate() { * target (stringsInitialized is Not Null) and source (stringInitialized is Null) target should * be explicitely set to null */ - @Test + @ProcessorTest public void shouldReturnNullForNvmsReturnNullForUpdateWithNonNullTargetAndNullSource() { Dto dto = new Dto(); @@ -117,7 +114,7 @@ public void shouldReturnNullForNvmsReturnNullForUpdateWithNonNullTargetAndNullSo * conversion from string to long that return null in the entire mapper, so also for the forged * mapper. Note the default NVMS is RETURN_NULL. */ - @Test + @ProcessorTest public void shouldReturnNullForNvmsReturnNullForUpdateWithReturn() { Dto dto = new Dto(); @@ -140,7 +137,7 @@ public void shouldReturnNullForNvmsReturnNullForUpdateWithReturn() { * * However, for plain mappings (strings to strings) the result will also be an empty collection. */ - @Test + @ProcessorTest public void shouldReturnDefaultForNvmsReturnDefaultForCreate() { Dto dto = new Dto(); @@ -158,7 +155,7 @@ public void shouldReturnDefaultForNvmsReturnDefaultForCreate() { * * However, for plain mappings (strings to strings) the result will also be an empty collection. */ - @Test + @ProcessorTest public void shouldReturnDefaultForNvmsReturnDefaultForUpdate() { Dto dto = new Dto(); @@ -184,7 +181,7 @@ public void shouldReturnDefaultForNvmsReturnDefaultForUpdate() { * However, for plain mappings (strings to strings) the result will also be an empty collection. * */ - @Test + @ProcessorTest public void shouldReturnDefaultForNvmsReturnDefaultForUpdateWithReturn() { Dto dto = new Dto(); @@ -211,7 +208,7 @@ public void shouldReturnDefaultForNvmsReturnDefaultForUpdateWithReturn() { * Test create method ICW presence checker. The presence checker is responsible for the null check. * */ - @Test + @ProcessorTest public void shouldReturnNullForCreateWithPresenceChecker() { DtoWithPresenceCheck dto = new DtoWithPresenceCheck(); @@ -229,7 +226,7 @@ public void shouldReturnNullForCreateWithPresenceChecker() { * */ @IssueKey( "#954") - @Test + @ProcessorTest public void shouldReturnNullForUpdateWithPresenceChecker() { DtoWithPresenceCheck dto = new DtoWithPresenceCheck(); @@ -252,7 +249,7 @@ public void shouldReturnNullForUpdateWithPresenceChecker() { * */ @IssueKey( "#954") - @Test + @ProcessorTest public void shouldReturnNullForUpdateWithReturnWithPresenceChecker() { DtoWithPresenceCheck dto = new DtoWithPresenceCheck(); @@ -277,7 +274,7 @@ public void shouldReturnNullForUpdateWithReturnWithPresenceChecker() { * */ @IssueKey( "#954") - @Test + @ProcessorTest public void shouldReturnNullForCreateWithNcvsAlways() { DtoWithPresenceCheck dto = new DtoWithPresenceCheck(); @@ -295,7 +292,7 @@ public void shouldReturnNullForCreateWithNcvsAlways() { * */ @IssueKey( "#954") - @Test + @ProcessorTest public void shouldReturnNullForUpdateWithNcvsAlways() { DtoWithPresenceCheck dto = new DtoWithPresenceCheck(); @@ -318,7 +315,7 @@ public void shouldReturnNullForUpdateWithNcvsAlways() { * */ @IssueKey( "#954") - @Test + @ProcessorTest public void shouldReturnNullForUpdateWithReturnWithNcvsAlways() { DtoWithPresenceCheck dto = new DtoWithPresenceCheck(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Issue931Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Issue931Test.java index 8fc0e0cbcf..25c2baf89d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Issue931Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_931/Issue931Test.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.bugs._931; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Verifies that source.nested == null, leads to target.id == null @@ -19,10 +17,9 @@ * @author Sjaak Derksen */ @IssueKey( "931" ) -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses( { Source.class, Nested.class, Target.class, SourceTargetMapper.class } ) public class Issue931Test { - @Test + @ProcessorTest public void shouldMapNestedNullToNull() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_955/Issue955Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_955/Issue955Test.java index 8178668900..4e03de5373 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_955/Issue955Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_955/Issue955Test.java @@ -5,24 +5,21 @@ */ package org.mapstruct.ap.test.bugs._955; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.bugs._955.dto.Car; import org.mapstruct.ap.test.bugs._955.dto.Person; import org.mapstruct.ap.test.bugs._955.dto.SuperCar; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * * @author Sjaak Derksen */ @IssueKey( "955" ) -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses( { CarMapper.class, CustomMapper.class, Car.class, SuperCar.class, Person.class } ) public class Issue955Test { - @Test + @ProcessorTest public void shouldCompile() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_971/Issue971Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_971/Issue971Test.java index 236f1641c9..fe178f815b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_971/Issue971Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_971/Issue971Test.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.bugs._971; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov * */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("971") public class Issue971Test { - @Test + @ProcessorTest @WithClasses({ CollectionSource.class, CollectionTarget.class, Issue971CollectionMapper.class }) public void shouldCompileForCollections() { } - @Test + @ProcessorTest @WithClasses({ MapSource.class, MapTarget.class, Issue971MapMapper.class }) public void shouldCompileForMaps() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/abstractBuilder/AbstractBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/abstractBuilder/AbstractBuilderTest.java index 8ff3bf9fad..47a534d20a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/abstractBuilder/AbstractBuilderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/abstractBuilder/AbstractBuilderTest.java @@ -5,10 +5,8 @@ */ package org.mapstruct.ap.test.builder.abstractBuilder; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -23,7 +21,6 @@ ProductDto.class, ProductMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class AbstractBuilderTest { /** @@ -35,7 +32,7 @@ public class AbstractBuilderTest { * - all values are mapped * - all values are set properly */ - @Test + @ProcessorTest public void testThatAbstractBuilderMapsAllProperties() { ImmutableProduct product = ProductMapper.INSTANCE.fromMutable( new ProductDto( "router", 31 ) ); @@ -43,7 +40,7 @@ public void testThatAbstractBuilderMapsAllProperties() { assertThat( product.getName() ).isEqualTo( "router" ); } - @Test + @ProcessorTest public void testThatAbstractBuilderReverseMapsAllProperties() { ProductDto product = ProductMapper.INSTANCE.fromImmutable( ImmutableProduct.builder() .price( 31000 ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/abstractGenericTarget/AbstractGenericTargetBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/abstractGenericTarget/AbstractGenericTargetBuilderTest.java index 8ff58c5af3..2b77353175 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/abstractGenericTarget/AbstractGenericTargetBuilderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/abstractGenericTarget/AbstractGenericTargetBuilderTest.java @@ -5,10 +5,8 @@ */ package org.mapstruct.ap.test.builder.abstractGenericTarget; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -26,10 +24,9 @@ ParentSource.class, ParentMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class AbstractGenericTargetBuilderTest { - @Test + @ProcessorTest public void testAbstractTargetMapper() { ParentSource parent = new ParentSource(); parent.setCount( 4 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/factory/BuilderFactoryMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/factory/BuilderFactoryMapperTest.java index 57641a6dc1..5ec38b2d0a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/factory/BuilderFactoryMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/factory/BuilderFactoryMapperTest.java @@ -5,17 +5,14 @@ */ package org.mapstruct.ap.test.builder.factory; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ BuilderFactoryMapper.class, BuilderImplicitFactoryMapper.class, @@ -24,7 +21,7 @@ }) public class BuilderFactoryMapperTest { - @Test + @ProcessorTest public void shouldUseBuilderFactory() { Person person = BuilderFactoryMapper.INSTANCE.map( new PersonDto( "Filip" ) ); @@ -32,7 +29,7 @@ public void shouldUseBuilderFactory() { assertThat( person.getSource() ).isEqualTo( "Factory with @ObjectFactory" ); } - @Test + @ProcessorTest public void shouldUseImplicitBuilderFactory() { Person person = BuilderImplicitFactoryMapper.INSTANCE.map( new PersonDto( "Filip" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringTest.java index 5d28aa3f53..5be9d50ff6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/ignore/BuilderIgnoringTest.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.builder.ignore; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1452") @WithClasses({ BaseDto.class, @@ -28,7 +25,7 @@ }) public class BuilderIgnoringTest { - @Test + @ProcessorTest @IssueKey( "1933" ) public void shouldIgnoreBase() { PersonDto source = new PersonDto(); @@ -43,7 +40,7 @@ public void shouldIgnoreBase() { assertThat( target.getLastName() ).isEqualTo( "Doe" ); } - @Test + @ProcessorTest public void shouldMapOnlyExplicit() { PersonDto source = new PersonDto(); source.setId( 100L ); @@ -57,7 +54,7 @@ public void shouldMapOnlyExplicit() { assertThat( target.getLastName() ).isNull(); } - @Test + @ProcessorTest public void shouldMapAll() { PersonDto source = new PersonDto(); source.setId( 100L ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/BuilderLifecycleCallbacksTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/BuilderLifecycleCallbacksTest.java index 706a08eea1..e3892f2154 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/BuilderLifecycleCallbacksTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/BuilderLifecycleCallbacksTest.java @@ -7,18 +7,15 @@ import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith( AnnotationProcessorTestRunner.class ) @IssueKey( "1433" ) @WithClasses( { Item.class, @@ -30,7 +27,7 @@ } ) public class BuilderLifecycleCallbacksTest { - @Test + @ProcessorTest public void lifecycleMethodsShouldBeInvoked() { OrderDto source = new OrderDto(); source.setCreator( "Filip" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java index e44912ff99..3682f2ccc5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.builder.mappingTarget.simple; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -20,13 +18,12 @@ SimpleImmutableTarget.class, SimpleBuilderMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class BuilderInfoTargetTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest public void testSimpleImmutableBuilderHappyPath() { SimpleMutableSource source = new SimpleMutableSource(); source.setAge( 3 ); @@ -39,7 +36,7 @@ public void testSimpleImmutableBuilderHappyPath() { assertThat( targetObject.getName() ).isEqualTo( "Bob" ); } - @Test + @ProcessorTest public void testMutableTargetWithBuilder() { SimpleMutableSource source = new SimpleMutableSource(); source.setAge( 20 ); @@ -50,7 +47,7 @@ public void testMutableTargetWithBuilder() { assertThat( target.getSource() ).isEqualTo( "Builder" ); } - @Test + @ProcessorTest public void testUpdateMutableWithBuilder() { SimpleMutableSource source = new SimpleMutableSource(); source.setAge( 20 ); @@ -69,7 +66,7 @@ public void testUpdateMutableWithBuilder() { assertThat( target.getSource() ).isEqualTo( "Empty constructor" ); } - @Test + @ProcessorTest public void updatingTargetWithNoSettersShouldNotFail() { SimpleMutableSource source = new SimpleMutableSource(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/multiple/MultipleBuilderMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/multiple/MultipleBuilderMapperTest.java index 21b9566c4f..825794d6d7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/multiple/MultipleBuilderMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/multiple/MultipleBuilderMapperTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.builder.multiple; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.builder.multiple.build.Process; import org.mapstruct.ap.test.builder.multiple.builder.Case; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1479") @WithClasses({ Process.class, @@ -53,7 +50,7 @@ public class MultipleBuilderMapperTest { "\"wrongCreate(), create()\"." ) }) - @Test + @ProcessorTest public void moreThanOneBuildMethod() { } @@ -72,14 +69,14 @@ public void moreThanOneBuildMethod() { "\"wrongCreate(), create()\"." ) }) - @Test + @ProcessorTest public void moreThanOneBuildMethodDefinedOnMapper() { } @WithClasses({ BuilderDefinedMapper.class }) - @Test + @ProcessorTest public void builderMappingDefined() { Process map = BuilderDefinedMapper.INSTANCE.map( new Source( "map" ) ); Process wrongMap = BuilderDefinedMapper.INSTANCE.wrongMap( new Source( "wrongMap" ) ); @@ -92,7 +89,7 @@ public void builderMappingDefined() { BuilderMapperConfig.class, BuilderConfigDefinedMapper.class }) - @Test + @ProcessorTest public void builderMappingMapperConfigDefined() { Process map = BuilderConfigDefinedMapper.INSTANCE.map( new Source( "map" ) ); Process wrongMap = BuilderConfigDefinedMapper.INSTANCE.wrongMap( new Source( "wrongMap" ) ); @@ -115,7 +112,7 @@ public void builderMappingMapperConfigDefined() { "implementing a custom BuilderProvider SPI." ) }) - @Test + @ProcessorTest public void tooManyBuilderCreationMethods() { Case caseTarget = TooManyBuilderCreationMethodsMapper.INSTANCE.map( new Source( "test" ) ); @@ -128,7 +125,7 @@ public void tooManyBuilderCreationMethods() { @WithClasses( { DefaultBuildMethodMapper.class } ) - @Test + @ProcessorTest public void defaultBuildMethod() { Task task = DefaultBuildMethodMapper.INSTANCE.map( new Source( "test" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/expanding/BuilderNestedPropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/expanding/BuilderNestedPropertyTest.java index 45073dd906..94233a373e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/expanding/BuilderNestedPropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/expanding/BuilderNestedPropertyTest.java @@ -5,10 +5,8 @@ */ package org.mapstruct.ap.test.builder.nestedprop.expanding; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -21,10 +19,9 @@ ImmutableArticle.class, ExpandingMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class BuilderNestedPropertyTest { - @Test + @ProcessorTest public void testNestedImmutablePropertyMapper() { FlattenedStock stock = new FlattenedStock( "Sock", "Tie", 33 ); ImmutableExpandedStock expandedTarget = ExpandingMapper.INSTANCE.writeToNestedProperty( stock ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/flattening/BuilderNestedPropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/flattening/BuilderNestedPropertyTest.java index a25e224472..861bf810bf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/flattening/BuilderNestedPropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/nestedprop/flattening/BuilderNestedPropertyTest.java @@ -5,10 +5,8 @@ */ package org.mapstruct.ap.test.builder.nestedprop.flattening; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -21,10 +19,9 @@ Article.class, FlatteningMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class BuilderNestedPropertyTest { - @Test + @ProcessorTest public void testNestedImmutablePropertyMapper() { Stock stock = new Stock(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/noop/NoOpBuilderProviderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/noop/NoOpBuilderProviderTest.java index 6334be7ff0..e90babfdd8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/noop/NoOpBuilderProviderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/noop/NoOpBuilderProviderTest.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.builder.noop; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.spi.NoOpBuilderProvider; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith( AnnotationProcessorTestRunner.class ) @IssueKey( "1418" ) @WithServiceImplementation(NoOpBuilderProvider.class) @WithClasses( { @@ -28,7 +25,7 @@ } ) public class NoOpBuilderProviderTest { - @Test + @ProcessorTest public void shouldNotUseBuilder() { Person person = PersonMapper.INSTANCE.map( new PersonDto( "Filip" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleNotRealyImmutableBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleNotRealyImmutableBuilderTest.java index a98be84d22..cc11a8fcdb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleNotRealyImmutableBuilderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleNotRealyImmutableBuilderTest.java @@ -5,12 +5,10 @@ */ package org.mapstruct.ap.test.builder.off; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.mapstruct.factory.Mappers; @@ -21,13 +19,12 @@ SimpleMutablePerson.class, SimpleNotRealyImmutablePerson.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class SimpleNotRealyImmutableBuilderTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses({ SimpleMapper.class }) public void testSimpleImmutableBuilderHappyPath() { SimpleMapper mapper = Mappers.getMapper( SimpleMapper.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/parentchild/ParentChildBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/parentchild/ParentChildBuilderTest.java index f1a3b4d617..dd45573a59 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/parentchild/ParentChildBuilderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/parentchild/ParentChildBuilderTest.java @@ -8,10 +8,8 @@ import java.util.ArrayList; import org.assertj.core.api.Condition; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -22,10 +20,9 @@ ImmutableParent.class, ParentChildMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ParentChildBuilderTest { - @Test + @ProcessorTest public void testParentChildBuilderMapper() { final MutableParent parent = new MutableParent(); parent.setCount( 4 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java index 3bc4e91ed8..7f77621644 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java @@ -7,14 +7,12 @@ import java.util.Arrays; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.mapstruct.factory.Mappers; @@ -24,13 +22,12 @@ SimpleMutablePerson.class, SimpleImmutablePerson.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class SimpleImmutableBuilderTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses({ SimpleBuilderMapper.class }) public void testSimpleImmutableBuilderHappyPath() { SimpleBuilderMapper mapper = Mappers.getMapper( SimpleBuilderMapper.class ); @@ -50,7 +47,7 @@ public void testSimpleImmutableBuilderHappyPath() { assertThat( targetObject.getChildren() ).contains( "Alice", "Tom" ); } - @Test + @ProcessorTest @WithClasses({ ErroneousSimpleBuilderMapper.class }) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = @Diagnostic( diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java index a16cb26ee6..e6cd7a19b4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java @@ -24,12 +24,8 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.mapstruct.ap.test.builtin._target.IterableTarget; import org.mapstruct.ap.test.builtin._target.MapTarget; import org.mapstruct.ap.test.builtin.bean.BigDecimalProperty; @@ -62,8 +58,8 @@ import org.mapstruct.ap.test.builtin.source.IterableSource; import org.mapstruct.ap.test.builtin.source.MapSource; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -89,24 +85,22 @@ IterableSource.class, MapSource.class }) -@RunWith(AnnotationProcessorTestRunner.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class BuiltInTest { private static TimeZone originalTimeZone; - @BeforeClass + @BeforeAll public static void setDefaultTimeZoneToCet() { originalTimeZone = TimeZone.getDefault(); TimeZone.setDefault( TimeZone.getTimeZone( "Europe/Berlin" ) ); } - @AfterClass + @AfterAll public static void restoreOriginalTimeZone() { TimeZone.setDefault( originalTimeZone ); } - @Test + @ProcessorTest @WithClasses( JaxbMapper.class ) public void shouldApplyBuiltInOnJAXBElement() { JaxbElementProperty source = new JaxbElementProperty(); @@ -119,7 +113,7 @@ public void shouldApplyBuiltInOnJAXBElement() { assertThat( target.publicProp ).isEqualTo( "PUBLIC TEST" ); } - @Test + @ProcessorTest @WithClasses( JaxbMapper.class ) @IssueKey( "1698" ) public void shouldApplyBuiltInOnJAXBElementExtra() { @@ -142,7 +136,7 @@ public void shouldApplyBuiltInOnJAXBElementExtra() { assertThat( target2.getProp() ).isNotNull(); } - @Test + @ProcessorTest @WithClasses( JaxbListMapper.class ) @IssueKey( "141" ) public void shouldApplyBuiltInOnJAXBElementList() { @@ -157,7 +151,7 @@ public void shouldApplyBuiltInOnJAXBElementList() { assertThat( target.publicProp.get( 0 ) ).isEqualTo( "PUBLIC TEST2" ); } - @Test + @ProcessorTest @WithClasses( DateToXmlGregCalMapper.class ) public void shouldApplyBuiltInOnDateToXmlGregCal() throws ParseException { @@ -173,7 +167,7 @@ public void shouldApplyBuiltInOnDateToXmlGregCal() throws ParseException { assertThat( target.publicProp.toString() ).isEqualTo( "2016-08-31T10:20:56.000+02:00" ); } - @Test + @ProcessorTest @WithClasses( XmlGregCalToDateMapper.class ) public void shouldApplyBuiltInOnXmlGregCalToDate() throws DatatypeConfigurationException { @@ -190,7 +184,7 @@ public void shouldApplyBuiltInOnXmlGregCalToDate() throws DatatypeConfigurationE } - @Test + @ProcessorTest @WithClasses( StringToXmlGregCalMapper.class ) public void shouldApplyBuiltInStringToXmlGregCal() { @@ -225,7 +219,7 @@ public void shouldApplyBuiltInStringToXmlGregCal() { } - @Test + @ProcessorTest @WithClasses( XmlGregCalToStringMapper.class ) public void shouldApplyBuiltInXmlGregCalToString() throws DatatypeConfigurationException { @@ -252,7 +246,7 @@ public void shouldApplyBuiltInXmlGregCalToString() throws DatatypeConfigurationE } - @Test + @ProcessorTest @WithClasses( CalendarToXmlGregCalMapper.class ) public void shouldApplyBuiltInOnCalendarToXmlGregCal() throws ParseException { @@ -268,7 +262,7 @@ public void shouldApplyBuiltInOnCalendarToXmlGregCal() throws ParseException { assertThat( target.publicProp.toString() ).isEqualTo( "2016-03-02T00:00:00.000+01:00" ); } - @Test + @ProcessorTest @WithClasses( XmlGregCalToCalendarMapper.class ) public void shouldApplyBuiltInOnXmlGregCalToCalendar() throws DatatypeConfigurationException { @@ -286,7 +280,7 @@ public void shouldApplyBuiltInOnXmlGregCalToCalendar() throws DatatypeConfigurat } - @Test + @ProcessorTest @WithClasses( CalendarToDateMapper.class ) public void shouldApplyBuiltInOnCalendarToDate() throws ParseException { @@ -302,7 +296,7 @@ public void shouldApplyBuiltInOnCalendarToDate() throws ParseException { assertThat( target.publicProp ).isEqualTo( createCalendar( "02.03.2016" ).getTime() ); } - @Test + @ProcessorTest @WithClasses( DateToCalendarMapper.class ) public void shouldApplyBuiltInOnDateToCalendar() throws ParseException { @@ -319,7 +313,7 @@ public void shouldApplyBuiltInOnDateToCalendar() throws ParseException { } - @Test + @ProcessorTest @WithClasses( CalendarToStringMapper.class ) public void shouldApplyBuiltInOnCalendarToString() throws ParseException { @@ -335,7 +329,7 @@ public void shouldApplyBuiltInOnCalendarToString() throws ParseException { assertThat( target.publicProp ).isEqualTo( "02.03.2016" ); } - @Test + @ProcessorTest @WithClasses( StringToCalendarMapper.class ) public void shouldApplyBuiltInOnStringToCalendar() throws ParseException { @@ -352,7 +346,7 @@ public void shouldApplyBuiltInOnStringToCalendar() throws ParseException { } - @Test + @ProcessorTest @WithClasses( IterableSourceTargetMapper.class ) public void shouldApplyBuiltInOnIterable() throws DatatypeConfigurationException { @@ -366,7 +360,7 @@ public void shouldApplyBuiltInOnIterable() throws DatatypeConfigurationException assertThat( target.publicDates ).containsExactly( "02.03.2016" ); } - @Test + @ProcessorTest @WithClasses( MapSourceTargetMapper.class ) public void shouldApplyBuiltInOnMap() throws DatatypeConfigurationException { @@ -382,7 +376,7 @@ public void shouldApplyBuiltInOnMap() throws DatatypeConfigurationException { assertThat( target.publicExample.get( "TEST" ) ).isEqualTo( "2016-03-02+01:00" ); } - @Test + @ProcessorTest @WithClasses( CalendarToZonedDateTimeMapper.class ) public void shouldApplyBuiltInOnCalendarToZonedDateTime() throws ParseException { assertThat( CalendarToZonedDateTimeMapper.INSTANCE.map( null ) ).isNull(); @@ -399,7 +393,7 @@ public void shouldApplyBuiltInOnCalendarToZonedDateTime() throws ParseException assertThat( target.publicProp ).isEqualTo( ZonedDateTime.of( 2016, 3, 2, 0, 0, 0, 0, ZoneId.systemDefault() ) ); } - @Test + @ProcessorTest @WithClasses( ZonedDateTimeToCalendarMapper.class ) public void shouldApplyBuiltInOnZonedDateTimeToCalendar() throws ParseException { assertThat( ZonedDateTimeToCalendarMapper.INSTANCE.map( null ) ).isNull(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java index 17d7980ff8..85c1bed399 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java @@ -12,17 +12,15 @@ import java.util.GregorianCalendar; import java.util.TimeZone; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.mapstruct.ap.test.builtin.bean.CalendarProperty; import org.mapstruct.ap.test.builtin.bean.DatatypeFactory; import org.mapstruct.ap.test.builtin.bean.DateProperty; import org.mapstruct.ap.test.builtin.bean.XmlGregorianCalendarFactorizedProperty; import org.mapstruct.ap.test.builtin.mapper.ToXmlGregCalMapper; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -34,23 +32,22 @@ DateProperty.class } ) -@RunWith(AnnotationProcessorTestRunner.class) public class DatatypeFactoryTest { private TimeZone originalTimeZone; - @Before + @BeforeEach public void setUp() { originalTimeZone = TimeZone.getDefault(); TimeZone.setDefault( TimeZone.getTimeZone( "Europe/Berlin" ) ); } - @After + @AfterEach public void tearDown() { TimeZone.setDefault( originalTimeZone ); } - @Test + @ProcessorTest public void testNoConflictsWithOwnDatatypeFactory() throws ParseException { DateProperty source1 = new DateProperty(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/jodatime/JodaTimeTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/jodatime/JodaTimeTest.java index 9f1e6cef20..77c62c9cac 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/jodatime/JodaTimeTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/jodatime/JodaTimeTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.builtin.jodatime; -import static org.assertj.core.api.Assertions.assertThat; - import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; @@ -16,8 +14,6 @@ import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.builtin.jodatime.bean.DateTimeBean; import org.mapstruct.ap.test.builtin.jodatime.bean.LocalDateBean; import org.mapstruct.ap.test.builtin.jodatime.bean.LocalDateTimeBean; @@ -32,8 +28,10 @@ import org.mapstruct.ap.test.builtin.jodatime.mapper.XmlGregorianCalendarToLocalDateTime; import org.mapstruct.ap.test.builtin.jodatime.mapper.XmlGregorianCalendarToLocalTime; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -46,11 +44,10 @@ LocalDateTimeBean.class, XmlGregorianCalendarBean.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey( "689" ) public class JodaTimeTest { - @Test + @ProcessorTest @WithClasses(DateTimeToXmlGregorianCalendar.class) public void shouldMapDateTimeToXmlGregorianCalendar() { @@ -69,7 +66,7 @@ public void shouldMapDateTimeToXmlGregorianCalendar() { assertThat( res.getxMLGregorianCalendar().getTimezone() ).isEqualTo( -60 ); } - @Test + @ProcessorTest @WithClasses(DateTimeToXmlGregorianCalendar.class) public void shouldMapIncompleteDateTimeToXmlGregorianCalendar() { @@ -85,7 +82,7 @@ public void shouldMapIncompleteDateTimeToXmlGregorianCalendar() { assertThat( res.getxMLGregorianCalendar().getMinute() ).isEqualTo( 1 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToDateTime.class) public void shouldMapXmlGregorianCalendarToDateTime() throws Exception { @@ -105,7 +102,7 @@ public void shouldMapXmlGregorianCalendarToDateTime() throws Exception { assertThat( res.getDateTime().getZone().getOffset( null ) ).isEqualTo( 3600000 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToDateTime.class) public void shouldMapXmlGregorianCalendarWithoutTimeZoneToDateTimeWithDefaultTimeZone() throws Exception { @@ -131,7 +128,7 @@ public void shouldMapXmlGregorianCalendarWithoutTimeZoneToDateTimeWithDefaultTim assertThat( res.getDateTime().getZone().getOffset( 0 ) ).isEqualTo( DateTimeZone.getDefault().getOffset( 0 ) ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToDateTime.class) public void shouldMapXmlGregorianCalendarWithoutMillisToDateTime() throws Exception { @@ -157,7 +154,7 @@ public void shouldMapXmlGregorianCalendarWithoutMillisToDateTime() throws Except assertThat( res.getDateTime().getZone().getOffset( null ) ).isEqualTo( 3600000 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToDateTime.class) public void shouldMapXmlGregorianCalendarWithoutMillisAndTimeZoneToDateTimeWithDefaultTimeZone() throws Exception { @@ -182,7 +179,7 @@ public void shouldMapXmlGregorianCalendarWithoutMillisAndTimeZoneToDateTimeWithD assertThat( res.getDateTime().getZone().getOffset( 0 ) ).isEqualTo( DateTimeZone.getDefault().getOffset( 0 ) ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToDateTime.class) public void shouldMapXmlGregorianCalendarWithoutSecondsToDateTime() throws Exception { @@ -207,7 +204,7 @@ public void shouldMapXmlGregorianCalendarWithoutSecondsToDateTime() throws Excep assertThat( res.getDateTime().getZone().getOffset( null ) ).isEqualTo( 3600000 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToDateTime.class) public void shouldMapXmlGregorianCalendarWithoutSecondsAndTimeZoneToDateTimeWithDefaultTimeZone() throws Exception { @@ -231,7 +228,7 @@ public void shouldMapXmlGregorianCalendarWithoutSecondsAndTimeZoneToDateTimeWith assertThat( res.getDateTime().getZone().getOffset( 0 ) ).isEqualTo( DateTimeZone.getDefault().getOffset( 0 ) ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToDateTime.class) public void shouldNotMapXmlGregorianCalendarWithoutMinutes() throws Exception { @@ -247,7 +244,7 @@ public void shouldNotMapXmlGregorianCalendarWithoutMinutes() throws Exception { assertThat( res.getDateTime() ).isNull(); } - @Test + @ProcessorTest @WithClasses({DateTimeToXmlGregorianCalendar.class, XmlGregorianCalendarToDateTime.class}) public void shouldMapRoundTrip() { @@ -263,7 +260,7 @@ public void shouldMapRoundTrip() { } - @Test + @ProcessorTest @WithClasses(LocalDateTimeToXmlGregorianCalendar.class) public void shouldMapLocalDateTimeToXmlGregorianCalendar() { @@ -282,7 +279,7 @@ public void shouldMapLocalDateTimeToXmlGregorianCalendar() { assertThat( res.getxMLGregorianCalendar().getTimezone() ).isEqualTo( DatatypeConstants.FIELD_UNDEFINED ); } - @Test + @ProcessorTest @WithClasses(LocalDateTimeToXmlGregorianCalendar.class) public void shouldMapIncompleteLocalDateTimeToXmlGregorianCalendar() { @@ -301,7 +298,7 @@ public void shouldMapIncompleteLocalDateTimeToXmlGregorianCalendar() { assertThat( res.getxMLGregorianCalendar().getTimezone() ).isEqualTo( DatatypeConstants.FIELD_UNDEFINED ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalDateTime.class) public void shouldMapXmlGregorianCalendarToLocalDateTime() throws Exception { @@ -320,7 +317,7 @@ public void shouldMapXmlGregorianCalendarToLocalDateTime() throws Exception { assertThat( res.getLocalDateTime().getMillisOfSecond() ).isEqualTo( 100 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalDateTime.class) public void shouldMapXmlGregorianCalendarWithoutMillisToLocalDateTime() throws Exception { @@ -344,7 +341,7 @@ public void shouldMapXmlGregorianCalendarWithoutMillisToLocalDateTime() throws E assertThat( res.getLocalDateTime().getMillisOfSecond() ).isEqualTo( 0 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalDateTime.class) public void shouldMapXmlGregorianCalendarWithoutSecondsToLocalDateTime() throws Exception { @@ -368,7 +365,7 @@ public void shouldMapXmlGregorianCalendarWithoutSecondsToLocalDateTime() throws assertThat( res.getLocalDateTime().getMillisOfSecond() ).isEqualTo( 0 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalDateTime.class) public void shouldNotMapXmlGregorianCalendarWithoutMinutesToLocalDateTime() throws Exception { @@ -385,7 +382,7 @@ public void shouldNotMapXmlGregorianCalendarWithoutMinutesToLocalDateTime() thro } - @Test + @ProcessorTest @WithClasses(LocalDateToXmlGregorianCalendar.class) public void shouldMapLocalDateToXmlGregorianCalendar() { @@ -404,7 +401,7 @@ public void shouldMapLocalDateToXmlGregorianCalendar() { assertThat( res.getxMLGregorianCalendar().getTimezone() ).isEqualTo( DatatypeConstants.FIELD_UNDEFINED ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalDate.class) public void shouldMapXmlGregorianCalendarToLocalDate() throws Exception { @@ -422,7 +419,7 @@ public void shouldMapXmlGregorianCalendarToLocalDate() throws Exception { assertThat( res.getLocalDate().getDayOfMonth() ).isEqualTo( 25 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalDate.class) public void shouldNotMapXmlGregorianCalendarWithoutDaysToLocalDate() throws Exception { @@ -439,7 +436,7 @@ public void shouldNotMapXmlGregorianCalendarWithoutDaysToLocalDate() throws Exce } - @Test + @ProcessorTest @WithClasses(LocalTimeToXmlGregorianCalendar.class) public void shouldMapIncompleteLocalTimeToXmlGregorianCalendar() { @@ -458,7 +455,7 @@ public void shouldMapIncompleteLocalTimeToXmlGregorianCalendar() { assertThat( res.getxMLGregorianCalendar().getTimezone() ).isEqualTo( DatatypeConstants.FIELD_UNDEFINED ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalTime.class) public void shouldMapXmlGregorianCalendarToLocalTime() throws Exception { @@ -474,7 +471,7 @@ public void shouldMapXmlGregorianCalendarToLocalTime() throws Exception { assertThat( res.getLocalTime().getMillisOfSecond() ).isEqualTo( 100 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalTime.class) public void shouldMapXmlGregorianCalendarWithoutMillisToLocalTime() throws Exception { @@ -492,7 +489,7 @@ public void shouldMapXmlGregorianCalendarWithoutMillisToLocalTime() throws Excep assertThat( res.getLocalTime().getMillisOfSecond() ).isEqualTo( 0 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalTime.class) public void shouldMapXmlGregorianCalendarWithoutSecondsToLocalTime() throws Exception { @@ -510,7 +507,7 @@ public void shouldMapXmlGregorianCalendarWithoutSecondsToLocalTime() throws Exce assertThat( res.getLocalTime().getMillisOfSecond() ).isEqualTo( 0 ); } - @Test + @ProcessorTest @WithClasses(XmlGregorianCalendarToLocalTime.class) public void shouldNotMapXmlGregorianCalendarWithoutMinutesToLocalTime() throws Exception { diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/CallbackMethodTest.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/CallbackMethodTest.java index e4f363d4c3..7026a335d8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/CallbackMethodTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/CallbackMethodTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.callbacks; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -14,34 +12,33 @@ import java.util.List; import java.util.Map; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; import org.mapstruct.AfterMapping; import org.mapstruct.BeforeMapping; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for callback methods that are defined using {@link BeforeMapping} / {@link AfterMapping} * * @author Andreas Gudian */ -@RunWith( AnnotationProcessorTestRunner.class ) @WithClasses( { ClassContainingCallbacks.class, Invocation.class, Source.class, Target.class, SourceTargetMapper.class, SourceTargetCollectionMapper.class, BaseMapper.class, Qualified.class, SourceEnum.class, TargetEnum.class }) @IssueKey("14") public class CallbackMethodTest { - @Before + @BeforeEach public void reset() { ClassContainingCallbacks.reset(); BaseMapper.reset(); } - @Test + @ProcessorTest public void callbackMethodsForBeanMappingCalled() { SourceTargetMapper.INSTANCE.sourceToTarget( createSource() ); @@ -49,7 +46,7 @@ public void callbackMethodsForBeanMappingCalled() { assertBeanMappingInvocations( BaseMapper.getInvocations() ); } - @Test + @ProcessorTest public void callbackMethodsForBeanMappingWithResultParamCalled() { SourceTargetMapper.INSTANCE.sourceToTarget( createSource(), createEmptyTarget() ); @@ -57,7 +54,7 @@ public void callbackMethodsForBeanMappingWithResultParamCalled() { assertBeanMappingInvocations( BaseMapper.getInvocations() ); } - @Test + @ProcessorTest public void callbackMethodsForIterableMappingCalled() { SourceTargetCollectionMapper.INSTANCE.sourceToTarget( Arrays.asList( createSource() ) ); @@ -65,7 +62,7 @@ public void callbackMethodsForIterableMappingCalled() { assertIterableMappingInvocations( BaseMapper.getInvocations() ); } - @Test + @ProcessorTest public void callbackMethodsForIterableMappingWithResultParamCalled() { SourceTargetCollectionMapper.INSTANCE.sourceToTarget( Arrays.asList( createSource() ), new ArrayList<>() ); @@ -74,7 +71,7 @@ public void callbackMethodsForIterableMappingWithResultParamCalled() { assertIterableMappingInvocations( BaseMapper.getInvocations() ); } - @Test + @ProcessorTest public void callbackMethodsForMapMappingCalled() { SourceTargetCollectionMapper.INSTANCE.sourceToTarget( toMap( "foo", createSource() ) ); @@ -82,7 +79,7 @@ public void callbackMethodsForMapMappingCalled() { assertMapMappingInvocations( BaseMapper.getInvocations() ); } - @Test + @ProcessorTest public void callbackMethodsForMapMappingWithResultParamCalled() { SourceTargetCollectionMapper.INSTANCE.sourceToTarget( toMap( "foo", createSource() ), @@ -92,7 +89,7 @@ public void callbackMethodsForMapMappingWithResultParamCalled() { assertMapMappingInvocations( BaseMapper.getInvocations() ); } - @Test + @ProcessorTest public void qualifiersAreEvaluatedCorrectly() { Source source = createSource(); Target target = SourceTargetMapper.INSTANCE.qualifiedSourceToTarget( source ); @@ -109,7 +106,7 @@ public void qualifiersAreEvaluatedCorrectly() { assertQualifiedInvocations( BaseMapper.getInvocations(), sourceList, targetList ); } - @Test + @ProcessorTest public void callbackMethodsForEnumMappingCalled() { SourceEnum source = SourceEnum.B; TargetEnum target = SourceTargetMapper.INSTANCE.toTargetEnum( source ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/ongeneratedmethods/MappingResultPostprocessorTest.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/ongeneratedmethods/MappingResultPostprocessorTest.java index 2927108002..6ec9ee129d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/ongeneratedmethods/MappingResultPostprocessorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/ongeneratedmethods/MappingResultPostprocessorTest.java @@ -5,15 +5,13 @@ */ package org.mapstruct.ap.test.callbacks.ongeneratedmethods; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -31,10 +29,9 @@ CompanyMapperPostProcessing.class }) @IssueKey("183") -@RunWith(AnnotationProcessorTestRunner.class) public class MappingResultPostprocessorTest { - @Test + @ProcessorTest public void test() { // setup diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java index 6dac09f190..8c391bbfeb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java @@ -5,17 +5,16 @@ */ package org.mapstruct.ap.test.callbacks.returning; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; import java.util.concurrent.atomic.AtomicReference; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.callbacks.returning.NodeMapperContext.ContextListener; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Test case for https://github.com/mapstruct/mapstruct/issues/469 @@ -26,21 +25,22 @@ @WithClasses( { Attribute.class, AttributeDto.class, Node.class, NodeDto.class, NodeMapperDefault.class, NodeMapperWithContext.class, NodeMapperContext.class, Number.class, NumberMapperDefault.class, NumberMapperContext.class, NumberMapperWithContext.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class CallbacksWithReturnValuesTest { - @Test( expected = StackOverflowError.class ) + @ProcessorTest public void mappingWithDefaultHandlingRaisesStackOverflowError() { Node root = buildNodes(); - NodeMapperDefault.INSTANCE.nodeToNodeDto( root ); + assertThatThrownBy( () -> NodeMapperDefault.INSTANCE.nodeToNodeDto( root ) ) + .isInstanceOf( StackOverflowError.class ); } - @Test( expected = StackOverflowError.class ) + @ProcessorTest public void updatingWithDefaultHandlingRaisesStackOverflowError() { Node root = buildNodes(); - NodeMapperDefault.INSTANCE.nodeToNodeDto( root, new NodeDto() ); + assertThatThrownBy( () -> NodeMapperDefault.INSTANCE.nodeToNodeDto( root, new NodeDto() ) ) + .isInstanceOf( StackOverflowError.class ); } - @Test + @ProcessorTest public void mappingWithContextCorrectlyResolvesCycles() { final AtomicReference contextLevel = new AtomicReference<>( null ); ContextListener contextListener = new ContextListener() { @@ -74,7 +74,7 @@ private static Node buildNodes() { return root; } - @Test + @ProcessorTest public void numberMappingWithoutContextDoesNotUseCache() { Number n1 = NumberMapperDefault.INSTANCE.integerToNumber( 2342 ); Number n2 = NumberMapperDefault.INSTANCE.integerToNumber( 2342 ); @@ -82,7 +82,7 @@ public void numberMappingWithoutContextDoesNotUseCache() { assertThat( n1 ).isNotSameAs( n2 ); } - @Test + @ProcessorTest public void numberMappingWithContextUsesCache() { NumberMapperContext.putCache( new Number( 2342 ) ); Number n1 = NumberMapperWithContext.INSTANCE.integerToNumber( 2342 ); @@ -92,7 +92,7 @@ public void numberMappingWithContextUsesCache() { NumberMapperContext.clearCache(); } - @Test + @ProcessorTest public void numberMappingWithContextCallsVisitNumber() { Number n1 = NumberMapperWithContext.INSTANCE.integerToNumber( 1234 ); Number n2 = NumberMapperWithContext.INSTANCE.integerToNumber( 5678 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/typematching/CallbackMethodTypeMatchingTest.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/typematching/CallbackMethodTypeMatchingTest.java index e92dec0105..ebdca6c04c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/typematching/CallbackMethodTypeMatchingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/typematching/CallbackMethodTypeMatchingTest.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.callbacks.typematching; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.callbacks.typematching.CarMapper.CarDto; import org.mapstruct.ap.test.callbacks.typematching.CarMapper.CarEntity; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Andreas Gudian * */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ CarMapper.class }) public class CallbackMethodTypeMatchingTest { - @Test + @ProcessorTest public void callbackMethodAreCalled() { CarEntity carEntity = CarMapper.INSTANCE.toCarEntity( new CarDto() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/CollectionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/CollectionMappingTest.java index d67df4a983..42ef2a25a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/CollectionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/CollectionMappingTest.java @@ -5,9 +5,6 @@ */ package org.mapstruct.ap.test.collection; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; @@ -17,20 +14,20 @@ import java.util.Map; import java.util.Set; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; @WithClasses({ Source.class, Target.class, Colour.class, SourceTargetMapper.class, TestList.class, TestMap.class, StringHolderArrayList.class, StringHolderToLongMap.class, StringHolder.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class CollectionMappingTest { - @Test + @ProcessorTest @IssueKey("6") public void shouldMapNullList() { Source source = new Source(); @@ -41,7 +38,7 @@ public void shouldMapNullList() { assertThat( target.getStringList() ).isNull(); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapNullList() { Target target = new Target(); @@ -52,7 +49,7 @@ public void shouldReverseMapNullList() { assertThat( source.getStringList() ).isNull(); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapList() { Source source = new Source(); @@ -64,7 +61,7 @@ public void shouldMapList() { assertThat( target.getStringList() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("92") public void shouldMapListWithoutSetter() { Source source = new Source(); @@ -76,7 +73,7 @@ public void shouldMapListWithoutSetter() { assertThat( target.getStringListNoSetter() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapList() { Target target = new Target(); @@ -88,7 +85,7 @@ public void shouldReverseMapList() { assertThat( source.getStringList() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapListAsCopy() { Source source = new Source(); @@ -101,7 +98,7 @@ public void shouldMapListAsCopy() { assertThat( source.getStringList() ).isNotEqualTo( target.getStringList() ); } - @Test + @ProcessorTest @IssueKey( "153" ) public void shouldMapListWithClearAndAddAll() { Source source = new Source(); @@ -130,7 +127,7 @@ public void shouldMapListWithClearAndAddAll() { TestList.setAddAllCalled( false ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapListAsCopy() { Target target = new Target(); @@ -142,7 +139,7 @@ public void shouldReverseMapListAsCopy() { assertThat( target.getStringList() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapArrayList() { Source source = new Source(); @@ -154,7 +151,7 @@ public void shouldMapArrayList() { assertThat( target.getStringArrayList() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapArrayList() { Target target = new Target(); @@ -166,7 +163,7 @@ public void shouldReverseMapArrayList() { assertThat( source.getStringArrayList() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapSet() { Source source = new Source(); @@ -178,7 +175,7 @@ public void shouldMapSet() { assertThat( target.getStringSet() ).contains( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapSet() { Target target = new Target(); @@ -190,7 +187,7 @@ public void shouldReverseMapSet() { assertThat( source.getStringSet() ).contains( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapSetAsCopy() { Source source = new Source(); @@ -202,7 +199,7 @@ public void shouldMapSetAsCopy() { assertThat( source.getStringSet() ).containsOnly( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapHashSetAsCopy() { Source source = new Source(); @@ -214,7 +211,7 @@ public void shouldMapHashSetAsCopy() { assertThat( source.getStringHashSet() ).containsOnly( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapSetAsCopy() { Target target = new Target(); @@ -226,7 +223,7 @@ public void shouldReverseMapSetAsCopy() { assertThat( target.getStringSet() ).containsOnly( "Bob", "Alice" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapListToCollection() { Source source = new Source(); @@ -238,7 +235,7 @@ public void shouldMapListToCollection() { assertThat( target.getIntegerCollection() ).containsOnly( 1, 2 ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapListToCollection() { Target target = new Target(); @@ -250,7 +247,7 @@ public void shouldReverseMapListToCollection() { assertThat( source.getIntegerList() ).containsOnly( 1, 2 ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapIntegerSetToRawSet() { Source source = new Source(); @@ -262,7 +259,7 @@ public void shouldMapIntegerSetToRawSet() { assertThat( target.getSet() ).containsOnly( 1, 2 ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapIntegerSetToStringSet() { Source source = new Source(); @@ -274,7 +271,7 @@ public void shouldMapIntegerSetToStringSet() { assertThat( target.getAnotherStringSet() ).containsOnly( "1", "2" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapIntegerSetToStringSet() { Target target = new Target(); @@ -286,7 +283,7 @@ public void shouldReverseMapIntegerSetToStringSet() { assertThat( source.getAnotherIntegerSet() ).containsOnly( 1, 2 ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldMapSetOfEnumToStringSet() { Source source = new Source(); @@ -298,7 +295,7 @@ public void shouldMapSetOfEnumToStringSet() { assertThat( target.getColours() ).containsOnly( "BLUE", "GREEN" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapSetOfEnumToStringSet() { Target target = new Target(); @@ -310,7 +307,7 @@ public void shouldReverseMapSetOfEnumToStringSet() { assertThat( source.getColours() ).containsOnly( Colour.GREEN, Colour.BLUE ); } - @Test + @ProcessorTest public void shouldMapMapAsCopy() { Source source = new Source(); @@ -326,7 +323,7 @@ public void shouldMapMapAsCopy() { assertThat( target.getStringLongMap() ).hasSize( 3 ); } - @Test + @ProcessorTest @IssueKey( "153" ) public void shouldMapMapWithClearAndPutAll() { Source source = new Source(); @@ -358,7 +355,7 @@ public void shouldMapMapWithClearAndPutAll() { TestMap.setPuttAllCalled( false ); } - @Test + @ProcessorTest @IssueKey("87") public void shouldMapIntegerSetToNumberSet() { Set numbers = SourceTargetMapper.INSTANCE @@ -368,7 +365,7 @@ public void shouldMapIntegerSetToNumberSet() { assertThat( numbers ).containsOnly( 123, 456 ); } - @Test + @ProcessorTest @IssueKey("732") public void shouldEnumSetAsCopy() { Source source = new Source(); @@ -381,7 +378,7 @@ public void shouldEnumSetAsCopy() { assertThat( target.getEnumSet() ).containsOnly( Colour.BLUE, Colour.GREEN ); } - @Test + @ProcessorTest @IssueKey("853") public void shouldMapNonGenericList() { Source source = new Source(); @@ -406,7 +403,7 @@ public void shouldMapNonGenericList() { assertThat( mappedSource.getStringList3() ).containsExactly( "Bill", "Bob" ); } - @Test + @ProcessorTest @IssueKey("853") public void shouldMapNonGenericMap() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/AdderTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/AdderTest.java index d3c667099c..b555970770 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/AdderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/AdderTest.java @@ -5,16 +5,10 @@ */ package org.mapstruct.ap.test.collection.adder; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import java.util.ArrayList; import java.util.Arrays; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.collection.adder._target.AdderUsageObserver; import org.mapstruct.ap.test.collection.adder._target.IndoorPet; import org.mapstruct.ap.test.collection.adder._target.OutdoorPet; @@ -34,10 +28,15 @@ import org.mapstruct.ap.test.collection.adder.source.SourceTeeth; import org.mapstruct.ap.test.collection.adder.source.SourceWithPets; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * @author Sjaak Derksen */ @@ -70,18 +69,17 @@ Source2Target2Mapper.class, Foo.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class AdderTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( SourceTargetMapper.class, SourceTargetMapperStrategyDefault.class, SourceTargetMapperStrategySetterPreferred.class ); @IssueKey("241") - @Test + @ProcessorTest public void testAdd() throws DogException { AdderUsageObserver.setUsed( false ); @@ -95,28 +93,30 @@ public void testAdd() throws DogException { assertTrue( AdderUsageObserver.isUsed() ); } - @Test(expected = DogException.class) + @ProcessorTest public void testAddWithExceptionInThrowsClause() throws DogException { AdderUsageObserver.setUsed( false ); Source source = new Source(); source.setPets( Arrays.asList( "dog" ) ); - SourceTargetMapper.INSTANCE.toTarget( source ); + assertThatThrownBy( () -> SourceTargetMapper.INSTANCE.toTarget( source ) ) + .isInstanceOf( DogException.class ); } - @Test(expected = RuntimeException.class) + @ProcessorTest public void testAddWithExceptionNotInThrowsClause() throws DogException { AdderUsageObserver.setUsed( false ); Source source = new Source(); source.setPets( Arrays.asList( "cat" ) ); - SourceTargetMapper.INSTANCE.toTarget( source ); + assertThatThrownBy( () -> SourceTargetMapper.INSTANCE.toTarget( source ) ) + .isInstanceOf( RuntimeException.class ); } @IssueKey("241") - @Test + @ProcessorTest public void testAddWithExistingTarget() { AdderUsageObserver.setUsed( false ); @@ -134,7 +134,7 @@ public void testAddWithExistingTarget() { assertTrue( AdderUsageObserver.isUsed() ); } - @Test + @ProcessorTest public void testShouldUseDefaultStrategy() throws DogException { AdderUsageObserver.setUsed( false ); @@ -148,7 +148,7 @@ public void testShouldUseDefaultStrategy() throws DogException { assertFalse( AdderUsageObserver.isUsed() ); } - @Test + @ProcessorTest public void testShouldPreferSetterStrategyButThereIsNone() throws DogException { AdderUsageObserver.setUsed( false ); @@ -162,7 +162,7 @@ public void testShouldPreferSetterStrategyButThereIsNone() throws DogException { assertTrue( AdderUsageObserver.isUsed() ); } - @Test + @ProcessorTest public void testShouldPreferHumanSingular() { AdderUsageObserver.setUsed( false ); @@ -177,7 +177,7 @@ public void testShouldPreferHumanSingular() { assertTrue( AdderUsageObserver.isUsed() ); } - @Test + @ProcessorTest public void testShouldFallBackToDaliSingularInAbsenceOfHumanSingular() { AdderUsageObserver.setUsed( false ); @@ -191,7 +191,7 @@ public void testShouldFallBackToDaliSingularInAbsenceOfHumanSingular() { assertTrue( AdderUsageObserver.isUsed() ); } - @Test + @ProcessorTest public void testAddReverse() { AdderUsageObserver.setUsed( false ); @@ -204,7 +204,7 @@ public void testAddReverse() { assertThat( target.getPets().get( 0 ) ).isEqualTo( "cat" ); } - @Test + @ProcessorTest public void testAddOnlyGetter() throws DogException { AdderUsageObserver.setUsed( false ); @@ -218,7 +218,7 @@ public void testAddOnlyGetter() throws DogException { assertTrue( AdderUsageObserver.isUsed() ); } - @Test + @ProcessorTest public void testAddViaTargetType() { AdderUsageObserver.setUsed( false ); @@ -234,7 +234,7 @@ public void testAddViaTargetType() { } @IssueKey("242") - @Test + @ProcessorTest public void testSingleElementSource() { AdderUsageObserver.setUsed( false ); @@ -249,7 +249,7 @@ public void testSingleElementSource() { } @IssueKey( "310" ) - @Test + @ProcessorTest public void testMissingImport() { generatedSource.addComparisonToFixtureFor( Source2Target2Mapper.class ); @@ -262,7 +262,7 @@ public void testMissingImport() { } @IssueKey("1478") - @Test + @ProcessorTest public void useIterationNameFromSource() { generatedSource.addComparisonToFixtureFor( SourceTargetMapperWithDifferentProperties.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java index a6ba036e81..9d360656a0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java @@ -5,9 +5,6 @@ */ package org.mapstruct.ap.test.collection.defaultimplementation; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -23,14 +20,15 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentNavigableMap; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; +import static org.assertj.core.api.Assertions.entry; + @WithClasses({ Source.class, Target.class, @@ -38,14 +36,13 @@ TargetFoo.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class DefaultCollectionImplementationTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource() + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() .addComparisonToFixtureFor( SourceTargetMapper.class ); - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForConcurrentMap() { ConcurrentMap target = @@ -54,7 +51,7 @@ public void shouldUseDefaultImplementationForConcurrentMap() { assertResultMap( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForConcurrentNavigableMap() { ConcurrentNavigableMap target = @@ -63,7 +60,7 @@ public void shouldUseDefaultImplementationForConcurrentNavigableMap() { assertResultMap( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForMap() { Map target = SourceTargetMapper.INSTANCE.sourceFooMapToTargetFooMap( createSourceFooMap() ); @@ -71,7 +68,7 @@ public void shouldUseDefaultImplementationForMap() { assertResultMap( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForNavigableMap() { NavigableMap target = @@ -80,7 +77,7 @@ public void shouldUseDefaultImplementationForNavigableMap() { assertResultMap( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForSortedMap() { SortedMap target = @@ -89,7 +86,7 @@ public void shouldUseDefaultImplementationForSortedMap() { assertResultMap( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForNaviableSet() { NavigableSet target = @@ -98,7 +95,7 @@ public void shouldUseDefaultImplementationForNaviableSet() { assertResultList( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForCollection() { Collection target = @@ -107,7 +104,7 @@ public void shouldUseDefaultImplementationForCollection() { assertResultList( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForIterable() { Iterable target = @@ -116,7 +113,7 @@ public void shouldUseDefaultImplementationForIterable() { assertResultList( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForList() { List target = SourceTargetMapper.INSTANCE.sourceFoosToTargetFoos( createSourceFooList() ); @@ -124,7 +121,7 @@ public void shouldUseDefaultImplementationForList() { assertResultList( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForSet() { Set target = @@ -133,7 +130,7 @@ public void shouldUseDefaultImplementationForSet() { assertResultList( target ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldUseDefaultImplementationForSortedSet() { SortedSet target = @@ -142,7 +139,7 @@ public void shouldUseDefaultImplementationForSortedSet() { assertResultList( target ); } - @Test + @ProcessorTest @IssueKey("19") public void shouldUseTargetParameterForMapping() { List target = new ArrayList<>(); @@ -154,7 +151,7 @@ public void shouldUseTargetParameterForMapping() { assertResultList( target ); } - @Test + @ProcessorTest @IssueKey("19") public void shouldUseAndReturnTargetParameterForMapping() { List target = new ArrayList<>(); @@ -166,7 +163,7 @@ public void shouldUseAndReturnTargetParameterForMapping() { assertResultList( target ); } - @Test + @ProcessorTest @IssueKey("92") public void shouldUseDefaultImplementationForListWithoutSetter() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/NoSetterCollectionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/NoSetterCollectionMappingTest.java index 1d9747854b..8723969723 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/NoSetterCollectionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/NoSetterCollectionMappingTest.java @@ -5,29 +5,26 @@ */ package org.mapstruct.ap.test.collection.defaultimplementation; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * @author Andreas Gudian * */ @WithClasses( { NoSetterMapper.class, NoSetterSource.class, NoSetterTarget.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class NoSetterCollectionMappingTest { - @Test + @ProcessorTest @IssueKey( "220" ) public void compilesAndMapsCorrectly() { NoSetterSource source = new NoSetterSource(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java index e386be191c..d9861be87e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/erroneous/ErroneousCollectionMappingTest.java @@ -7,26 +7,23 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.NoProperties; import org.mapstruct.ap.test.WithProperties; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Test for illegal mappings between collection types, iterable and non-iterable types etc. * * @author Gunnar Morling */ -@RunWith(AnnotationProcessorTestRunner.class) public class ErroneousCollectionMappingTest { - @Test + @ProcessorTest @IssueKey("6") @WithClasses({ ErroneousCollectionToNonCollectionMapper.class, Source.class }) @ExpectedCompilationOutcome( @@ -49,7 +46,7 @@ public class ErroneousCollectionMappingTest { public void shouldFailToGenerateImplementationBetweenCollectionAndNonCollectionWithResultTypeFromJava() { } - @Test + @ProcessorTest @IssueKey("729") @WithClasses({ ErroneousCollectionToPrimitivePropertyMapper.class, Source.class, Target.class }) @ExpectedCompilationOutcome( @@ -65,7 +62,7 @@ public void shouldFailToGenerateImplementationBetweenCollectionAndNonCollectionW public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { } - @Test + @ProcessorTest @IssueKey("417") @WithClasses({ EmptyItererableMappingMapper.class }) @ExpectedCompilationOutcome( @@ -81,7 +78,7 @@ public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { public void shouldFailOnEmptyIterableAnnotation() { } - @Test + @ProcessorTest @IssueKey("417") @WithClasses({ EmptyMapMappingMapper.class }) @ExpectedCompilationOutcome( @@ -98,7 +95,7 @@ public void shouldFailOnEmptyIterableAnnotation() { public void shouldFailOnEmptyMapAnnotation() { } - @Test + @ProcessorTest @IssueKey("459") @WithClasses({ ErroneousCollectionNoElementMappingFound.class, NoProperties.class, WithProperties.class }) @ExpectedCompilationOutcome( @@ -115,7 +112,7 @@ public void shouldFailOnEmptyMapAnnotation() { public void shouldFailOnNoElementMappingFound() { } - @Test + @ProcessorTest @IssueKey("993") @WithClasses({ ErroneousCollectionNoElementMappingFoundDisabledAuto.class }) @ExpectedCompilationOutcome( @@ -131,7 +128,7 @@ public void shouldFailOnNoElementMappingFound() { public void shouldFailOnNoElementMappingFoundWithDisabledAuto() { } - @Test + @ProcessorTest @IssueKey("459") @WithClasses({ ErroneousCollectionNoKeyMappingFound.class, NoProperties.class, WithProperties.class }) @ExpectedCompilationOutcome( @@ -148,7 +145,7 @@ public void shouldFailOnNoElementMappingFoundWithDisabledAuto() { public void shouldFailOnNoKeyMappingFound() { } - @Test + @ProcessorTest @IssueKey("993") @WithClasses({ ErroneousCollectionNoKeyMappingFoundDisabledAuto.class }) @ExpectedCompilationOutcome( @@ -164,7 +161,7 @@ public void shouldFailOnNoKeyMappingFound() { public void shouldFailOnNoKeyMappingFoundWithDisabledAuto() { } - @Test + @ProcessorTest @IssueKey("459") @WithClasses({ ErroneousCollectionNoValueMappingFound.class, NoProperties.class, WithProperties.class }) @ExpectedCompilationOutcome( @@ -181,7 +178,7 @@ public void shouldFailOnNoKeyMappingFoundWithDisabledAuto() { public void shouldFailOnNoValueMappingFound() { } - @Test + @ProcessorTest @IssueKey("993") @WithClasses({ ErroneousCollectionNoValueMappingFoundDisabledAuto.class }) @ExpectedCompilationOutcome( diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java index 9fe6fec56a..67cabc9f95 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/forged/CollectionMappingTest.java @@ -5,24 +5,19 @@ */ package org.mapstruct.ap.test.collection.forged; - -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Map; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; +import com.google.common.collect.ImmutableMap; import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -import com.google.common.collect.ImmutableMap; +import static org.assertj.core.api.Assertions.assertThat; /** * Test for mappings between collection types, @@ -30,10 +25,9 @@ * @author Sjaak Derksen */ @IssueKey( "4" ) -@RunWith(AnnotationProcessorTestRunner.class) public class CollectionMappingTest { - @Test + @ProcessorTest @WithClasses({ CollectionMapper.class, Source.class, Target.class }) public void shouldForgeNewIterableMappingMethod() { @@ -52,7 +46,7 @@ public void shouldForgeNewIterableMappingMethod() { assertThat( source2.publicFooSet ).isEqualTo( Collections.asSet( "3", "4" ) ); } - @Test + @ProcessorTest @WithClasses({ CollectionMapper.class, Source.class, Target.class }) public void shouldForgeNewMapMappingMethod() { @@ -74,7 +68,7 @@ public void shouldForgeNewMapMappingMethod() { assertThat( source2.publicBarMap ).isEqualTo( source.publicBarMap ); } - @Test + @ProcessorTest @WithClasses({ ErroneousCollectionNonMappableSetMapper.class, ErroneousNonMappableSetSource.class, @@ -96,7 +90,7 @@ public void shouldForgeNewMapMappingMethod() { public void shouldGenerateNonMappleMethodForSetMapping() { } - @Test + @ProcessorTest @WithClasses({ ErroneousCollectionNonMappableMapMapper.class, ErroneousNonMappableMapSource.class, ErroneousNonMappableMapTarget.class, @@ -123,7 +117,7 @@ public void shouldGenerateNonMappleMethodForSetMapping() { public void shouldGenerateNonMappleMethodForMapMapping() { } - @Test + @ProcessorTest @IssueKey( "640" ) @WithClasses({ CollectionMapper.class, Source.class, Target.class }) public void shouldForgeNewIterableMappingMethodReturnNullOnNullSource() { @@ -143,7 +137,7 @@ public void shouldForgeNewIterableMappingMethodReturnNullOnNullSource() { assertThat( source2.publicFooSet ).isNull(); } - @Test + @ProcessorTest @IssueKey( "640" ) @WithClasses({ CollectionMapper.class, Source.class, Target.class }) public void shouldForgeNewMapMappingMethodReturnNullOnNullSource() { @@ -163,7 +157,7 @@ public void shouldForgeNewMapMappingMethodReturnNullOnNullSource() { assertThat( source2.publicBarMap ).isNull(); } - @Test + @ProcessorTest @IssueKey( "640" ) @WithClasses({ CollectionMapperNullValueMappingReturnDefault.class, Source.class, Target.class }) public void shouldForgeNewIterableMappingMethodReturnEmptyOnNullSource() { @@ -185,7 +179,7 @@ public void shouldForgeNewIterableMappingMethodReturnEmptyOnNullSource() { assertThat( source2.publicBarMap ).isEmpty(); } - @Test + @ProcessorTest @IssueKey( "640" ) @WithClasses({ CollectionMapperNullValueMappingReturnDefault.class, Source.class, Target.class }) public void shouldForgeNewMapMappingMethodReturnEmptyOnNullSource() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java index 85dff16393..b3d2096bc3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java @@ -5,30 +5,27 @@ */ package org.mapstruct.ap.test.collection.immutabletarget; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; import java.util.Collections; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({CupboardDto.class, CupboardEntity.class, CupboardMapper.class}) @IssueKey( "1126" ) public class ImmutableProductTest { - @Test + @ProcessorTest public void shouldHandleImmutableTarget() { CupboardDto in = new CupboardDto(); @@ -42,7 +39,7 @@ public void shouldHandleImmutableTarget() { assertThat( out.getContent() ).containsExactly( "cups", "soucers" ); } - @Test + @ProcessorTest @WithClasses({ ErroneousCupboardMapper.class, CupboardEntityOnlyGetter.class diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/IterableToNonIterableMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/IterableToNonIterableMappingTest.java index feb0948c65..892e24df4c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/IterableToNonIterableMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletononiterable/IterableToNonIterableMappingTest.java @@ -10,18 +10,15 @@ import java.util.Arrays; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @WithClasses({ Source.class, Target.class, SourceTargetMapper.class, StringListMapper.class, FruitsMenu.class, FruitSalad.class, Fruit.class, FruitsMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class IterableToNonIterableMappingTest { - @Test + @ProcessorTest @IssueKey("6") public void shouldMapStringListToStringUsingCustomMapper() { Source source = new Source(); @@ -34,7 +31,7 @@ public void shouldMapStringListToStringUsingCustomMapper() { assertThat( target.publicNames ).isEqualTo( "Alice-Bob-Jim" ); } - @Test + @ProcessorTest @IssueKey("6") public void shouldReverseMapStringListToStringUsingCustomMapper() { Target target = new Target(); @@ -48,7 +45,7 @@ public void shouldReverseMapStringListToStringUsingCustomMapper() { assertThat( source.publicNames ).isEqualTo( Arrays.asList( "Alice", "Bob", "Jim" ) ); } - @Test + @ProcessorTest @IssueKey("607") public void shouldMapIterableToNonIterable() { List fruits = Arrays.asList( new Fruit( "mango" ), new Fruit( "apple" ), @@ -60,7 +57,7 @@ public void shouldMapIterableToNonIterable() { assertThat( salad.getFruits().get( 2 ).getType() ).isEqualTo( "banana" ); } - @Test + @ProcessorTest @IssueKey("607") public void shouldMapNonIterableToIterable() { List fruits = Arrays.asList( new Fruit( "mango" ), new Fruit( "apple" ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java index a9a17e5416..ddba51f826 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java @@ -5,21 +5,19 @@ */ package org.mapstruct.ap.test.collection.map; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.collection.map.other.ImportedType; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * Test for implementation of {@code Map} mapping methods. @@ -28,10 +26,9 @@ */ @WithClasses({ SourceTargetMapper.class, CustomNumberMapper.class, Source.class, Target.class, ImportedType.class }) @IssueKey("44") -@RunWith(AnnotationProcessorTestRunner.class) public class MapMappingTest { - @Test + @ProcessorTest public void shouldCreateMapMethodImplementation() { Map values = new HashMap<>(); values.put( 42L, new GregorianCalendar( 1980, Calendar.JANUARY, 1 ).getTime() ); @@ -47,7 +44,7 @@ public void shouldCreateMapMethodImplementation() { ); } - @Test + @ProcessorTest public void shouldCreateReverseMapMethodImplementation() { Map values = createStringStringMap(); @@ -56,7 +53,7 @@ public void shouldCreateReverseMapMethodImplementation() { assertResult( target ); } - @Test + @ProcessorTest @IssueKey("19") public void shouldCreateMapMethodImplementationWithTargetParameter() { Map values = createStringStringMap(); @@ -69,7 +66,7 @@ public void shouldCreateMapMethodImplementationWithTargetParameter() { assertResult( target ); } - @Test + @ProcessorTest @IssueKey("19") public void shouldCreateMapMethodImplementationWithReturnedTargetParameter() { Map values = createStringStringMap(); @@ -101,7 +98,7 @@ private Map createStringStringMap() { return values; } - @Test + @ProcessorTest public void shouldInvokeMapMethodImplementationForMapTypedProperty() { Map values = new HashMap<>(); values.put( 42L, new GregorianCalendar( 1980, Calendar.JANUARY, 1 ).getTime() ); @@ -130,7 +127,7 @@ public void shouldInvokeMapMethodImplementationForMapTypedProperty() { ); } - @Test + @ProcessorTest public void shouldInvokeReverseMapMethodImplementationForMapTypedProperty() { Map values = createStringStringMap(); @@ -164,7 +161,7 @@ private Map createIntIntMap() { return values; } - @Test + @ProcessorTest @IssueKey("87") public void shouldCreateMapMethodImplementationWithoutConversionOrElementMappingMethod() { Map values = createIntIntMap(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java index aa09cdbc4f..37ed4606f6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java @@ -5,37 +5,33 @@ */ package org.mapstruct.ap.test.collection.wildcard; -import static org.assertj.core.api.Assertions.assertThat; - import java.math.BigDecimal; - import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; +import static org.assertj.core.api.Assertions.assertThat; + /** * Reproducer for https://github.com/mapstruct/mapstruct/issues/527. * * @author Sjaak Derksen */ @IssueKey("527") -@RunWith(AnnotationProcessorTestRunner.class) public class WildCardTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses({ ExtendsBoundSourceTargetMapper.class, ExtendsBoundSource.class, @@ -57,7 +53,7 @@ public void shouldGenerateExtendsBoundSourceForgedIterableMethod() { .doesNotContain( "? extends org.mapstruct.ap.test.collection.wildcard.Idea" ); } - @Test + @ProcessorTest @WithClasses({ SourceSuperBoundTargetMapper.class, Source.class, @@ -79,7 +75,7 @@ public void shouldGenerateSuperBoundTargetForgedIterableMethod() { .doesNotContain( "? super org.mapstruct.ap.test.collection.wildcard.Idea" ); } - @Test + @ProcessorTest @WithClasses({ ErroneousIterableSuperBoundSourceMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -93,7 +89,7 @@ public void shouldGenerateSuperBoundTargetForgedIterableMethod() { public void shouldFailOnSuperBoundSource() { } - @Test + @ProcessorTest @WithClasses({ ErroneousIterableExtendsBoundTargetMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -107,7 +103,7 @@ public void shouldFailOnSuperBoundSource() { public void shouldFailOnExtendsBoundTarget() { } - @Test + @ProcessorTest @WithClasses({ ErroneousIterableTypeVarBoundMapperOnMethod.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -121,7 +117,7 @@ public void shouldFailOnExtendsBoundTarget() { public void shouldFailOnTypeVarSource() { } - @Test + @ProcessorTest @WithClasses({ ErroneousIterableTypeVarBoundMapperOnMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -135,7 +131,7 @@ public void shouldFailOnTypeVarSource() { public void shouldFailOnTypeVarTarget() { } - @Test + @ProcessorTest @WithClasses( { BeanMapper.class, GoodIdea.class, CunningPlan.class } ) public void shouldMapBean() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapperTest.java index 128abccf55..5eaf44765d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapperTest.java @@ -5,24 +5,22 @@ */ package org.mapstruct.ap.test.complex; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.complex._target.CarDto; import org.mapstruct.ap.test.complex._target.PersonDto; import org.mapstruct.ap.test.complex.other.DateMapper; import org.mapstruct.ap.test.complex.source.Car; import org.mapstruct.ap.test.complex.source.Category; import org.mapstruct.ap.test.complex.source.Person; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ Car.class, @@ -33,15 +31,14 @@ Category.class, DateMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class CarMapperTest { - @Test + @ProcessorTest public void shouldProvideMapperInstance() { assertThat( CarMapper.INSTANCE ).isNotNull(); } - @Test + @ProcessorTest public void shouldMapAttributeByName() { //given Car car = new Car( @@ -60,7 +57,7 @@ public void shouldMapAttributeByName() { assertThat( carDto.getMake() ).isEqualTo( car.getMake() ); } - @Test + @ProcessorTest public void shouldMapReferenceAttribute() { //given Car car = new Car( @@ -80,7 +77,7 @@ public void shouldMapReferenceAttribute() { assertThat( carDto.getDriver().getName() ).isEqualTo( "Bob" ); } - @Test + @ProcessorTest public void shouldReverseMapReferenceAttribute() { //given CarDto carDto = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ), new ArrayList<>() ); @@ -94,7 +91,7 @@ public void shouldReverseMapReferenceAttribute() { assertThat( car.getDriver().getName() ).isEqualTo( "Bob" ); } - @Test + @ProcessorTest public void shouldMapAttributeWithCustomMapping() { //given Car car = new Car( @@ -113,7 +110,7 @@ public void shouldMapAttributeWithCustomMapping() { assertThat( carDto.getSeatCount() ).isEqualTo( car.getNumberOfSeats() ); } - @Test + @ProcessorTest public void shouldConsiderCustomMappingForReverseMapping() { //given CarDto carDto = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ), new ArrayList<>() ); @@ -126,7 +123,7 @@ public void shouldConsiderCustomMappingForReverseMapping() { assertThat( car.getNumberOfSeats() ).isEqualTo( carDto.getSeatCount() ); } - @Test + @ProcessorTest public void shouldApplyConverter() { //given Car car = new Car( @@ -145,7 +142,7 @@ public void shouldApplyConverter() { assertThat( carDto.getManufacturingYear() ).isEqualTo( "1980" ); } - @Test + @ProcessorTest public void shouldApplyConverterForReverseMapping() { //given CarDto carDto = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ), new ArrayList<>() ); @@ -160,7 +157,7 @@ public void shouldApplyConverterForReverseMapping() { ); } - @Test + @ProcessorTest public void shouldMapIterable() { //given Car car1 = new Car( @@ -196,7 +193,7 @@ public void shouldMapIterable() { assertThat( dtos.get( 1 ).getDriver().getName() ).isEqualTo( "Bill" ); } - @Test + @ProcessorTest public void shouldReverseMapIterable() { //given CarDto car1 = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ), new ArrayList<>() ); @@ -224,7 +221,7 @@ public void shouldReverseMapIterable() { assertThat( cars.get( 1 ).getDriver().getName() ).isEqualTo( "Bill" ); } - @Test + @ProcessorTest public void shouldMapIterableAttribute() { //given Car car = new Car( @@ -246,7 +243,7 @@ public void shouldMapIterableAttribute() { assertThat( dto.getPassengers().get( 1 ).getName() ).isEqualTo( "Bill" ); } - @Test + @ProcessorTest public void shouldReverseMapIterableAttribute() { //given CarDto carDto = new CarDto( @@ -268,7 +265,7 @@ public void shouldReverseMapIterableAttribute() { assertThat( car.getPassengers().get( 1 ).getName() ).isEqualTo( "Bill" ); } - @Test + @ProcessorTest public void shouldMapEnumToString() { //given Car car = new Car(); @@ -281,7 +278,7 @@ public void shouldMapEnumToString() { assertThat( carDto.getCategory() ).isEqualTo( "CONVERTIBLE" ); } - @Test + @ProcessorTest public void shouldMapStringToEnum() { //given CarDto carDto = new CarDto(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java index 300f97bce4..0fc791fb13 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java @@ -8,15 +8,13 @@ import java.util.Arrays; import java.util.Collections; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; @@ -29,13 +27,12 @@ BasicEmployee.class, BasicEmployeeDto.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ConditionalMappingTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses({ ConditionalMethodInMapper.class }) @@ -53,7 +50,7 @@ public void conditionalMethodInMapper() { assertThat( employee.getName() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ ConditionalMethodAndBeanPresenceCheckMapper.class }) @@ -70,7 +67,7 @@ public void conditionalMethodAndBeanPresenceCheckMapper() { assertThat( employee.getName() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ ConditionalMethodInUsesMapper.class }) @@ -87,7 +84,7 @@ public void conditionalMethodInUsesMapper() { assertThat( employee.getName() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ ConditionalMethodInUsesStaticMapper.class }) @@ -104,7 +101,7 @@ public void conditionalMethodInUsesStaticMapper() { assertThat( employee.getName() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ ConditionalMethodInContextMapper.class }) @@ -122,7 +119,7 @@ public void conditionalMethodInUsesContextMapper() { assertThat( employee.getName() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ ConditionalMethodWithSourceParameterMapper.class }) @@ -136,7 +133,7 @@ public void conditionalMethodWithSourceParameter() { assertThat( employee.getName() ).isEqualTo( "Tester" ); } - @Test + @ProcessorTest @WithClasses({ ConditionalMethodWithSourceParameterAndValueMapper.class }) @@ -155,7 +152,7 @@ public void conditionalMethodWithSourceParameterAndValue() { assertThat( employee.getName() ).isEqualTo( "Tester" ); } - @Test + @ProcessorTest @WithClasses({ ErroneousAmbiguousConditionalMethodMapper.class }) @@ -177,7 +174,7 @@ public void ambiguousConditionalMethod() { } - @Test + @ProcessorTest @WithClasses({ ConditionalMethodForCollectionMapper.class }) @@ -205,7 +202,7 @@ public void conditionalMethodForCollection() { .containsExactly( "Test", "Test Vol. 2" ); } - @Test + @ProcessorTest @WithClasses({ OptionalLikeConditionalMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java index cf38d85dd1..60179cdeac 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java @@ -5,17 +5,15 @@ */ package org.mapstruct.ap.test.conditional.expression; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.conditional.Employee; import org.mapstruct.ap.test.conditional.EmployeeDto; import org.mapstruct.ap.test.conditional.basic.BasicEmployee; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -27,10 +25,9 @@ Employee.class, EmployeeDto.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ConditionalExpressionTest { - @Test + @ProcessorTest @WithClasses({ ConditionalMethodsInUtilClassMapper.class }) @@ -62,7 +59,7 @@ public void conditionalExpressionInStaticClassMethod() { assertThat( employee.getSsid() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ SimpleConditionalExpressionMapper.class }) @@ -77,7 +74,7 @@ public void conditionalSimpleExpression() { assertThat( target.getValue() ).isEqualTo( 0 ); } - @Test + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java index 3dc6d7c8aa..5d13787e65 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.conditional.qualifier; -import org.junit.Test; -import org.junit.runner.RunWith; 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 org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -23,10 +21,9 @@ Employee.class, EmployeeDto.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ConditionalQualifierTest { - @Test + @ProcessorTest @WithClasses({ ConditionalMethodWithSourceParameterMapper.class }) @@ -58,7 +55,7 @@ public void conditionalMethodWithSourceParameter() { assertThat( employee.getSsid() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ ConditionalMethodWithClassQualifiersMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesTest.java index b58d55f1ee..3e26b442ee 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/constructorproperties/SimpleConstructorPropertiesTest.java @@ -7,12 +7,10 @@ import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.constructor.ConstructorProperties; import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -25,10 +23,9 @@ PersonDto.class, SimpleConstructorPropertiesMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class SimpleConstructorPropertiesTest { - @Test + @ProcessorTest public void mapDefault() { PersonDto source = new PersonDto(); source.setName( "Bob" ); @@ -48,7 +45,7 @@ public void mapDefault() { assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); } - @Test + @ProcessorTest public void mapWithConstants() { PersonDto source = new PersonDto(); source.setName( "Bob" ); @@ -68,7 +65,7 @@ public void mapWithConstants() { assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); } - @Test + @ProcessorTest public void mapWithExpressions() { PersonDto source = new PersonDto(); source.setName( "Bob" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorTest.java index ac447d7878..25f4fb8b1d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/defaultannotated/SimpleDefaultAnnotatedConstructorTest.java @@ -7,12 +7,10 @@ import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.constructor.Default; import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -25,10 +23,9 @@ PersonDto.class, SimpleDefaultAnnotatedConstructorMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class SimpleDefaultAnnotatedConstructorTest { - @Test + @ProcessorTest public void mapDefault() { PersonDto source = new PersonDto(); source.setName( "Bob" ); @@ -44,7 +41,7 @@ public void mapDefault() { assertThat( target.getAge() ).isEqualTo( 30 ); } - @Test + @ProcessorTest public void mapWithConstants() { PersonDto source = new PersonDto(); source.setName( "Bob" ); @@ -61,7 +58,7 @@ public void mapWithConstants() { assertThat( target.getAge() ).isEqualTo( 25 ); } - @Test + @ProcessorTest public void mapWithExpressions() { PersonDto source = new PersonDto(); source.setName( "Bob" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java index eac6188ab6..2799e10847 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.constructor.erroneous; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.constructor.ConstructorProperties; import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) public class ErroneousConstructorTest { - @Test + @ProcessorTest @WithClasses({ ErroneousAmbiguousConstructorsMapper.class, PersonDto.class @@ -39,7 +36,7 @@ public class ErroneousConstructorTest { public void shouldUseMultipleConstructors() { } - @Test + @ProcessorTest @WithClasses({ ConstructorProperties.class, ErroneousConstructorPropertiesMapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMappingTest.java index dde307adf7..7611cc2364 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/manysourcearguments/ManySourceArgumentsConstructorMappingTest.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.constructor.manysourcearguments; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.constructor.Person; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith( AnnotationProcessorTestRunner.class ) @WithClasses( { ManySourceArgumentsConstructorMapper.class, Person.class, } ) public class ManySourceArgumentsConstructorMappingTest { - @Test + @ProcessorTest public void shouldCorrectlyUseDefaultValueForSourceParameters() { Person person = ManySourceArgumentsConstructorMapper.INSTANCE.map( null, "Test Valley" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersTest.java index 2c24e33cb5..dde9b798f4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/mixed/ConstructorMixedWithSettersTest.java @@ -7,11 +7,9 @@ import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -23,10 +21,9 @@ PersonDto.class, ConstructorMixedWithSettersMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ConstructorMixedWithSettersTest { - @Test + @ProcessorTest public void mapDefault() { PersonDto source = new PersonDto(); source.setName( "Bob" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/NestedSourcePropertiesConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/NestedSourcePropertiesConstructorTest.java index 4fe43506ab..653fccd118 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/NestedSourcePropertiesConstructorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/NestedSourcePropertiesConstructorTest.java @@ -7,9 +7,7 @@ import java.util.Collections; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; import org.mapstruct.ap.test.constructor.nestedsource.source.Artist; import org.mapstruct.ap.test.constructor.nestedsource.source.Chart; @@ -17,8 +15,8 @@ import org.mapstruct.ap.test.constructor.nestedsource.source.Song; import org.mapstruct.ap.test.constructor.nestedsource.source.Studio; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -28,13 +26,12 @@ */ @WithClasses({ Song.class, Artist.class, Chart.class, Label.class, Studio.class, ChartEntry.class }) @IssueKey("73") -@RunWith(AnnotationProcessorTestRunner.class) public class NestedSourcePropertiesConstructorTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntry.class }) public void shouldGenerateImplementationForPropertyNamesOnly() { generatedSource.addComparisonToFixtureFor( ArtistToChartEntry.class ); @@ -58,7 +55,7 @@ public void shouldGenerateImplementationForPropertyNamesOnly() { assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntry.class }) public void shouldGenerateImplementationForMultipleParam() { @@ -113,7 +110,7 @@ public void shouldGenerateImplementationForMultipleParam() { assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntry.class }) public void shouldPickPropertyNameOverParameterName() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ReversingNestedSourcePropertiesConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ReversingNestedSourcePropertiesConstructorTest.java index 231a4fcad7..73b23c5677 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ReversingNestedSourcePropertiesConstructorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedsource/ReversingNestedSourcePropertiesConstructorTest.java @@ -7,8 +7,6 @@ import java.util.Collections; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.constructor.nestedsource._target.BaseChartEntry; import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryComposed; @@ -21,8 +19,8 @@ import org.mapstruct.ap.test.constructor.nestedsource.source.Song; import org.mapstruct.ap.test.constructor.nestedsource.source.Studio; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -31,10 +29,9 @@ */ @IssueKey("73") @WithClasses({ Song.class, Artist.class, Chart.class, Label.class, Studio.class, ChartEntry.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ReversingNestedSourcePropertiesConstructorTest { - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryReverse.class }) public void shouldGenerateNestedReverse() { @@ -63,7 +60,7 @@ public void shouldGenerateNestedReverse() { assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryWithIgnoresReverse.class }) public void shouldIgnoreEverytingBelowArtist() { @@ -86,7 +83,7 @@ public void shouldIgnoreEverytingBelowArtist() { assertThat( song2.getArtist() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryWithFactoryReverse.class }) public void shouldGenerateNestedReverseWithFactory() { @@ -116,7 +113,7 @@ public void shouldGenerateNestedReverseWithFactory() { } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryComposedReverse.class, ChartEntryComposed.class, ChartEntryLabel.class }) public void shouldGenerateNestedComposedReverse() { @@ -146,7 +143,7 @@ public void shouldGenerateNestedComposedReverse() { assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryWithMappingReverse.class, ChartEntryWithMapping.class }) public void shouldGenerateNestedWithMappingReverse() { @@ -175,7 +172,7 @@ public void shouldGenerateNestedWithMappingReverse() { assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryWithConfigReverse.class, ArtistToChartEntryConfig.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/NestedProductPropertiesConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/NestedProductPropertiesConstructorTest.java index 78d113a8c8..c5cbe8279c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/NestedProductPropertiesConstructorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/nestedtarget/NestedProductPropertiesConstructorTest.java @@ -5,9 +5,7 @@ */ package org.mapstruct.ap.test.constructor.nestedtarget; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry; import org.mapstruct.ap.test.constructor.nestedsource.source.Artist; import org.mapstruct.ap.test.constructor.nestedsource.source.Chart; @@ -15,8 +13,8 @@ import org.mapstruct.ap.test.constructor.nestedsource.source.Song; import org.mapstruct.ap.test.constructor.nestedsource.source.Studio; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -34,15 +32,14 @@ ChartEntryToArtist.class, }) @IssueKey("73") -@RunWith(AnnotationProcessorTestRunner.class) public class NestedProductPropertiesConstructorTest { - @Rule - public GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( ChartEntryToArtist.class ); - @Test + @ProcessorTest public void shouldMapNestedTarget() { ChartEntry chartEntry = new ChartEntry( @@ -72,7 +69,7 @@ public void shouldMapNestedTarget() { } - @Test + @ProcessorTest public void shouldReverseNestedTarget() { ChartEntry chartEntry = new ChartEntry( diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorTest.java index e24abb84b1..49bb7e6d8a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/simple/SimpleConstructorTest.java @@ -7,12 +7,10 @@ import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.constructor.Person; import org.mapstruct.ap.test.constructor.PersonDto; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -24,10 +22,9 @@ PersonDto.class, SimpleConstructorMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class SimpleConstructorTest { - @Test + @ProcessorTest public void mapDefault() { PersonDto source = new PersonDto(); source.setName( "Bob" ); @@ -47,7 +44,7 @@ public void mapDefault() { assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); } - @Test + @ProcessorTest public void mapWithConstants() { PersonDto source = new PersonDto(); source.setName( "Bob" ); @@ -67,7 +64,7 @@ public void mapWithConstants() { assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" ); } - @Test + @ProcessorTest public void mapWithExpressions() { PersonDto source = new PersonDto(); source.setName( "Bob" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorTest.java index 7a7f859041..0e35361f48 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/unmapped/UnmappedConstructorTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.constructor.unmapped; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -23,10 +21,9 @@ OrderDto.class, UnmappedConstructorMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class UnmappedConstructorTest { - @Test + @ProcessorTest @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { @Diagnostic(type = UnmappedConstructorMapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java index b40dcbf17c..b7a254296d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.constructor.visibility; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.constructor.Default; import org.mapstruct.ap.test.constructor.PersonDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -19,14 +17,13 @@ * @author Filip Hrisafov */ @IssueKey("2150") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ PersonDto.class, Default.class, }) public class ConstructorVisibilityTest { - @Test + @ProcessorTest @WithClasses({ SimpleWithPublicConstructorMapper.class }) @@ -43,7 +40,7 @@ public void shouldUseSinglePublicConstructorAlways() { assertThat( target.getAge() ).isEqualTo( 30 ); } - @Test + @ProcessorTest @WithClasses({ SimpleWithPublicParameterlessConstructorMapper.class }) @@ -60,7 +57,7 @@ public void shouldUsePublicParameterConstructorIfPresent() { assertThat( target.getAge() ).isEqualTo( -1 ); } - @Test + @ProcessorTest @WithClasses({ SimpleWithPublicParameterlessConstructorAndDefaultAnnotatedMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java index 2569973372..77fefe756e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java @@ -7,16 +7,14 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.Context; import org.mapstruct.ap.test.context.erroneous.ErroneousNodeMapperWithNonUniqueContextTypes; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Tests the erroneous usage of the {@link Context} annotation in the following situations: @@ -31,10 +29,9 @@ Node.class, NodeDto.class, CycleContext.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ContextParameterErroneousTest { - @Test + @ProcessorTest @WithClasses(ErroneousNodeMapperWithNonUniqueContextTypes.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = @Diagnostic( diff --git a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterTest.java b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterTest.java index 8fc6e436e0..017183743b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.context; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.BeforeMapping; import org.mapstruct.Context; import org.mapstruct.ObjectFactory; import org.mapstruct.ap.test.context.Node.Attribute; import org.mapstruct.ap.test.context.NodeDto.AttributeDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Tests the usage of the {@link Context} annotation in the following situations: @@ -44,12 +42,11 @@ CycleContextLifecycleMethods.class, FactoryContextMethods.class, SelfContainingCycleContext.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ContextParameterTest { private static final int MAGIC_NUMBER_OFFSET = 10; - @Test + @ProcessorTest public void mappingWithContextCorrectlyResolvesCycles() { Node root = buildNodes(); NodeDto rootDto = @@ -61,7 +58,7 @@ public void mappingWithContextCorrectlyResolvesCycles() { assertResult( updated ); } - @Test + @ProcessorTest public void automappingWithContextCorrectlyResolvesCycles() { Node root = buildNodes(); NodeDto rootDto = AutomappingNodeMapperWithContext.INSTANCE @@ -74,7 +71,7 @@ public void automappingWithContextCorrectlyResolvesCycles() { assertResult( updated ); } - @Test + @ProcessorTest public void automappingWithSelfContainingContextCorrectlyResolvesCycles() { Node root = buildNodes(); NodeDto rootDto = AutomappingNodeMapperWithSelfContainingContext.INSTANCE diff --git a/processor/src/test/java/org/mapstruct/ap/test/context/objectfactory/ContextWithObjectFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/context/objectfactory/ContextWithObjectFactoryTest.java index 08ca1ce1b9..068a5a0d95 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/context/objectfactory/ContextWithObjectFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/context/objectfactory/ContextWithObjectFactoryTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.context.objectfactory; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -23,10 +21,9 @@ ValveDto.class, ContextObjectFactory.class, ContextWithObjectFactoryMapper.class}) -@RunWith(AnnotationProcessorTestRunner.class) public class ContextWithObjectFactoryTest { - @Test + @ProcessorTest public void testFactoryCalled( ) { ValveDto dto = new ValveDto(); dto.setOneWay( true ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/ConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/ConversionTest.java index 400026254a..d59f069458 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/ConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/ConversionTest.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.conversion; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ConversionTest { - @Test + @ProcessorTest public void shouldApplyConversions() { Source source = new Source(); source.setFoo( 42 ); @@ -31,7 +28,7 @@ public void shouldApplyConversions() { assertThat( target.getZip() ).isEqualTo( "73" ); } - @Test + @ProcessorTest public void shouldHandleNulls() { Source source = new Source(); Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); @@ -42,7 +39,7 @@ public void shouldHandleNulls() { assertThat( target.getZip() ).isEqualTo( "0" ); } - @Test + @ProcessorTest public void shouldApplyConversionsToMappedProperties() { Source source = new Source(); source.setQax( 42 ); @@ -55,7 +52,7 @@ public void shouldApplyConversionsToMappedProperties() { assertThat( target.getQax() ).isEqualTo( 23 ); } - @Test + @ProcessorTest public void shouldApplyConversionsForReverseMapping() { Target target = new Target(); target.setFoo( 42L ); @@ -70,7 +67,7 @@ public void shouldApplyConversionsForReverseMapping() { assertThat( source.getZip() ).isEqualTo( 73 ); } - @Test + @ProcessorTest public void shouldApplyConversionsToMappedPropertiesForReverseMapping() { Target target = new Target(); target.setQax( 42 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigNumbersConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigNumbersConversionTest.java index 95adb3e050..69f87f3980 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigNumbersConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigNumbersConversionTest.java @@ -5,35 +5,28 @@ */ package org.mapstruct.ap.test.conversion.bignumbers; -import static org.assertj.core.api.Assertions.assertThat; - import java.math.BigDecimal; import java.math.BigInteger; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; + /** * Tests conversions between {@link BigInteger} and numbers as well as String. * * @author Gunnar Morling */ -@RunWith(AnnotationProcessorTestRunner.class) public class BigNumbersConversionTest { - private final GeneratedSource generatedSource = new GeneratedSource(); - - @Rule - public GeneratedSource getGeneratedSource() { - return generatedSource; - } + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @IssueKey("21") @WithClasses({ BigIntegerSource.class, BigIntegerTarget.class, BigIntegerMapper.class }) public void shouldApplyBigIntegerConversions() { @@ -70,7 +63,7 @@ public void shouldApplyBigIntegerConversions() { assertThat( target.getString() ).isEqualTo( "13" ); } - @Test + @ProcessorTest @IssueKey("21") @WithClasses({ BigIntegerSource.class, BigIntegerTarget.class, BigIntegerMapper.class }) public void shouldApplyReverseBigIntegerConversions() { @@ -107,7 +100,7 @@ public void shouldApplyReverseBigIntegerConversions() { assertThat( source.getString() ).isEqualTo( new BigInteger( "13" ) ); } - @Test + @ProcessorTest @IssueKey("21") @WithClasses({ BigDecimalSource.class, BigDecimalTarget.class, BigDecimalMapper.class }) public void shouldApplyBigDecimalConversions() { @@ -146,7 +139,7 @@ public void shouldApplyBigDecimalConversions() { assertThat( target.getBigInteger() ).isEqualTo( new BigInteger( "14" ) ); } - @Test + @ProcessorTest @IssueKey("21") @WithClasses({ BigDecimalSource.class, BigDecimalTarget.class, BigDecimalMapper.class }) public void shouldApplyReverseBigDecimalConversions() { @@ -185,10 +178,10 @@ public void shouldApplyReverseBigDecimalConversions() { assertThat( source.getBigInteger() ).isEqualTo( new BigDecimal( "14" ) ); } - @Test + @ProcessorTest @IssueKey("1009") @WithClasses({ BigIntegerSource.class, BigIntegerTarget.class, BigIntegerMapper.class }) public void shouldNotGenerateCreateDecimalFormatMethod() { - getGeneratedSource().forMapper( BigIntegerMapper.class ).content().doesNotContain( "createDecimalFormat" ); + generatedSource.forMapper( BigIntegerMapper.class ).content().doesNotContain( "createDecimalFormat" ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/currency/CurrencyConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/currency/CurrencyConversionTest.java index 092388e2e8..57b4f7e4e4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/currency/CurrencyConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/currency/CurrencyConversionTest.java @@ -9,12 +9,10 @@ import java.util.HashSet; import java.util.Set; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -23,10 +21,9 @@ */ @WithClasses({ CurrencyMapper.class, CurrencySource.class, CurrencyTarget.class }) @IssueKey("1355") -@RunWith(AnnotationProcessorTestRunner.class) public class CurrencyConversionTest { - @Test + @ProcessorTest public void shouldApplyCurrencyConversions() { final CurrencySource source = new CurrencySource(); source.setCurrencyA( Currency.getInstance( "USD" ) ); @@ -44,7 +41,7 @@ public void shouldApplyCurrencyConversions() { .containsExactlyInAnyOrder( "EUR", "CHF" ); } - @Test + @ProcessorTest public void shouldApplyReverseConversions() { final CurrencyTarget target = new CurrencyTarget(); target.setCurrencyA( "USD" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java index 2d98b73bc9..363b287b71 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.conversion.date; -import static org.assertj.core.api.Assertions.assertThat; - import java.sql.Time; import java.sql.Timestamp; import java.util.Arrays; @@ -16,16 +14,16 @@ import java.util.List; import java.util.Locale; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -import org.mapstruct.ap.testutil.runner.Compiler; -import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; -import org.mapstruct.ap.testutil.runner.EnabledOnCompiler; + +import static org.assertj.core.api.Assertions.assertThat; /** * Tests application of format strings for conversions between strings and dates. @@ -38,27 +36,23 @@ SourceTargetMapper.class }) @IssueKey("43") -@RunWith(AnnotationProcessorTestRunner.class) public class DateConversionTest { private Locale originalLocale; - @Before + @BeforeEach public void setDefaultLocale() { originalLocale = Locale.getDefault(); Locale.setDefault( Locale.GERMAN ); } - @After + @AfterEach public void tearDown() { Locale.setDefault( originalLocale ); } - @Test - @DisabledOnCompiler({ - Compiler.JDK11, - Compiler.ECLIPSE11 - }) + @ProcessorTest + @EnabledOnJre( JRE.JAVA_8 ) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversions() { Source source = new Source(); @@ -72,11 +66,8 @@ public void shouldApplyDateFormatForConversions() { assertThat( target.getAnotherDate() ).isEqualTo( "14.02.13 00:00" ); } - @Test - @EnabledOnCompiler({ - Compiler.JDK11, - Compiler.ECLIPSE11 - }) + @ProcessorTest + @EnabledForJreRange(min = JRE.JAVA_11) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionsJdk11() { Source source = new Source(); @@ -90,11 +81,8 @@ public void shouldApplyDateFormatForConversionsJdk11() { assertThat( target.getAnotherDate() ).isEqualTo( "14.02.13, 00:00" ); } - @Test - @DisabledOnCompiler({ - Compiler.JDK11, - Compiler.ECLIPSE11 - }) + @ProcessorTest + @EnabledOnJre(JRE.JAVA_8) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionInReverseMapping() { Target target = new Target(); @@ -110,11 +98,8 @@ public void shouldApplyDateFormatForConversionInReverseMapping() { ); } - @Test - @EnabledOnCompiler({ - Compiler.JDK11, - Compiler.ECLIPSE11 - }) + @ProcessorTest + @EnabledForJreRange(min = JRE.JAVA_11) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionInReverseMappingJdk11() { Target target = new Target(); @@ -130,7 +115,7 @@ public void shouldApplyDateFormatForConversionInReverseMappingJdk11() { ); } - @Test + @ProcessorTest public void shouldApplyStringConversionForIterableMethod() { List dates = Arrays.asList( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime(), @@ -144,7 +129,7 @@ public void shouldApplyStringConversionForIterableMethod() { assertThat( stringDates ).containsExactly( "06.07.2013", "14.02.2013", "11.04.2013" ); } - @Test + @ProcessorTest public void shouldApplyStringConversionForArrayMethod() { List dates = Arrays.asList( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime(), @@ -158,7 +143,7 @@ public void shouldApplyStringConversionForArrayMethod() { assertThat( stringDates ).isEqualTo( new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" } ); } - @Test + @ProcessorTest public void shouldApplyStringConversionForReverseIterableMethod() { List stringDates = Arrays.asList( "06.07.2013", "14.02.2013", "11.04.2013" ); @@ -172,7 +157,7 @@ public void shouldApplyStringConversionForReverseIterableMethod() { ); } - @Test + @ProcessorTest public void shouldApplyStringConversionForReverseArrayMethod() { String[] stringDates = new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" }; @@ -186,7 +171,7 @@ public void shouldApplyStringConversionForReverseArrayMethod() { ); } - @Test + @ProcessorTest public void shouldApplyStringConversionForReverseArrayArrayMethod() { Date[] dates = new Date[]{ new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime(), @@ -199,7 +184,7 @@ public void shouldApplyStringConversionForReverseArrayArrayMethod() { assertThat( stringDates ).isEqualTo( new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" } ); } - @Test + @ProcessorTest public void shouldApplyDateConversionForReverseArrayArrayMethod() { String[] stringDates = new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" }; @@ -214,7 +199,7 @@ public void shouldApplyDateConversionForReverseArrayArrayMethod() { } @IssueKey("858") - @Test + @ProcessorTest public void shouldApplyDateToSqlConversion() { GregorianCalendar time = new GregorianCalendar( 2016, Calendar.AUGUST, 24, 20, 30, 30 ); GregorianCalendar sqlDate = new GregorianCalendar( 2016, Calendar.AUGUST, 23, 21, 35, 35 ); @@ -236,7 +221,7 @@ public void shouldApplyDateToSqlConversion() { } @IssueKey("858") - @Test + @ProcessorTest public void shouldApplySqlToDateConversion() { Target target = new Target(); GregorianCalendar time = new GregorianCalendar( 2016, Calendar.AUGUST, 24, 20, 30, 30 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java index 8f630df22c..5eb67bee05 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.conversion.erroneous; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -22,7 +20,6 @@ Target.class }) @IssueKey("725") -@RunWith(AnnotationProcessorTestRunner.class) public class InvalidDateFormatTest { @WithClasses({ @@ -79,7 +76,7 @@ public class InvalidDateFormatTest { line = 43, message = "Given date format \"qwertz\" is invalid. Message: \"Illegal pattern character 'q'\".") }) - @Test + @ProcessorTest public void shouldFailWithInvalidDateFormats() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java index 25ed958867..bcd5045646 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java @@ -17,37 +17,34 @@ import java.util.Date; import java.util.TimeZone; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * Tests for conversions to/from Java 8 date and time types. */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) @IssueKey("121") public class Java8TimeConversionTest { private TimeZone originalTimeZone; - @Before + @BeforeEach public void setUp() { originalTimeZone = TimeZone.getDefault(); } - @After + @AfterEach public void tearDown() { TimeZone.setDefault( originalTimeZone ); } - @Test + @ProcessorTest public void testDateTimeToString() { Source src = new Source(); src.setZonedDateTime( ZonedDateTime.of( java.time.LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) ) ); @@ -56,7 +53,7 @@ public void testDateTimeToString() { assertThat( target.getZonedDateTime() ).isEqualTo( "01.01.2014 00:00 UTC" ); } - @Test + @ProcessorTest public void testLocalDateTimeToString() { Source src = new Source(); src.setLocalDateTime( LocalDateTime.of( 2014, 1, 1, 0, 0 ) ); @@ -65,7 +62,7 @@ public void testLocalDateTimeToString() { assertThat( target.getLocalDateTime() ).isEqualTo( "01.01.2014 00:00" ); } - @Test + @ProcessorTest public void testLocalDateToString() { Source src = new Source(); src.setLocalDate( LocalDate.of( 2014, 1, 1 ) ); @@ -74,7 +71,7 @@ public void testLocalDateToString() { assertThat( target.getLocalDate() ).isEqualTo( "01.01.2014" ); } - @Test + @ProcessorTest public void testLocalTimeToString() { Source src = new Source(); src.setLocalTime( LocalTime.of( 0, 0 ) ); @@ -83,7 +80,7 @@ public void testLocalTimeToString() { assertThat( target.getLocalTime() ).isEqualTo( "00:00" ); } - @Test + @ProcessorTest public void testSourceToTargetMappingForStrings() { Source src = new Source(); src.setLocalTime( LocalTime.of( 0, 0 ) ); @@ -109,7 +106,7 @@ public void testSourceToTargetMappingForStrings() { assertThat( target.getLocalTime() ).isEqualTo( "00:00" ); } - @Test + @ProcessorTest public void testStringToDateTime() { String dateTimeAsString = "01.01.2014 00:00 UTC"; Target target = new Target(); @@ -122,7 +119,7 @@ public void testStringToDateTime() { assertThat( src.getZonedDateTime() ).isEqualTo( sourceDateTime ); } - @Test + @ProcessorTest public void testStringToLocalDateTime() { String dateTimeAsString = "01.01.2014 00:00"; Target target = new Target(); @@ -135,7 +132,7 @@ public void testStringToLocalDateTime() { assertThat( src.getLocalDateTime() ).isEqualTo( sourceDateTime ); } - @Test + @ProcessorTest public void testStringToLocalDate() { String dateTimeAsString = "01.01.2014"; Target target = new Target(); @@ -148,7 +145,7 @@ public void testStringToLocalDate() { assertThat( src.getLocalDate() ).isEqualTo( sourceDate ); } - @Test + @ProcessorTest public void testStringToLocalTime() { String dateTimeAsString = "00:00"; Target target = new Target(); @@ -161,7 +158,7 @@ public void testStringToLocalTime() { assertThat( src.getLocalTime() ).isEqualTo( sourceTime ); } - @Test + @ProcessorTest public void testTargetToSourceNullMapping() { Target target = new Target(); Source src = SourceTargetMapper.INSTANCE.targetToSource( target ); @@ -173,7 +170,7 @@ public void testTargetToSourceNullMapping() { assertThat( src.getLocalTime() ).isNull(); } - @Test + @ProcessorTest public void testTargetToSourceMappingForStrings() { Target target = new Target(); @@ -198,7 +195,7 @@ public void testTargetToSourceMappingForStrings() { assertThat( src.getLocalTime() ).isEqualTo( LocalTime.of( 0, 0 ) ); } - @Test + @ProcessorTest public void testCalendarMapping() { Source source = new Source(); ZonedDateTime dateTime = ZonedDateTime.of( LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) ); @@ -223,7 +220,7 @@ public void testCalendarMapping() { assertThat( source.getForCalendarConversion() ).isEqualTo( dateTime ); } - @Test + @ProcessorTest public void testZonedDateTimeToDateMapping() { TimeZone.setDefault( TimeZone.getTimeZone( "UTC" ) ); Source source = new Source(); @@ -248,7 +245,7 @@ public void testZonedDateTimeToDateMapping() { assertThat( source.getForDateConversionWithZonedDateTime() ).isEqualTo( dateTime ); } - @Test + @ProcessorTest public void testInstantToDateMapping() { Instant instant = Instant.ofEpochMilli( 1539366615000L ); @@ -263,7 +260,7 @@ public void testInstantToDateMapping() { assertThat( source.getForDateConversionWithInstant() ).isEqualTo( instant ); } - @Test + @ProcessorTest public void testLocalDateTimeToDateMapping() { TimeZone.setDefault( TimeZone.getTimeZone( "Australia/Melbourne" ) ); @@ -289,7 +286,7 @@ public void testLocalDateTimeToDateMapping() { assertThat( source.getForDateConversionWithLocalDateTime() ).isEqualTo( dateTime ); } - @Test + @ProcessorTest public void testLocalDateToDateMapping() { TimeZone.setDefault( TimeZone.getTimeZone( "Australia/Melbourne" ) ); @@ -313,7 +310,7 @@ public void testLocalDateToDateMapping() { assertThat( source.getForDateConversionWithLocalDate() ).isEqualTo( localDate ); } - @Test + @ProcessorTest public void testLocalDateToSqlDateMapping() { TimeZone.setDefault( TimeZone.getTimeZone( "Australia/Melbourne" ) ); @@ -337,7 +334,7 @@ public void testLocalDateToSqlDateMapping() { assertThat( source.getForSqlDateConversionWithLocalDate() ).isEqualTo( localDate ); } - @Test + @ProcessorTest public void testInstantToStringMapping() { Source source = new Source(); source.setForInstantConversionWithString( Instant.ofEpochSecond( 42L ) ); @@ -347,7 +344,7 @@ public void testInstantToStringMapping() { assertThat( periodString ).isEqualTo( "1970-01-01T00:00:42Z" ); } - @Test + @ProcessorTest public void testInstantToStringNullMapping() { Source source = new Source(); source.setForInstantConversionWithString( null ); @@ -357,7 +354,7 @@ public void testInstantToStringNullMapping() { assertThat( periodString ).isNull(); } - @Test + @ProcessorTest public void testStringToInstantMapping() { Target target = new Target(); target.setForInstantConversionWithString( "1970-01-01T00:00:00.000Z" ); @@ -367,7 +364,7 @@ public void testStringToInstantMapping() { assertThat( instant ).isEqualTo( Instant.EPOCH ); } - @Test + @ProcessorTest public void testStringToInstantNullMapping() { Target target = new Target(); target.setForInstantConversionWithString( null ); @@ -377,7 +374,7 @@ public void testStringToInstantNullMapping() { assertThat( instant ).isNull(); } - @Test + @ProcessorTest public void testPeriodToStringMapping() { Source source = new Source(); source.setForPeriodConversionWithString( Period.ofDays( 42 ) ); @@ -387,7 +384,7 @@ public void testPeriodToStringMapping() { assertThat( periodString ).isEqualTo( "P42D" ); } - @Test + @ProcessorTest public void testPeriodToStringNullMapping() { Source source = new Source(); source.setForPeriodConversionWithString( null ); @@ -397,7 +394,7 @@ public void testPeriodToStringNullMapping() { assertThat( periodString ).isNull(); } - @Test + @ProcessorTest public void testStringToPeriodMapping() { Target target = new Target(); target.setForPeriodConversionWithString( "P1Y2M3D" ); @@ -407,7 +404,7 @@ public void testStringToPeriodMapping() { assertThat( period ).isEqualTo( Period.of( 1, 2, 3 ) ); } - @Test + @ProcessorTest public void testStringToPeriodNullMapping() { Target target = new Target(); target.setForPeriodConversionWithString( null ); @@ -417,7 +414,7 @@ public void testStringToPeriodNullMapping() { assertThat( period ).isNull(); } - @Test + @ProcessorTest public void testDurationToStringMapping() { Source source = new Source(); source.setForDurationConversionWithString( Duration.ofMinutes( 42L ) ); @@ -427,7 +424,7 @@ public void testDurationToStringMapping() { assertThat( durationString ).isEqualTo( "PT42M" ); } - @Test + @ProcessorTest public void testDurationToStringNullMapping() { Source source = new Source(); source.setForDurationConversionWithString( null ); @@ -437,7 +434,7 @@ public void testDurationToStringNullMapping() { assertThat( durationString ).isNull(); } - @Test + @ProcessorTest public void testStringToDurationMapping() { Target target = new Target(); target.setForDurationConversionWithString( "PT20.345S" ); @@ -447,7 +444,7 @@ public void testStringToDurationMapping() { assertThat( duration ).isEqualTo( Duration.ofSeconds( 20L, 345000000L ) ); } - @Test + @ProcessorTest public void testStringToDurationNullMapping() { Target target = new Target(); target.setForDurationConversionWithString( null ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/LocalDateTimeToXMLGregorianCalendarConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/LocalDateTimeToXMLGregorianCalendarConversionTest.java index ebaa271d04..5f656f2f76 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/LocalDateTimeToXMLGregorianCalendarConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/LocalDateTimeToXMLGregorianCalendarConversionTest.java @@ -7,19 +7,16 @@ import java.time.LocalDateTime; import java.util.Calendar; - import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mapstruct.ap.test.conversion.java8time.localdatetimetoxmlgregoriancalendarconversion.SourceTargetMapper; -import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.test.conversion.java8time.localdatetimetoxmlgregoriancalendarconversion.Source; +import org.mapstruct.ap.test.conversion.java8time.localdatetimetoxmlgregoriancalendarconversion.SourceTargetMapper; import org.mapstruct.ap.test.conversion.java8time.localdatetimetoxmlgregoriancalendarconversion.Target; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; import static org.assertj.core.api.Assertions.assertThat; @@ -27,10 +24,9 @@ * @author Andrei Arlou */ @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class LocalDateTimeToXMLGregorianCalendarConversionTest { - @Test + @ProcessorTest public void shouldNullCheckOnConversionToTarget() { Target target = SourceTargetMapper.INSTANCE.toTarget( new Source() ); @@ -38,7 +34,7 @@ public void shouldNullCheckOnConversionToTarget() { assertThat( target.getLocalDateTime() ).isNull(); } - @Test + @ProcessorTest public void shouldNullCheckOnConversionToSource() { Source source = SourceTargetMapper.INSTANCE.toSource( new Target() ); @@ -46,7 +42,7 @@ public void shouldNullCheckOnConversionToSource() { assertThat( source.getXmlGregorianCalendar() ).isNull(); } - @Test + @ProcessorTest public void shouldMapLocalDateTimeToXmlGregorianCalendarCorrectlyWithNanoseconds() throws DatatypeConfigurationException { LocalDateTime localDateTime = LocalDateTime.of( 1994, Calendar.MARCH, 5, 11, 30, 50, 9000000 ); @@ -64,7 +60,7 @@ public void shouldMapLocalDateTimeToXmlGregorianCalendarCorrectlyWithNanoseconds assertThat( source.getXmlGregorianCalendar() ).isEqualTo( expectedCalendar ); } - @Test + @ProcessorTest public void shouldMapLocalDateTimeToXmlGregorianCalendarCorrectlyWithSeconds() throws DatatypeConfigurationException { LocalDateTime localDateTime = LocalDateTime.of( 1994, Calendar.MARCH, 5, 11, 30, 50 ); @@ -82,7 +78,7 @@ public void shouldMapLocalDateTimeToXmlGregorianCalendarCorrectlyWithSeconds() assertThat( source.getXmlGregorianCalendar() ).isEqualTo( expectedCalendar ); } - @Test + @ProcessorTest public void shouldMapLocalDateTimeToXmlGregorianCalendarCorrectlyWithMinutes() throws DatatypeConfigurationException { LocalDateTime localDateTime = LocalDateTime.of( 1994, Calendar.MARCH, 5, 11, 30 ); @@ -100,7 +96,7 @@ public void shouldMapLocalDateTimeToXmlGregorianCalendarCorrectlyWithMinutes() assertThat( source.getXmlGregorianCalendar() ).isEqualTo( expectedCalendar ); } - @Test + @ProcessorTest public void shouldMapXmlGregorianCalendarToLocalDateTimeCorrectly() throws DatatypeConfigurationException { XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance() .newXMLGregorianCalendar( 1994, Calendar.MARCH, 5, 11, 30, 50, 500, diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/LocalDateToXMLGregorianCalendarConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/LocalDateToXMLGregorianCalendarConversionTest.java index 2e5c7b63e7..935e12d55d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/LocalDateToXMLGregorianCalendarConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/LocalDateToXMLGregorianCalendarConversionTest.java @@ -5,32 +5,28 @@ */ package org.mapstruct.ap.test.conversion.java8time; -import static org.assertj.core.api.Assertions.assertThat; - import java.time.LocalDate; - import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.conversion.java8time.localdatetoxmlgregoriancalendarconversion.Source; import org.mapstruct.ap.test.conversion.java8time.localdatetoxmlgregoriancalendarconversion.SourceTargetMapper; import org.mapstruct.ap.test.conversion.java8time.localdatetoxmlgregoriancalendarconversion.Target; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Andreas Gudian */ @IssueKey("580") @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class LocalDateToXMLGregorianCalendarConversionTest { - @Test + @ProcessorTest public void shouldNullCheckOnBuiltinAndConversion() { Target target = SourceTargetMapper.INSTANCE.toTarget( new Source() ); @@ -43,7 +39,7 @@ public void shouldNullCheckOnBuiltinAndConversion() { assertThat( source.getDate() ).isNull(); } - @Test + @ProcessorTest public void shouldMapCorrectlyOnBuiltinAndConversion() throws Exception { XMLGregorianCalendar calendarDate = DatatypeFactory.newInstance().newXMLGregorianCalendarDate( 2007, diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java index 9f61c222db..e861497169 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.conversion.jodatime; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; @@ -16,41 +14,40 @@ import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -import org.mapstruct.ap.testutil.runner.Compiler; -import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; -import org.mapstruct.ap.testutil.runner.EnabledOnCompiler; + +import static org.assertj.core.api.Assertions.assertThat; /** * Tests the conversion between Joda-Time types and String/Date/Calendar. * * @author Timo Eckhardt */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) @IssueKey("75") public class JodaConversionTest { private Locale originalLocale; - @Before + @BeforeEach public void setDefaultLocale() { originalLocale = Locale.getDefault(); Locale.setDefault( Locale.GERMAN ); } - @After + @AfterEach public void tearDown() { Locale.setDefault( originalLocale ); } - @Test + @ProcessorTest public void testDateTimeToString() { Source src = new Source(); src.setDateTime( new DateTime( 2014, 1, 1, 0, 0, 0, DateTimeZone.UTC ) ); @@ -59,7 +56,7 @@ public void testDateTimeToString() { assertThat( target.getDateTime() ).isEqualTo( "01.01.2014 00:00 UTC" ); } - @Test + @ProcessorTest public void testLocalDateTimeToString() { Source src = new Source(); src.setLocalDateTime( new LocalDateTime( 2014, 1, 1, 0, 0 ) ); @@ -68,7 +65,7 @@ public void testLocalDateTimeToString() { assertThat( target.getLocalDateTime() ).isEqualTo( "01.01.2014 00:00" ); } - @Test + @ProcessorTest public void testLocalDateToString() { Source src = new Source(); src.setLocalDate( new LocalDate( 2014, 1, 1 ) ); @@ -77,7 +74,7 @@ public void testLocalDateToString() { assertThat( target.getLocalDate() ).isEqualTo( "01.01.2014" ); } - @Test + @ProcessorTest public void testLocalTimeToString() { Source src = new Source(); src.setLocalTime( new LocalTime( 0, 0 ) ); @@ -86,11 +83,8 @@ public void testLocalTimeToString() { assertThat( target.getLocalTime() ).isEqualTo( "00:00" ); } - @Test - @DisabledOnCompiler({ - Compiler.JDK11, - Compiler.ECLIPSE11 - }) + @ProcessorTest + @EnabledOnJre(JRE.JAVA_8) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void testSourceToTargetMappingForStrings() { Source src = new Source(); @@ -117,11 +111,8 @@ public void testSourceToTargetMappingForStrings() { assertThat( target.getLocalTime() ).isEqualTo( "00:00:00" ); } - @Test - @EnabledOnCompiler({ - Compiler.JDK11, - Compiler.ECLIPSE11 - }) + @ProcessorTest + @EnabledForJreRange(min = JRE.JAVA_11) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void testSourceToTargetMappingForStringsJdk11() { Source src = new Source(); @@ -148,7 +139,7 @@ public void testSourceToTargetMappingForStringsJdk11() { assertThat( target.getLocalTime() ).isEqualTo( "00:00:00" ); } - @Test + @ProcessorTest public void testStringToDateTime() { String dateTimeAsString = "01.01.2014 00:00 UTC"; Target target = new Target(); @@ -161,7 +152,7 @@ public void testStringToDateTime() { assertThat( src.getDateTime() ).isEqualTo( sourceDateTime ); } - @Test + @ProcessorTest public void testStringToLocalDateTime() { String dateTimeAsString = "01.01.2014 00:00"; Target target = new Target(); @@ -174,7 +165,7 @@ public void testStringToLocalDateTime() { assertThat( src.getLocalDateTime() ).isEqualTo( sourceDateTime ); } - @Test + @ProcessorTest public void testStringToLocalDate() { String dateTimeAsString = "01.01.2014"; Target target = new Target(); @@ -187,7 +178,7 @@ public void testStringToLocalDate() { assertThat( src.getLocalDate() ).isEqualTo( sourceDate ); } - @Test + @ProcessorTest public void testStringToLocalTime() { String dateTimeAsString = "00:00"; Target target = new Target(); @@ -200,7 +191,7 @@ public void testStringToLocalTime() { assertThat( src.getLocalTime() ).isEqualTo( sourceTime ); } - @Test + @ProcessorTest public void testTargetToSourceNullMapping() { Target target = new Target(); Source src = SourceTargetMapper.INSTANCE.targetToSource( target ); @@ -212,7 +203,7 @@ public void testTargetToSourceNullMapping() { assertThat( src.getLocalTime() ).isNull(); } - @Test + @ProcessorTest public void testTargetToSourceMappingForStrings() { Target target = new Target(); @@ -229,7 +220,7 @@ public void testTargetToSourceMappingForStrings() { assertThat( src.getLocalTime() ).isEqualTo( new LocalTime( 0, 0 ) ); } - @Test + @ProcessorTest public void testCalendar() { Calendar calendar = Calendar.getInstance( TimeZone.getTimeZone( "CET" ) ); DateTime dateTimeWithCalendar = new DateTime( calendar ); @@ -245,7 +236,7 @@ public void testCalendar() { assertThat( mappedSource.getDateTimeForCalendarConversion() ).isEqualTo( dateTimeWithCalendar ); } - @Test + @ProcessorTest @WithClasses({ StringToLocalDateMapper.class, SourceWithStringDate.class, TargetWithLocalDate.class }) @IssueKey("456") public void testStringToLocalDateUsingDefaultFormat() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java index 1e5d275f43..d27f04374a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.conversion.lossy; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.withinPercentage; @@ -22,7 +20,6 @@ * * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ OversizedKitchenDrawerDto.class, RegularKitchenDrawerEntity.class, @@ -35,7 +32,7 @@ @IssueKey("5") public class LossyConversionTest { - @Test + @ProcessorTest public void testNoErrorCase() { CutleryInventoryDto dto = new CutleryInventoryDto(); @@ -51,7 +48,7 @@ public void testNoErrorCase() { assertThat( entity.getApproximateKnifeLength() ).isCloseTo( 3.7d, withinPercentage( 0.0001d ) ); } - @Test + @ProcessorTest @WithClasses(ErroneousKitchenDrawerMapper1.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -64,7 +61,7 @@ public void testNoErrorCase() { public void testConversionFromLongToInt() { } - @Test + @ProcessorTest @WithClasses(KitchenDrawerMapper2.class) @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { @@ -77,7 +74,7 @@ public void testConversionFromLongToInt() { public void testConversionFromBigIntegerToInteger() { } - @Test + @ProcessorTest @WithClasses(ErroneousKitchenDrawerMapper3.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -90,7 +87,7 @@ public void testConversionFromBigIntegerToInteger() { public void test2StepConversionFromBigIntegerToLong() { } - @Test + @ProcessorTest @WithClasses(ErroneousKitchenDrawerMapper4.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -103,7 +100,7 @@ public void test2StepConversionFromBigIntegerToLong() { public void testConversionFromDoubleToFloat() { } - @Test + @ProcessorTest @WithClasses(ErroneousKitchenDrawerMapper5.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -116,7 +113,7 @@ public void testConversionFromDoubleToFloat() { public void testConversionFromBigDecimalToFloat() { } - @Test + @ProcessorTest @WithClasses(KitchenDrawerMapper6.class) @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { @@ -128,7 +125,7 @@ public void testConversionFromBigDecimalToFloat() { public void test2StepConversionFromDoubleToFloat() { } - @Test + @ProcessorTest @WithClasses(ListMapper.class) @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { @@ -140,7 +137,7 @@ public void test2StepConversionFromDoubleToFloat() { public void testListElementConversion() { } - @Test + @ProcessorTest @WithClasses(MapMapper.class) @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/BooleanConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/BooleanConversionTest.java index 0f90586007..7c4035417b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/BooleanConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/BooleanConversionTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.conversion.nativetypes; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ BooleanSource.class, BooleanTarget.class, BooleanMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class BooleanConversionTest { - @Test + @ProcessorTest public void shouldApplyBooleanConversion() { BooleanSource source = new BooleanSource(); source.setB( true ); @@ -34,7 +31,7 @@ public void shouldApplyBooleanConversion() { assertThat( target.getBool() ).isEqualTo( Boolean.TRUE ); } - @Test + @ProcessorTest public void shouldApplyReverseBooleanConversion() { BooleanTarget target = new BooleanTarget(); target.setB( Boolean.TRUE ); @@ -47,7 +44,7 @@ public void shouldApplyReverseBooleanConversion() { assertThat( source.getBool() ).isEqualTo( true ); } - @Test + @ProcessorTest @IssueKey( "229" ) public void wrapperToPrimitiveIsNullSafe() { BooleanTarget target = new BooleanTarget(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/CharConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/CharConversionTest.java index 29b8b76484..def3b45ac5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/CharConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/CharConversionTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.conversion.nativetypes; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ CharSource.class, CharTarget.class, CharMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class CharConversionTest { - @Test + @ProcessorTest public void shouldApplyCharConversion() { CharSource source = new CharSource(); source.setC( 'G' ); @@ -32,7 +29,7 @@ public void shouldApplyCharConversion() { assertThat( target.getC() ).isEqualTo( Character.valueOf( 'G' ) ); } - @Test + @ProcessorTest public void shouldApplyReverseCharConversion() { CharTarget target = new CharTarget(); target.setC( 'G' ); @@ -43,7 +40,7 @@ public void shouldApplyReverseCharConversion() { assertThat( source.getC() ).isEqualTo( 'G' ); } - @Test + @ProcessorTest @IssueKey( "229" ) public void wrapperToPrimitiveIsNullSafe() { CharTarget target = new CharTarget(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/NumberConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/NumberConversionTest.java index bba6b9fc27..61be21e797 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/NumberConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/NumberConversionTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.conversion.nativetypes; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ ByteSource.class, @@ -40,10 +38,9 @@ DoubleWrapperTarget.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class NumberConversionTest { - @Test + @ProcessorTest public void shouldApplyByteConversions() { ByteSource source = new ByteSource(); source.setB( (byte) 1 ); @@ -76,7 +73,7 @@ public void shouldApplyByteConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyByteWrapperConversions() { ByteWrapperSource source = new ByteWrapperSource(); source.setB( (byte) 1 ); @@ -109,7 +106,7 @@ public void shouldApplyByteWrapperConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyShortConversions() { ShortSource source = new ShortSource(); source.setB( (short) 1 ); @@ -142,7 +139,7 @@ public void shouldApplyShortConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyShortWrapperConversions() { ShortWrapperSource source = new ShortWrapperSource(); source.setB( (short) 1 ); @@ -175,7 +172,7 @@ public void shouldApplyShortWrapperConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyIntConversions() { IntSource source = new IntSource(); source.setB( 1 ); @@ -208,7 +205,7 @@ public void shouldApplyIntConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyIntWrapperConversions() { IntWrapperSource source = new IntWrapperSource(); source.setB( 1 ); @@ -241,7 +238,7 @@ public void shouldApplyIntWrapperConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyLongConversions() { LongSource source = new LongSource(); source.setB( 1 ); @@ -274,7 +271,7 @@ public void shouldApplyLongConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyLongWrapperConversions() { LongWrapperSource source = new LongWrapperSource(); source.setB( (long) 1 ); @@ -307,7 +304,7 @@ public void shouldApplyLongWrapperConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyFloatConversions() { FloatSource source = new FloatSource(); source.setB( 1 ); @@ -340,7 +337,7 @@ public void shouldApplyFloatConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyFloatWrapperConversions() { FloatWrapperSource source = new FloatWrapperSource(); source.setB( 1f ); @@ -373,7 +370,7 @@ public void shouldApplyFloatWrapperConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyDoubleConversions() { DoubleSource source = new DoubleSource(); source.setB( 1 ); @@ -406,7 +403,7 @@ public void shouldApplyDoubleConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest public void shouldApplyDoubleWrapperConversions() { DoubleWrapperSource source = new DoubleWrapperSource(); source.setB( 1d ); @@ -439,7 +436,7 @@ public void shouldApplyDoubleWrapperConversions() { assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); } - @Test + @ProcessorTest @IssueKey( "229" ) public void wrapperToPrimitiveIsNullSafe() { assertThat( SourceTargetMapper.INSTANCE.sourceToTarget( new ByteWrapperSource() ) ).isNotNull(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java index 9b92182f6f..ea1dd0da60 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java @@ -5,13 +5,6 @@ */ package org.mapstruct.ap.test.conversion.numbers; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; @@ -20,6 +13,11 @@ import java.util.Locale; import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; @@ -28,23 +26,22 @@ Target.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class NumberFormatConversionTest { private Locale originalLocale; - @Before + @BeforeEach public void setDefaultLocale() { originalLocale = Locale.getDefault(); Locale.setDefault( Locale.ENGLISH ); } - @After + @AfterEach public void tearDown() { Locale.setDefault( originalLocale ); } - @Test + @ProcessorTest public void shouldApplyStringConversions() { Source source = new Source(); source.setI( 1 ); @@ -85,7 +82,7 @@ public void shouldApplyStringConversions() { assertThat( target.getBigInteger1() ).isEqualTo( "1.23456789E12" ); } - @Test + @ProcessorTest public void shouldApplyReverseStringConversions() { Target target = new Target(); target.setI( "1.00" ); @@ -126,7 +123,7 @@ public void shouldApplyReverseStringConversions() { assertThat( source.getBigInteger1() ).isEqualTo( new BigInteger( "1234567890000" ) ); } - @Test + @ProcessorTest public void shouldApplyStringConversionsToIterables() { List target = SourceTargetMapper.INSTANCE.sourceToTarget( Arrays.asList( 2f ) ); @@ -139,7 +136,7 @@ public void shouldApplyStringConversionsToIterables() { assertThat( source ).isEqualTo( Arrays.asList( 2.00f ) ); } - @Test + @ProcessorTest public void shouldApplyStringConversionsToMaps() { Map source1 = new HashMap<>(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/precedence/ConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/precedence/ConversionTest.java index 5a3a968c1e..42c72e0727 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/precedence/ConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/precedence/ConversionTest.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.conversion.precedence; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ Source.class, Target.class, SourceTargetMapper.class, IntegerStringMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ConversionTest { - @Test + @ProcessorTest public void shouldInvokeMappingMethodInsteadOfConversion() { Source source = new Source(); source.setFoo( 42 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java index bc98fff256..994b47957b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java @@ -5,12 +5,10 @@ */ package org.mapstruct.ap.test.conversion.string; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -20,16 +18,15 @@ Target.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class StringConversionTest { private static final String STRING_CONSTANT = "String constant"; - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( SourceTargetMapper.class ); - @Test + @ProcessorTest public void shouldApplyStringConversions() { Source source = new Source(); source.setB( (byte) 1 ); @@ -72,7 +69,7 @@ public void shouldApplyStringConversions() { assertThat( target.getSb() ).isEqualTo( "SB" ); } - @Test + @ProcessorTest public void shouldApplyReverseStringConversions() { Target target = new Target(); target.setB( "1" ); @@ -115,7 +112,7 @@ public void shouldApplyReverseStringConversions() { assertThat( source.getSb().toString() ).isEqualTo( "SB" ); } - @Test + @ProcessorTest @IssueKey( "328" ) public void stringShouldBeMappedToObjectByReference() { Target target = new Target(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java index 59d59eb987..4e70581983 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.decorator; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Calendar; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for the application of decorators. @@ -32,10 +29,9 @@ AddressDto.class }) @IssueKey("163") -@RunWith(AnnotationProcessorTestRunner.class) public class DecoratorTest { - @Test + @ProcessorTest @WithClasses({ PersonMapper.class, PersonMapperDecorator.class @@ -56,7 +52,7 @@ public void shouldInvokeDecoratorMethods() { assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); } - @Test + @ProcessorTest @WithClasses({ PersonMapper.class, PersonMapperDecorator.class @@ -73,7 +69,7 @@ public void shouldDelegateNonDecoratedMethodsToDefaultImplementation() { assertThat( addressDto.getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); } - @Test + @ProcessorTest @WithClasses({ PersonMapper.class, PersonMapperDecorator.class @@ -91,7 +87,7 @@ public void shouldDelegateNonDecoratedVoidMethodsToDefaultImplementation() { assertThat( address.getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); } - @Test + @ProcessorTest @WithClasses({ AnotherPersonMapper.class, AnotherPersonMapperDecorator.class @@ -112,7 +108,7 @@ public void shouldApplyDecoratorWithDefaultConstructor() { assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); } - @Test + @ProcessorTest @WithClasses({ YetAnotherPersonMapper.class, YetAnotherPersonMapperDecorator.class @@ -135,7 +131,7 @@ public void shouldApplyDelegateToClassBasedMapper() { } @IssueKey("173") - @Test + @ProcessorTest @WithClasses({ Person2Mapper.class, Person2MapperDecorator.class, @@ -171,7 +167,7 @@ public void shouldApplyCustomMappers() { assertThat( personDto.getSportsClub().getName() ).isEqualTo( "SC Duckburg" ); } - @Test + @ProcessorTest @WithClasses({ ErroneousPersonMapper.class, ErroneousPersonMapperDecorator.class diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java index ba1f6012bc..d00368dfd4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java @@ -5,32 +5,29 @@ */ package org.mapstruct.ap.test.decorator.jsr330; -import static java.lang.System.lineSeparator; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Calendar; - import javax.inject.Inject; import javax.inject.Named; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.decorator.Address; import org.mapstruct.ap.test.decorator.AddressDto; import org.mapstruct.ap.test.decorator.Person; import org.mapstruct.ap.test.decorator.PersonDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import static java.lang.System.lineSeparator; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test for the application of decorators using component model jsr330. * @@ -45,37 +42,32 @@ PersonMapperDecorator.class }) @IssueKey("592") -@RunWith(AnnotationProcessorTestRunner.class) @ComponentScan(basePackageClasses = Jsr330DecoratorTest.class) @Configuration public class Jsr330DecoratorTest { - private final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); @Inject @Named private PersonMapper personMapper; private ConfigurableApplicationContext context; - @Rule - public GeneratedSource getGeneratedSource() { - return generatedSource; - } - - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldInvokeDecoratorMethods() { Calendar birthday = Calendar.getInstance(); birthday.set( 1928, Calendar.MAY, 23 ); @@ -89,7 +81,7 @@ public void shouldInvokeDecoratorMethods() { assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); } - @Test + @ProcessorTest public void shouldDelegateNonDecoratedMethodsToDefaultImplementation() { Address address = new Address( "42 Ocean View Drive" ); @@ -100,7 +92,7 @@ public void shouldDelegateNonDecoratedMethodsToDefaultImplementation() { } @IssueKey("664") - @Test + @ProcessorTest public void hasSingletonAnnotation() { // check the decorator generatedSource.forMapper( PersonMapper.class ).content() diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/SpringDecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/SpringDecoratorTest.java index feffc8c52c..dc834683e3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/SpringDecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/SpringDecoratorTest.java @@ -5,27 +5,25 @@ */ package org.mapstruct.ap.test.decorator.spring.constructor; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Calendar; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.mapstruct.ap.test.decorator.Address; import org.mapstruct.ap.test.decorator.AddressDto; import org.mapstruct.ap.test.decorator.Person; import org.mapstruct.ap.test.decorator.PersonDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test for the application of decorators using component model spring. * @@ -40,7 +38,6 @@ PersonMapperDecorator.class }) @IssueKey("592") -@RunWith(AnnotationProcessorTestRunner.class) @ComponentScan(basePackageClasses = SpringDecoratorTest.class) @Configuration public class SpringDecoratorTest { @@ -49,20 +46,20 @@ public class SpringDecoratorTest { private PersonMapper personMapper; private ConfigurableApplicationContext context; - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldInvokeDecoratorMethods() { //given Calendar birthday = Calendar.getInstance(); @@ -79,7 +76,7 @@ public void shouldInvokeDecoratorMethods() { assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); } - @Test + @ProcessorTest public void shouldDelegateNonDecoratedMethodsToDefaultImplementation() { //given Address address = new Address( "42 Ocean View Drive" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/SpringDecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/SpringDecoratorTest.java index d882dfc6db..1b81ed8c48 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/SpringDecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/SpringDecoratorTest.java @@ -5,27 +5,25 @@ */ package org.mapstruct.ap.test.decorator.spring.field; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Calendar; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.mapstruct.ap.test.decorator.Address; import org.mapstruct.ap.test.decorator.AddressDto; import org.mapstruct.ap.test.decorator.Person; import org.mapstruct.ap.test.decorator.PersonDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test for the application of decorators using component model spring. * @@ -40,7 +38,6 @@ PersonMapperDecorator.class }) @IssueKey("592") -@RunWith(AnnotationProcessorTestRunner.class) @ComponentScan(basePackageClasses = SpringDecoratorTest.class) @Configuration public class SpringDecoratorTest { @@ -49,20 +46,20 @@ public class SpringDecoratorTest { private PersonMapper personMapper; private ConfigurableApplicationContext context; - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldInvokeDecoratorMethods() { //given Calendar birthday = Calendar.getInstance(); @@ -79,7 +76,7 @@ public void shouldInvokeDecoratorMethods() { assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); } - @Test + @ProcessorTest public void shouldDelegateNonDecoratedMethodsToDefaultImplementation() { //given Address address = new Address( "42 Ocean View Drive" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/DefaultComponentModelMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/DefaultComponentModelMapperTest.java index b6b19ea88d..2c860f05f7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/DefaultComponentModelMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultcomponentmodel/DefaultComponentModelMapperTest.java @@ -5,29 +5,26 @@ */ package org.mapstruct.ap.test.defaultcomponentmodel; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; /** * @author Filip Hrisafov */ @IssueKey("2277") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Source.class, Target.class, }) public class DefaultComponentModelMapperTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses({ InstanceIterableMapper.class, InstanceMapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java index e834df3e3f..114d762c03 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/defaultvalue/DefaultValueTest.java @@ -5,27 +5,24 @@ */ package org.mapstruct.ap.test.defaultvalue; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.defaultvalue.other.Continent; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @IssueKey("600") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ CountryEntity.class, CountryDts.class, Continent.class }) public class DefaultValueTest { - @Test + @ProcessorTest @WithClasses({ Region.class, CountryMapper.class @@ -55,7 +52,7 @@ public void shouldDefaultValueAndUseConstantExpression() { assertThat( countryDts.getContinent() ).isEqualTo( Continent.EUROPE ); } - @Test + @ProcessorTest @WithClasses({ Region.class, CountryMapper.class @@ -76,7 +73,7 @@ public void shouldIgnoreDefaultValue() { assertThat( countryDts.getContinent() ).isEqualTo( Continent.NORTH_AMERICA ); } - @Test + @ProcessorTest @WithClasses({ Region.class, CountryMapper.class @@ -94,7 +91,7 @@ public void shouldHandleUpdateMethodsFromDtsToEntity() { assertThat( countryEntity.getContinent() ).isEqualTo( Continent.EUROPE ); } - @Test + @ProcessorTest @WithClasses({ Region.class, CountryMapper.class @@ -113,7 +110,7 @@ public void shouldHandleUpdateMethodsFromEntityToEntity() { assertThat( target.getContinent() ).isEqualTo( Continent.EUROPE ); } - @Test + @ProcessorTest @WithClasses({ ErroneousMapper.class, Region.class, @@ -136,7 +133,7 @@ public void shouldHandleUpdateMethodsFromEntityToEntity() { public void errorOnDefaultValueAndConstant() { } - @Test + @ProcessorTest @WithClasses({ ErroneousMapper2.class, Region.class, @@ -159,7 +156,7 @@ public void errorOnDefaultValueAndConstant() { public void errorOnDefaultValueAndExpression() { } - @Test + @ProcessorTest @IssueKey("2214") @WithClasses({ CountryMapperMultipleSources.class, @@ -173,7 +170,7 @@ public void shouldBeAbleToDetermineDefaultValueBasedOnOnlyTargetType() { assertThat( target.getCode() ).isEqualTo( "CH" ); } - @Test + @ProcessorTest @IssueKey("2220") @WithClasses({ ErroneousMissingSourceMapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/dependency/GraphAnalyzerTest.java b/processor/src/test/java/org/mapstruct/ap/test/dependency/GraphAnalyzerTest.java index d9f611d6d4..cbb0959b89 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/dependency/GraphAnalyzerTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/dependency/GraphAnalyzerTest.java @@ -5,16 +5,16 @@ */ package org.mapstruct.ap.test.dependency; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.HashSet; import java.util.List; import java.util.Set; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer; import org.mapstruct.ap.internal.util.Strings; +import static org.assertj.core.api.Assertions.assertThat; + /** * Unit test for {@link GraphAnalyzer}. * diff --git a/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java b/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java index 0a2ed604cf..cdf4cf6e97 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/dependency/OrderingTest.java @@ -5,17 +5,15 @@ */ package org.mapstruct.ap.test.dependency; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.Mapping; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for ordering mapped attributes by means of {@link Mapping#dependsOn()}. @@ -23,10 +21,9 @@ * @author Gunnar Morling */ @WithClasses({ Person.class, PersonDto.class, Address.class, AddressDto.class, AddressMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class OrderingTest { - @Test + @ProcessorTest @IssueKey("304") public void shouldApplyChainOfDependencies() { Address source = new Address(); @@ -40,7 +37,7 @@ public void shouldApplyChainOfDependencies() { assertThat( target.getFullName() ).isEqualTo( "Bob J. McRobb" ); } - @Test + @ProcessorTest @IssueKey("304") public void shouldApplySeveralDependenciesConfiguredForOneProperty() { Person source = new Person(); @@ -54,7 +51,7 @@ public void shouldApplySeveralDependenciesConfiguredForOneProperty() { assertThat( target.getFullName() ).isEqualTo( "Bob J. McRobb" ); } - @Test + @ProcessorTest @IssueKey("304") @WithClasses(ErroneousAddressMapperWithCyclicDependency.class) @ExpectedCompilationOutcome( @@ -71,7 +68,7 @@ public void shouldApplySeveralDependenciesConfiguredForOneProperty() { public void shouldReportErrorIfDependenciesContainCycle() { } - @Test + @ProcessorTest @IssueKey("304") @WithClasses(ErroneousAddressMapperWithUnknownPropertyInDependsOn.class) @ExpectedCompilationOutcome( diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java index 4f4454bcea..e61bd1cb2c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java @@ -5,35 +5,32 @@ */ package org.mapstruct.ap.test.destination; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + /** * @author Christophe Labouisse on 27/05/2015. */ -@RunWith(AnnotationProcessorTestRunner.class) public class DestinationClassNameTest { - @Test + @ProcessorTest @WithClasses({ DestinationClassNameMapper.class }) public void shouldGenerateRightName() { DestinationClassNameMapper instance = DestinationClassNameMapper.INSTANCE; assertThat( instance.getClass().getSimpleName() ).isEqualTo( "MyDestinationClassNameMapperCustomImpl" ); } - @Test + @ProcessorTest @WithClasses({ DestinationClassNameWithJsr330Mapper.class }) public void shouldNotGenerateSpi() throws Exception { Class clazz = DestinationClassNameWithJsr330Mapper.class; try { Mappers.getMapper( clazz ); - Assert.fail( "Should have thrown an ClassNotFoundException" ); + fail( "Should have thrown an ClassNotFoundException" ); } catch ( RuntimeException e ) { assertThat( e.getCause() ).isNotNull() @@ -46,7 +43,7 @@ public void shouldNotGenerateSpi() throws Exception { assertThat( instance.getClass().getSimpleName() ).isEqualTo( "DestinationClassNameWithJsr330MapperJsr330Impl" ); } - @Test + @ProcessorTest @WithClasses({ DestinationClassNameMapperConfig.class, DestinationClassNameMapperWithConfig.class }) public void shouldGenerateRightNameWithConfig() { DestinationClassNameMapperWithConfig instance = DestinationClassNameMapperWithConfig.INSTANCE; @@ -54,7 +51,7 @@ public void shouldGenerateRightNameWithConfig() { .isEqualTo( "MyDestinationClassNameMapperWithConfigConfigImpl" ); } - @Test + @ProcessorTest @WithClasses({ DestinationClassNameMapperConfig.class, DestinationClassNameMapperWithConfigOverride.class }) public void shouldGenerateRightNameWithConfigOverride() { DestinationClassNameMapperWithConfigOverride instance = DestinationClassNameMapperWithConfigOverride.INSTANCE; @@ -62,7 +59,7 @@ public void shouldGenerateRightNameWithConfigOverride() { .isEqualTo( "CustomDestinationClassNameMapperWithConfigOverrideMyImpl" ); } - @Test + @ProcessorTest @WithClasses({ DestinationClassNameMapperDecorated.class, DestinationClassNameMapperDecorator.class }) public void shouldGenerateRightNameWithDecorator() { DestinationClassNameMapperDecorated instance = DestinationClassNameMapperDecorated.INSTANCE; @@ -73,7 +70,7 @@ public void shouldGenerateRightNameWithDecorator() { .isEqualTo( "MyDestinationClassNameMapperDecoratedCustomImpl_" ); } - @Test + @ProcessorTest @WithClasses({ AbstractDestinationClassNameMapper.class, AbstractDestinationPackageNameMapper.class }) public void shouldWorkWithAbstractClasses() { AbstractDestinationClassNameMapper mapper1 = AbstractDestinationClassNameMapper.INSTANCE; diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameTest.java index 8f55dedc9b..63c7ece6d5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameTest.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.destination; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Christophe Labouisse on 27/05/2015. */ @IssueKey( "556" ) -@RunWith(AnnotationProcessorTestRunner.class) public class DestinationPackageNameTest { - @Test + @ProcessorTest @WithClasses({ DestinationPackageNameMapper.class }) public void shouldGenerateInRightPackage() { DestinationPackageNameMapper instance = DestinationPackageNameMapper.INSTANCE; @@ -27,7 +24,7 @@ public void shouldGenerateInRightPackage() { .isEqualTo( "org.mapstruct.ap.test.destination.dest.DestinationPackageNameMapperImpl" ); } - @Test + @ProcessorTest @WithClasses({ DestinationPackageNameMapperWithSuffix.class }) public void shouldGenerateInRightPackageWithSuffix() { DestinationPackageNameMapperWithSuffix instance = DestinationPackageNameMapperWithSuffix.INSTANCE; @@ -35,7 +32,7 @@ public void shouldGenerateInRightPackageWithSuffix() { .isEqualTo( "org.mapstruct.ap.test.destination.dest.DestinationPackageNameMapperWithSuffixMyImpl" ); } - @Test + @ProcessorTest @WithClasses({ DestinationPackageNameMapperConfig.class, DestinationPackageNameMapperWithConfig.class }) public void shouldGenerateRightSuffixWithConfig() { DestinationPackageNameMapperWithConfig instance = DestinationPackageNameMapperWithConfig.INSTANCE; @@ -43,7 +40,7 @@ public void shouldGenerateRightSuffixWithConfig() { .isEqualTo( "org.mapstruct.ap.test.destination.dest.DestinationPackageNameMapperWithConfigImpl" ); } - @Test + @ProcessorTest @WithClasses({ DestinationPackageNameMapperConfig.class, DestinationPackageNameMapperWithConfigOverride.class }) public void shouldGenerateRightSuffixWithConfigOverride() { DestinationPackageNameMapperWithConfigOverride instance = @@ -54,7 +51,7 @@ public void shouldGenerateRightSuffixWithConfigOverride() { ); } - @Test + @ProcessorTest @WithClasses({ DestinationPackageNameMapperDecorated.class, DestinationPackageNameMapperDecorator.class }) public void shouldGenerateRightSuffixWithDecorator() { DestinationPackageNameMapperDecorated instance = DestinationPackageNameMapperDecorated.INSTANCE; diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java index 3887cd4a46..99fd2b98cd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousannotatedfactorymethod/AmbiguousAnnotatedFactoryTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Remo Meier @@ -20,10 +18,9 @@ Bar.class, Foo.class, AmbiguousBarFactory.class, Source.class, SourceTargetMapperAndBarFactory.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class AmbiguousAnnotatedFactoryTest { - @Test + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java index dbce2ab5b2..b9ae5b8ffa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousfactorymethod/FactoryTest.java @@ -5,15 +5,13 @@ */ package org.mapstruct.ap.test.erroneous.ambiguousfactorymethod; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.a.BarFactory; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Sjaak Derksen @@ -23,10 +21,9 @@ Bar.class, Foo.class, BarFactory.class, Source.class, SourceTargetMapperAndBarFactory.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class FactoryTest { - @Test + @ProcessorTest @IssueKey("81") @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbiguousMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbiguousMapperTest.java index 3c4bd00a7e..c3fa15b244 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbiguousMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/ambiguousmapping/AmbiguousMapperTest.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.erroneous.ambiguousmapping; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @IssueKey("2156") -@RunWith(AnnotationProcessorTestRunner.class) public class AmbiguousMapperTest { - @Test + @ProcessorTest @WithClasses( ErroneousWithAmbiguousMethodsMapper.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -38,7 +35,7 @@ public class AmbiguousMapperTest { public void testErrorMessageForAmbiguous() { } - @Test + @ProcessorTest @WithClasses( ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -63,7 +60,7 @@ public void testErrorMessageForAmbiguous() { public void testErrorMessageForManyAmbiguous() { } - @Test + @ProcessorTest @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses( ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class) @ExpectedCompilationOutcome( diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/annotationnotfound/AnnotationNotFoundTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/annotationnotfound/AnnotationNotFoundTest.java index 50d5b7c82b..8a5ffefe6d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/annotationnotfound/AnnotationNotFoundTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/annotationnotfound/AnnotationNotFoundTest.java @@ -7,14 +7,12 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Test for (custom / external) annotation that is not on class path @@ -22,10 +20,9 @@ * @author Sjaak Derksen */ @WithClasses( { Source.class, Target.class, ErroneousMapper.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class AnnotationNotFoundTest { - @Test + @ProcessorTest @IssueKey( "298" ) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java index bc36e2af42..0d040f7366 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/attributereference/ErroneousMappingsTest.java @@ -7,14 +7,12 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Test for using unknown attributes in {@code @Mapping}. @@ -22,10 +20,9 @@ * @author Gunnar Morling */ @WithClasses({ Source.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ErroneousMappingsTest { - @Test + @ProcessorTest @IssueKey("11") @WithClasses( { ErroneousMapper.class, AnotherTarget.class } ) @ExpectedCompilationOutcome( @@ -58,7 +55,7 @@ public class ErroneousMappingsTest { public void shouldFailToGenerateMappings() { } - @Test + @ProcessorTest @WithClasses( { ErroneousMapper1.class, DummySource.class } ) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -72,7 +69,7 @@ public void shouldFailToGenerateMappings() { public void shouldFailToGenerateMappingsErrorOnMandatoryParameterName() { } - @Test + @ProcessorTest @WithClasses( { ErroneousMapper2.class } ) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/misbalancedbraces/MisbalancedBracesTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/misbalancedbraces/MisbalancedBracesTest.java index bc270d446e..f3f6e2417a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/misbalancedbraces/MisbalancedBracesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/misbalancedbraces/MisbalancedBracesTest.java @@ -7,15 +7,13 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.DisableCheckstyle; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Test for making sure that expressions with too many closing braces are passed through, letting the compiler raise an @@ -25,12 +23,11 @@ */ @WithClasses({ MapperWithMalformedExpression.class, Source.class, Target.class }) @DisableCheckstyle -@RunWith(AnnotationProcessorTestRunner.class) public class MisbalancedBracesTest { // the compiler messages due to the additional closing brace differ between JDK and Eclipse, hence we can only // assert on the line number but not the message - @Test + @ProcessorTest @IssueKey("1056") @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java index 627478d1c0..43d7dfca5b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/propertymapping/ErroneousPropertyMappingTest.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.erroneous.propertymapping; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @IssueKey("1504") @WithClasses({ Source.class, Target.class, UnmappableClass.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ErroneousPropertyMappingTest { - @Test + @ProcessorTest @WithClasses(ErroneousMapper1.class) @IssueKey("1504") @ExpectedCompilationOutcome( @@ -34,7 +31,7 @@ public class ErroneousPropertyMappingTest { public void testUnmappableSourceProperty() { } - @Test + @ProcessorTest @WithClasses(ErroneousMapper2.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -48,7 +45,7 @@ public void testUnmappableSourceProperty() { public void testUnmappableSourcePropertyWithNoSourceDefinedInMapping() { } - @Test + @ProcessorTest @WithClasses(ErroneousMapper3.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -61,7 +58,7 @@ public void testUnmappableSourcePropertyWithNoSourceDefinedInMapping() { public void testUnmappableConstantAssignment() { } - @Test + @ProcessorTest @WithClasses(ErroneousMapper4.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java index 2a1b2bae28..a045d16dfa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java @@ -7,14 +7,12 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Tests failures expected for unmappable attributes. @@ -22,10 +20,9 @@ * @author Gunnar Morling */ @WithClasses({ ErroneousMapper.class, Source.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ErroneousMappingsTest { - @Test + @ProcessorTest @IssueKey("6") @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/exceptions/ExceptionTest.java b/processor/src/test/java/org/mapstruct/ap/test/exceptions/ExceptionTest.java index 2364381efe..212893052a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/exceptions/ExceptionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/exceptions/ExceptionTest.java @@ -11,13 +11,13 @@ import java.util.List; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.exceptions.imports.TestException1; import org.mapstruct.ap.test.exceptions.imports.TestExceptionBase; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * @@ -32,105 +32,115 @@ TestExceptionBase.class, TestException1.class, TestException2.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class ExceptionTest { - @Test( expected = RuntimeException.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowRuntimeInBeanMapping() throws TestException2, ParseException { Source source = new Source(); source.setSize( 1 ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.sourceToTarget( source ); + assertThatThrownBy( () -> sourceTargetMapper.sourceToTarget( source ) ) + .isInstanceOf( RuntimeException.class ); } - @Test( expected = TestException2.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowTestException2InBeanMapping() throws TestException2, ParseException { Source source = new Source(); source.setSize( 2 ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.sourceToTarget( source ); + assertThatThrownBy( () -> sourceTargetMapper.sourceToTarget( source ) ) + .isInstanceOf( TestException2.class ); } - @Test( expected = ParseException.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowTestParseExceptionInBeanMappingDueToTypeConverion() throws TestException2, ParseException { Source source = new Source(); source.setDate( "nonsense" ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.sourceToTarget( source ); + assertThatThrownBy( () -> sourceTargetMapper.sourceToTarget( source ) ) + .isInstanceOf( ParseException.class ); } - @Test( expected = RuntimeException.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowRuntimeInIterableMapping() throws TestException2 { List source = new ArrayList<>(); source.add( 1 ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.integerListToLongList( source ); + assertThatThrownBy( () -> sourceTargetMapper.integerListToLongList( source ) ) + .isInstanceOf( RuntimeException.class ); } - @Test( expected = TestException2.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowTestException2InIterableMapping() throws TestException2 { List source = new ArrayList<>(); source.add( 2 ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.integerListToLongList( source ); + assertThatThrownBy( () -> sourceTargetMapper.integerListToLongList( source ) ) + .isInstanceOf( TestException2.class ); } - @Test( expected = RuntimeException.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowRuntimeInMapKeyMapping() throws TestException2 { Map source = new HashMap<>(); source.put( 1, "test" ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.integerKeyMapToLongKeyMap( source ); + assertThatThrownBy( () -> sourceTargetMapper.integerKeyMapToLongKeyMap( source ) ) + .isInstanceOf( RuntimeException.class ); } - @Test( expected = TestException2.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowTestException2InMapKeyMapping() throws TestException2 { Map source = new HashMap<>(); source.put( 2, "test" ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.integerKeyMapToLongKeyMap( source ); + assertThatThrownBy( () -> sourceTargetMapper.integerKeyMapToLongKeyMap( source ) ) + .isInstanceOf( TestException2.class ); } - @Test( expected = RuntimeException.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowRuntimeInMapValueMapping() throws TestException2 { Map source = new HashMap<>(); source.put( "test", 1 ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.integerValueMapToLongValueMap( source ); + assertThatThrownBy( () -> sourceTargetMapper.integerValueMapToLongValueMap( source ) ) + .isInstanceOf( RuntimeException.class ); } - @Test( expected = TestException2.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowTestException2InMapValueMapping() throws TestException2 { Map source = new HashMap<>(); source.put( "test", 2 ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.integerValueMapToLongValueMap( source ); + assertThatThrownBy( () -> sourceTargetMapper.integerValueMapToLongValueMap( source ) ) + .isInstanceOf( TestException2.class ); } - @Test( expected = RuntimeException.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowRuntimeInBeanMappingViaBaseException() throws TestExceptionBase { Source source = new Source(); source.setSize( 1 ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.sourceToTargetViaBaseException( source ); + assertThatThrownBy( () -> sourceTargetMapper.sourceToTargetViaBaseException( source ) ) + .isInstanceOf( RuntimeException.class ); } - @Test( expected = TestException2.class ) + @ProcessorTest @IssueKey( "198" ) public void shouldThrowTestException2InBeanMappingViaBaseException() throws TestExceptionBase { Source source = new Source(); source.setSize( 2 ); SourceTargetMapper sourceTargetMapper = SourceTargetMapper.INSTANCE; - sourceTargetMapper.sourceToTargetViaBaseException( source ); + assertThatThrownBy( () -> sourceTargetMapper.sourceToTargetViaBaseException( source ) ) + .isInstanceOf( TestException2.class ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java index 1480c146fa..58cb0a1fcf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java @@ -5,15 +5,11 @@ */ package org.mapstruct.ap.test.factories; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.factories.a.BarFactory; import org.mapstruct.ap.test.factories.targettype.Bar9Base; import org.mapstruct.ap.test.factories.targettype.Bar9Child; @@ -21,8 +17,10 @@ import org.mapstruct.ap.test.factories.targettype.Foo9Base; import org.mapstruct.ap.test.factories.targettype.Foo9Child; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen @@ -33,9 +31,8 @@ org.mapstruct.ap.test.factories.b.BarFactory.class, org.mapstruct.ap.test.factories.c.BarFactory.class, Bar9Factory.class, Source.class, SourceTargetMapperAndBar2Factory.class, Target.class, CustomList.class, CustomListImpl.class, CustomMap.class, CustomMapImpl.class, FactoryCreatable.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class FactoryTest { - @Test + @ProcessorTest public void shouldUseThreeFactoryMethods() { Target target = SourceTargetMapperAndBar2Factory.INSTANCE.sourceToTarget( createSource() ); @@ -94,7 +91,7 @@ private Source createSource() { return source; } - @Test + @ProcessorTest @IssueKey( "136" ) @WithClasses( { GenericFactory.class, SourceTargetMapperWithGenericFactory.class } ) public void shouldUseGenericFactory() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/assignment/ParameterAssigmentFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/factories/assignment/ParameterAssigmentFactoryTest.java index 5ea82a5096..41140a00a4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/factories/assignment/ParameterAssigmentFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/assignment/ParameterAssigmentFactoryTest.java @@ -5,22 +5,19 @@ */ package org.mapstruct.ap.test.factories.assignment; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Remo Meier */ @WithClasses( { Bar5.class, Foo5A.class, Foo5B.class, Bar6.class, Foo6A.class, Foo6B.class, Bar7.class, Foo7A.class, Foo7B.class, Bar5Factory.class, Bar6Factory.class, Bar7Factory.class, ParameterAssignmentFactoryTestMapper.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class ParameterAssigmentFactoryTest { - @Test + @ProcessorTest public void shouldUseFactoryMethodWithMultipleParams() { Foo5A foo5a = new Foo5A(); foo5a.setPropB( "foo5a" ); @@ -38,7 +35,7 @@ public void shouldUseFactoryMethodWithMultipleParams() { assertThat( bar5.getSomeTypeProp1() ).isEqualTo( "foo5b" ); } - @Test + @ProcessorTest public void shouldUseFactoryMethodWithFirstParamsOfMappingMethod() { Foo6A foo6a = new Foo6A(); foo6a.setPropB( "foo6a" ); @@ -54,7 +51,7 @@ public void shouldUseFactoryMethodWithFirstParamsOfMappingMethod() { assertThat( bar6.getSomeTypeProp0() ).isEqualTo( "FOO6A" ); } - @Test + @ProcessorTest public void shouldUseFactoryMethodWithSecondParamsOfMappingMethod() { Foo7A foo7a = new Foo7A(); foo7a.setPropB( "foo7a" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/qualified/QualifiedFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/factories/qualified/QualifiedFactoryTest.java index 226c2ca8b9..ac84e555d3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/factories/qualified/QualifiedFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/qualified/QualifiedFactoryTest.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.factories.qualified; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Remo Meier */ @WithClasses( { Foo10.class, Bar10.class, TestQualifier.class, Bar10Factory.class, QualifiedFactoryTestMapper.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class QualifiedFactoryTest { - @Test + @ProcessorTest public void shouldUseFactoryWithoutQualifier() { Foo10 foo10 = new Foo10(); foo10.setProp( "foo10" ); @@ -31,7 +28,7 @@ public void shouldUseFactoryWithoutQualifier() { assertThat( bar10.getSomeTypeProp() ).isEqualTo( "foo10" ); } - @Test + @ProcessorTest public void shouldUseFactoryWithQualifier() { Foo10 foo10 = new Foo10(); foo10.setProp( "foo10" ); @@ -43,7 +40,7 @@ public void shouldUseFactoryWithQualifier() { assertThat( bar10.getSomeTypeProp() ).isEqualTo( "foo10" ); } - @Test + @ProcessorTest public void shouldUseFactoryWithQualifierName() { Foo10 foo10 = new Foo10(); foo10.setProp( "foo10" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/targettype/ProductTypeFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/factories/targettype/ProductTypeFactoryTest.java index 0d39e433c0..ce140ee93c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/factories/targettype/ProductTypeFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/factories/targettype/ProductTypeFactoryTest.java @@ -5,22 +5,19 @@ */ package org.mapstruct.ap.test.factories.targettype; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Remo Meier */ @WithClasses( { Foo9Base.class, Foo9Child.class, Bar9Base.class, Bar9Child.class, Bar9Factory.class, TargetTypeFactoryTestMapper.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class ProductTypeFactoryTest { - @Test + @ProcessorTest public void shouldUseFactoryTwoCreateBaseClassDueToTargetType() { Foo9Base foo9 = new Foo9Base(); foo9.setProp( "foo9" ); @@ -32,7 +29,7 @@ public void shouldUseFactoryTwoCreateBaseClassDueToTargetType() { assertThat( bar9.getSomeTypeProp() ).isEqualTo( "FOO9" ); } - @Test + @ProcessorTest public void shouldUseFactoryTwoCreateChildClassDueToTargetType() { Foo9Child foo9 = new Foo9Child(); foo9.setProp( "foo9" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/fields/FieldsMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/fields/FieldsMappingTest.java index d215a00ab5..8e594969fc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/fields/FieldsMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/fields/FieldsMappingTest.java @@ -5,24 +5,21 @@ */ package org.mapstruct.ap.test.fields; -import static org.assertj.core.api.Assertions.assertThat; - import org.assertj.core.util.Lists; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey( "557" ) @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) public class FieldsMappingTest { - @Test + @ProcessorTest public void shouldMapSourceToTarget() { Source source = new Source(); source.normalInt = 4; @@ -41,7 +38,7 @@ public void shouldMapSourceToTarget() { assertThat( target.fieldWithMethods ).isEqualTo( "4111" ); } - @Test + @ProcessorTest public void shouldMapTargetToSource() { Target target = new Target(); target.finalInt = "40"; diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java index 711d6d467a..87a22931ca 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java @@ -5,12 +5,12 @@ */ package org.mapstruct.ap.test.gem; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.MappingConstants; import org.mapstruct.ap.internal.gem.MappingConstantsGem; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test constants values * diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java index 3da35b91fb..0c92fc6bbe 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.gem; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.CollectionMappingStrategy; import org.mapstruct.InjectionStrategy; import org.mapstruct.MappingInheritanceStrategy; @@ -25,6 +23,8 @@ import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test for manually created gems on enumeration types * diff --git a/processor/src/test/java/org/mapstruct/ap/test/generics/GenericsTest.java b/processor/src/test/java/org/mapstruct/ap/test/generics/GenericsTest.java index 3f1ca3a818..091c59f78d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/generics/GenericsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/generics/GenericsTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.generics; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Andreas Gudian @@ -24,10 +22,9 @@ SourceTargetMapper.class, TargetTo.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class GenericsTest { - @Test + @ProcessorTest @IssueKey("574") public void mapsIdCorrectly() { TargetTo target = new TargetTo(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/generics/genericsupertype/MapperWithGenericSuperClassTest.java b/processor/src/test/java/org/mapstruct/ap/test/generics/genericsupertype/MapperWithGenericSuperClassTest.java index 30b961432c..4caa6f5712 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/generics/genericsupertype/MapperWithGenericSuperClassTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/generics/genericsupertype/MapperWithGenericSuperClassTest.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.generics.genericsupertype; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Gunnar Morling @@ -24,10 +22,9 @@ MapperBase.class, VesselSearchResultMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class MapperWithGenericSuperClassTest { - @Test + @ProcessorTest public void canCreateImplementationForMapperWithGenericSuperClass() { Vessel vessel = new Vessel(); vessel.setName( "Pacific Queen" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java index 417a0aa26a..3bafb776a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/IgnorePropertyTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.ignore; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for ignoring properties during the mapping. @@ -24,10 +22,9 @@ * @author Gunnar Morling */ @WithClasses({ Animal.class, AnimalDto.class, AnimalMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class IgnorePropertyTest { - @Test + @ProcessorTest @IssueKey("72") public void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() { Animal animal = new Animal( "Bruno", 100, 23, "black" ); @@ -43,7 +40,7 @@ public void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() { assertThat( animalDto.publicColor ).isNull(); } - @Test + @ProcessorTest @IssueKey("1392") public void shouldIgnoreAllTargetPropertiesWithNoUnmappedTargetWarnings() { Animal animal = new Animal( "Bruno", 100, 23, "black" ); @@ -59,7 +56,7 @@ public void shouldIgnoreAllTargetPropertiesWithNoUnmappedTargetWarnings() { assertThat( animalDto.publicColor ).isNull(); } - @Test + @ProcessorTest @IssueKey("337") public void propertyIsIgnoredInReverseMappingWhenSourceIsAlsoSpecifiedICWIgnore() { AnimalDto animalDto = new AnimalDto( "Bruno", 100, 23, "black" ); @@ -73,7 +70,7 @@ public void propertyIsIgnoredInReverseMappingWhenSourceIsAlsoSpecifiedICWIgnore( assertThat( animal.publicColour ).isNull(); } - @Test + @ProcessorTest @IssueKey("833") @WithClasses({Preditor.class, PreditorDto.class, ErroneousTargetHasNoWriteAccessorMapper.class}) @ExpectedCompilationOutcome( diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/expand/IgnorePropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/expand/IgnorePropertyTest.java index f3dc994a6e..deaa75bbe7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/expand/IgnorePropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/expand/IgnorePropertyTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.ignore.expand; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for ignoring properties during the mapping. @@ -25,10 +23,9 @@ FlattenedToolBox.class, ToolBoxMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class IgnorePropertyTest { - @Test + @ProcessorTest @IssueKey("1392") public void shouldIgnoreAll() { FlattenedToolBox toolboxSource = new FlattenedToolBox(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java index ef0cf6fc68..7ff4ea386d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignore/inherit/IgnorePropertyTest.java @@ -7,13 +7,11 @@ import java.util.Date; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for ignoring properties during the mapping. @@ -30,10 +28,9 @@ WorkBenchEntity.class, ToolMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class IgnorePropertyTest { - @Test + @ProcessorTest @IssueKey("1392") /** * Should not issue warnings on unmapped target properties @@ -54,7 +51,7 @@ public void shouldIgnoreAllExeptOveriddenInherited() { } - @Test + @ProcessorTest @IssueKey("1933") public void shouldInheritIgnoreByDefaultFromBase() { @@ -74,7 +71,7 @@ public void shouldInheritIgnoreByDefaultFromBase() { assertThat( benchTarget.getCreationDate() ).isNull(); } - @Test + @ProcessorTest @IssueKey("1933") public void shouldOnlyIgnoreBase() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/ConflictingTypesNamesTest.java b/processor/src/test/java/org/mapstruct/ap/test/imports/ConflictingTypesNamesTest.java index 0fda48c63d..d09ddf66a2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/imports/ConflictingTypesNamesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/ConflictingTypesNamesTest.java @@ -5,11 +5,7 @@ */ package org.mapstruct.ap.test.imports; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.imports.from.Foo; import org.mapstruct.ap.test.imports.from.FooWrapper; import org.mapstruct.ap.test.imports.referenced.GenericMapper; @@ -17,10 +13,12 @@ import org.mapstruct.ap.test.imports.referenced.Source; import org.mapstruct.ap.test.imports.referenced.Target; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; + /** * Test for generating a mapper which references types whose names clash with names of used annotations and exceptions. * @@ -43,17 +41,12 @@ org.mapstruct.ap.test.imports.to.FooWrapper.class, SecondSourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ConflictingTypesNamesTest { - private final GeneratedSource generatedSource = new GeneratedSource(); - - @Rule - public GeneratedSource getGeneratedSource() { - return generatedSource; - } + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest public void mapperImportingTypesWithConflictingNamesCanBeGenerated() { Named source = new Named(); source.setFoo( 123 ); @@ -71,7 +64,7 @@ public void mapperImportingTypesWithConflictingNamesCanBeGenerated() { assertThat( foo2.getName() ).isEqualTo( "bar" ); } - @Test + @ProcessorTest @IssueKey("178") public void mapperHasNoUnnecessaryImports() { Source source = new Source(); @@ -91,7 +84,7 @@ public void mapperHasNoUnnecessaryImports() { generatedSource.forMapper( SecondSourceTargetMapper.class ).containsNoImportFor( NotImportedDatatype.class ); } - @Test + @ProcessorTest @IssueKey("156") public void importsForTargetTypes() { FooWrapper source = new FooWrapper(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java b/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java index 95dbbd3ee8..91e5df6a27 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java @@ -5,11 +5,7 @@ */ package org.mapstruct.ap.test.imports; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.imports.innerclasses.BeanFacade; import org.mapstruct.ap.test.imports.innerclasses.BeanWithInnerEnum; import org.mapstruct.ap.test.imports.innerclasses.BeanWithInnerEnum.InnerEnum; @@ -21,10 +17,12 @@ import org.mapstruct.ap.test.imports.innerclasses.TargetWithInnerClass.TargetInnerClass; import org.mapstruct.ap.test.imports.innerclasses.TargetWithInnerClass.TargetInnerClass.TargetInnerInnerClass; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; + /** * Test for generating a mapper which references nested types (static inner classes). * @@ -34,17 +32,12 @@ SourceWithInnerClass.class, TargetWithInnerClass.class, InnerClassMapper.class, // BeanFacade.class, BeanWithInnerEnum.class, BeanWithInnerEnumMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class InnerClassesImportsTest { - private final GeneratedSource generatedSource = new GeneratedSource(); - - @Rule - public GeneratedSource getGeneratedSource() { - return generatedSource; - } + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @IssueKey( "412" ) public void mapperRequiresInnerClassImports() { SourceWithInnerClass source = new SourceWithInnerClass(); @@ -58,7 +51,7 @@ public void mapperRequiresInnerClassImports() { generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( TargetInnerClass.class ); } - @Test + @ProcessorTest @IssueKey( "412" ) public void mapperRequiresInnerInnerClassImports() { SourceInnerClass source = new SourceInnerClass(); @@ -72,7 +65,7 @@ public void mapperRequiresInnerInnerClassImports() { generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( TargetInnerInnerClass.class ); } - @Test + @ProcessorTest @IssueKey( "209" ) public void mapperRequiresInnerEnumImports() { BeanWithInnerEnum source = new BeanWithInnerEnum(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/decorator/DecoratorInAnotherPackageTest.java b/processor/src/test/java/org/mapstruct/ap/test/imports/decorator/DecoratorInAnotherPackageTest.java index 13f373278f..90249b319e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/imports/decorator/DecoratorInAnotherPackageTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/decorator/DecoratorInAnotherPackageTest.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.imports.decorator; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.imports.decorator.other.ActorMapperDecorator; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for having a decorator in another package than the mapper interface. @@ -26,10 +24,9 @@ ActorMapper.class, ActorMapperDecorator.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class DecoratorInAnotherPackageTest { - @Test + @ProcessorTest public void shouldApplyDecoratorFromAnotherPackage() { Actor actor = new Actor(); actor.setAwards( 3 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/sourcewithextendsbound/SourceTypeContainsCollectionWithExtendsBoundTest.java b/processor/src/test/java/org/mapstruct/ap/test/imports/sourcewithextendsbound/SourceTypeContainsCollectionWithExtendsBoundTest.java index 069e23a58f..37f4b7a0a7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/imports/sourcewithextendsbound/SourceTypeContainsCollectionWithExtendsBoundTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/sourcewithextendsbound/SourceTypeContainsCollectionWithExtendsBoundTest.java @@ -5,23 +5,21 @@ */ package org.mapstruct.ap.test.imports.sourcewithextendsbound; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Collections; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.imports.sourcewithextendsbound.astronautmapper.AstronautMapper; import org.mapstruct.ap.test.imports.sourcewithextendsbound.dto.AstronautDto; import org.mapstruct.ap.test.imports.sourcewithextendsbound.dto.SpaceshipDto; import org.mapstruct.ap.test.imports.sourcewithextendsbound.entity.Astronaut; import org.mapstruct.ap.test.imports.sourcewithextendsbound.entity.Spaceship; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; + /** * Test for generating a mapper which references nested types (static inner classes). * @@ -31,17 +29,12 @@ Astronaut.class, Spaceship.class, AstronautDto.class, SpaceshipDto.class, SpaceshipMapper.class, AstronautMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class SourceTypeContainsCollectionWithExtendsBoundTest { - private final GeneratedSource generatedSource = new GeneratedSource(); - - @Rule - public GeneratedSource getGeneratedSource() { - return generatedSource; - } + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @IssueKey("768") public void generatesImportsForCollectionWithExtendsBoundInSourceType() { Astronaut astronaut = new Astronaut(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java index b13629d06c..d127a9ac11 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.inheritance; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for propagation of attributes inherited from super types. @@ -19,10 +17,9 @@ * @author Gunnar Morling */ @WithClasses({ SourceBase.class, SourceExt.class, TargetBase.class, TargetExt.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class InheritanceTest { - @Test + @ProcessorTest @IssueKey("17") public void shouldMapAttributeFromSuperType() { SourceExt source = createSource(); @@ -32,7 +29,7 @@ public void shouldMapAttributeFromSuperType() { assertResult( target ); } - @Test + @ProcessorTest @IssueKey("19") public void shouldMapAttributeFromSuperTypeUsingTargetParameter() { SourceExt source = createSource(); @@ -43,7 +40,7 @@ public void shouldMapAttributeFromSuperTypeUsingTargetParameter() { assertResult( target ); } - @Test + @ProcessorTest @IssueKey("19") public void shouldMapAttributeFromSuperTypeUsingReturnedTargetParameter() { SourceExt source = createSource(); @@ -71,7 +68,7 @@ private SourceExt createSource() { return source; } - @Test + @ProcessorTest @IssueKey("17") public void shouldReverseMapAttributeFromSuperType() { TargetExt target = new TargetExt(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java index f00319dcf7..7b9f2d6196 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/attribute/AttributeInheritanceTest.java @@ -5,27 +5,24 @@ */ package org.mapstruct.ap.test.inheritance.attribute; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for setting an attribute where the target attribute of a super-type. * * @author Gunnar Morling */ -@RunWith(AnnotationProcessorTestRunner.class) public class AttributeInheritanceTest { - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) public void shouldMapAttributeFromSuperType() { Source source = new Source(); @@ -36,7 +33,7 @@ public void shouldMapAttributeFromSuperType() { assertThat( target.getFoo().toString() ).isEqualTo( "Bob" ); } - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, ErroneousTargetSourceMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java index d67734423e..e2dc947d35 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/complex/ComplexInheritanceTest.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.inheritance.complex; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for propagation of attributes inherited from super types. @@ -30,10 +27,9 @@ SourceExt.class, SourceExt2.class, TargetComposite.class, AdditionalFooSource.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ComplexInheritanceTest { - @Test + @ProcessorTest @IssueKey("34") @WithClasses({ StandaloneSourceCompositeTargetCompositeMapper.class }) public void shouldMapAttributesWithSuperTypeInStandaloneMapper() { @@ -47,7 +43,7 @@ public void shouldMapAttributesWithSuperTypeInStandaloneMapper() { assertThat( target.getProp5() ).containsOnly( 42, 999 ); } - @Test + @ProcessorTest @IssueKey("34") @WithClasses({ SourceCompositeTargetCompositeMapper.class, SourceBaseMappingHelper.class }) public void shouldMapAttributesWithSuperTypeUsingOtherMapper() { @@ -61,7 +57,7 @@ public void shouldMapAttributesWithSuperTypeUsingOtherMapper() { assertThat( target.getProp5() ).containsOnly( 43, 1000 ); } - @Test + @ProcessorTest @IssueKey("34") @WithClasses({ ErroneousSourceCompositeTargetCompositeMapper.class, AdditionalMappingHelper.class }) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritedmappingmethod/InheritedMappingMethodTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritedmappingmethod/InheritedMappingMethodTest.java index 874acfc81d..36c7c5ea08 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritedmappingmethod/InheritedMappingMethodTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritedmappingmethod/InheritedMappingMethodTest.java @@ -5,33 +5,30 @@ */ package org.mapstruct.ap.test.inheritedmappingmethod; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.inheritedmappingmethod._target.CarDto; import org.mapstruct.ap.test.inheritedmappingmethod._target.FastCarDto; import org.mapstruct.ap.test.inheritedmappingmethod.source.Car; import org.mapstruct.ap.test.inheritedmappingmethod.source.FastCar; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @IssueKey( "274" ) @WithClasses({ Car.class, CarDto.class, UnboundMappable.class, CarMapper.class, // FastCar.class, FastCarDto.class, BoundMappable.class, FastCarMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class InheritedMappingMethodTest { - @Test + @ProcessorTest public void shouldProvideUnboundedMapperInstance() { UnboundMappable instance = CarMapper.INSTANCE; assertThat( instance ).isNotNull(); } - @Test + @ProcessorTest public void shouldMapUsingUnboundedInheretedMappingMethod() { // given CarDto bikeDto = new CarDto(); @@ -45,13 +42,13 @@ public void shouldMapUsingUnboundedInheretedMappingMethod() { assertThat( bike.getHorsepower() ).isEqualTo( 130 ); } - @Test + @ProcessorTest public void shouldProvideBoundedMapperInstance() { BoundMappable instance = FastCarMapper.INSTANCE; assertThat( instance ).isNotNull(); } - @Test + @ProcessorTest public void shouldMapUsingBoundedInheretedMappingMethod() { // given FastCarDto bikeDto = new FastCarDto(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java index 8ab3583aa9..4d8e0c28ab 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.inheritfromconfig; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Andreas Gudian */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ BaseVehicleDto.class, BaseVehicleEntity.class, @@ -35,7 +32,7 @@ @IssueKey("168") public class InheritFromConfigTest { - @Test + @ProcessorTest public void autoInheritedMappingIsApplied() { CarDto carDto = newTestDto(); @@ -44,7 +41,7 @@ public void autoInheritedMappingIsApplied() { assertEntity( carEntity ); } - @Test + @ProcessorTest public void autoInheritedMappingIsAppliedForMappingTarget() { CarDto carDto = newTestDto(); CarEntity carEntity = new CarEntity(); @@ -54,7 +51,7 @@ public void autoInheritedMappingIsAppliedForMappingTarget() { assertEntity( carEntity ); } - @Test + @ProcessorTest public void autoInheritedMappingIsAppliedForMappingTargetWithTwoStepInheritance() { CarDto carDto = newTestDto(); CarEntity carEntity = new CarEntity(); @@ -77,7 +74,7 @@ private CarDto newTestDto() { return carDto; } - @Test + @ProcessorTest public void autoInheritedMappingIsOverriddenAtMethodLevel() { CarDto carDto = newTestDto(); @@ -88,7 +85,7 @@ public void autoInheritedMappingIsOverriddenAtMethodLevel() { assertThat( carEntity.getAuditTrail() ).isEqualTo( "fixed" ); } - @Test + @ProcessorTest public void autoInheritedMappingIsAppliedInReverse() { CarEntity carEntity = new CarEntity(); carEntity.setColor( "red" ); @@ -100,7 +97,7 @@ public void autoInheritedMappingIsAppliedInReverse() { assertThat( carDto.getId() ).isEqualTo( 42L ); } - @Test + @ProcessorTest public void explicitInheritedMappingIsAppliedInReverse() { CarEntity carEntity = new CarEntity(); carEntity.setColor( "red" ); @@ -112,7 +109,7 @@ public void explicitInheritedMappingIsAppliedInReverse() { assertThat( carDto.getId() ).isEqualTo( 42L ); } - @Test + @ProcessorTest @IssueKey( "1065" ) @WithClasses({ CarMapperReverseWithExplicitInheritance.class } ) public void explicitInheritedMappingIsAppliedInReverseDirectlyFromConfig() { @@ -127,7 +124,7 @@ public void explicitInheritedMappingIsAppliedInReverseDirectlyFromConfig() { assertThat( carDto.getId() ).isEqualTo( 42L ); } - @Test + @ProcessorTest @IssueKey( "1255" ) @WithClasses({ CarMapperReverseWithAutoInheritance.class, AutoInheritedReverseConfig.class } ) public void autoInheritedMappingIsAppliedInReverseDirectlyFromConfig() { @@ -142,7 +139,7 @@ public void autoInheritedMappingIsAppliedInReverseDirectlyFromConfig() { assertThat( carDto.getId() ).isEqualTo( 42L ); } - @Test + @ProcessorTest @IssueKey( "1255" ) @WithClasses({ CarMapperAllWithAutoInheritance.class, AutoInheritedAllConfig.class } ) public void autoInheritedMappingIsAppliedInForwardAndReverseDirectlyFromConfig() { @@ -156,7 +153,7 @@ public void autoInheritedMappingIsAppliedInForwardAndReverseDirectlyFromConfig() assertThat( carDto.getId() ).isEqualTo( carDto2.getId() ); } - @Test + @ProcessorTest public void explicitInheritedMappingWithTwoLevelsIsOverriddenAtMethodLevel() { CarDto carDto = newTestDto(); @@ -167,7 +164,7 @@ public void explicitInheritedMappingWithTwoLevelsIsOverriddenAtMethodLevel() { assertThat( carEntity.getAuditTrail() ).isEqualTo( "fixed" ); } - @Test + @ProcessorTest public void explicitInheritedMappingIsApplied() { CarDto carDto = newTestDto(); @@ -176,7 +173,7 @@ public void explicitInheritedMappingIsApplied() { assertEntity( carEntity ); } - @Test + @ProcessorTest @WithClasses({ DriverDto.class, CarWithDriverEntity.class, @@ -195,7 +192,7 @@ public void autoInheritedFromMultipleSources() { assertThat( carWithDriverEntity.getDriverName() ).isEqualTo( "Malcroft" ); } - @Test + @ProcessorTest @WithClasses({ Erroneous1Mapper.class, Erroneous1Config.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -227,7 +224,7 @@ public void autoInheritedFromMultipleSources() { public void erroneous1MultiplePrototypeMethodsMatch() { } - @Test + @ProcessorTest @WithClasses({ Erroneous2Mapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -247,7 +244,7 @@ public void erroneous1MultiplePrototypeMethodsMatch() { public void erroneous2InheritanceCycle() { } - @Test + @ProcessorTest @IssueKey( "1255" ) @WithClasses({ ErroneousMapperAutoInheritance.class, AutoInheritedReverseConfig.class } ) @ExpectedCompilationOutcome( @@ -261,7 +258,7 @@ public void erroneous2InheritanceCycle() { ) public void erroneousWrongReverseConfigInherited() { } - @Test + @ProcessorTest @IssueKey( "1255" ) @WithClasses({ ErroneousMapperReverseWithAutoInheritance.class, AutoInheritedConfig.class } ) @ExpectedCompilationOutcome( @@ -275,7 +272,7 @@ public void erroneousWrongReverseConfigInherited() { } ) public void erroneousWrongConfigInherited() { } - @Test + @ProcessorTest @IssueKey("1255") @WithClasses({ Erroneous3Mapper.class, Erroneous3Config.class }) @ExpectedCompilationOutcome( diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/multiple/CarMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/multiple/CarMapperTest.java index 3e765d9b18..bc59060e13 100755 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/multiple/CarMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/multiple/CarMapperTest.java @@ -5,17 +5,14 @@ */ package org.mapstruct.ap.test.inheritfromconfig.multiple; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Date; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -@RunWith(AnnotationProcessorTestRunner.class) +import static org.assertj.core.api.Assertions.assertThat; + @WithClasses({ BaseDto.class, BaseEntity.class, @@ -30,7 +27,7 @@ @IssueKey("1367") public class CarMapperTest { - @Test + @ProcessorTest public void testMapEntityToDto() { CarDto dto = CarMapper.MAPPER.mapTo( newCar() ); assertThat( dto.getDbId() ).isEqualTo( 9L ); @@ -38,7 +35,7 @@ public void testMapEntityToDto() { assertThat( dto.getSeatCount() ).isEqualTo( 5 ); } - @Test + @ProcessorTest public void testMapDtoToEntity() { CarEntity car = CarMapper.MAPPER.mapFrom( newCarDto() ); assertThat( car.getId() ).isEqualTo( 9L ); @@ -48,7 +45,7 @@ public void testMapDtoToEntity() { assertThat( car.getCreationDate() ).isNull(); } - @Test + @ProcessorTest public void testForwardMappingShouldTakePrecedence() { Car2Dto dto = new Car2Dto(); dto.setMaker( "mazda" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java index 61c767869a..65d8acfb2c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java @@ -8,17 +8,15 @@ import javax.inject.Inject; import javax.inject.Named; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -42,33 +40,32 @@ CustomerJsr330DefaultCompileOptionFieldMapper.class, GenderJsr330DefaultCompileOptionFieldMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) @ComponentScan(basePackageClasses = CustomerJsr330DefaultCompileOptionFieldMapper.class) @Configuration public class Jsr330DefaultCompileOptionFieldMapperTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); @Inject @Named private CustomerJsr330DefaultCompileOptionFieldMapper customerMapper; private ConfigurableApplicationContext context; - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldConvertToTarget() { // given CustomerEntity customerEntity = new CustomerEntity(); @@ -84,7 +81,7 @@ public void shouldConvertToTarget() { assertThat( customerDto.getGender() ).isEqualTo( GenderDto.M ); } - @Test + @ProcessorTest public void shouldHaveFieldInjection() { generatedSource.forMapper( CustomerJsr330DefaultCompileOptionFieldMapper.class ) .content() diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java index dbf4a4f7ee..c564648ecf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.injectionstrategy.jsr330.compileoptionconstructor; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -41,33 +39,32 @@ CustomerJsr330CompileOptionConstructorMapper.class, GenderJsr330CompileOptionConstructorMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) @ProcessorOption( name = "mapstruct.defaultInjectionStrategy", value = "constructor") @ComponentScan(basePackageClasses = CustomerJsr330CompileOptionConstructorMapper.class) @Configuration public class Jsr330CompileOptionConstructorMapperTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); @Autowired private CustomerJsr330CompileOptionConstructorMapper customerMapper; private ConfigurableApplicationContext context; - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldConvertToTarget() { // given CustomerEntity customerEntity = new CustomerEntity(); @@ -83,7 +80,7 @@ public void shouldConvertToTarget() { assertThat( customerDto.getGender() ).isEqualTo( GenderDto.M ); } - @Test + @ProcessorTest public void shouldHaveConstructorInjectionFromCompileOption() { generatedSource.forMapper( CustomerJsr330CompileOptionConstructorMapper.class ) .content() diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java index d76b077a3f..06f4180289 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.injectionstrategy.jsr330.constructor; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -42,32 +40,31 @@ ConstructorJsr330Config.class }) @IssueKey("571") -@RunWith(AnnotationProcessorTestRunner.class) @ComponentScan(basePackageClasses = CustomerJsr330ConstructorMapper.class) @Configuration public class Jsr330ConstructorMapperTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); @Autowired private CustomerJsr330ConstructorMapper customerMapper; private ConfigurableApplicationContext context; - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldConvertToTarget() { // given CustomerEntity customerEntity = new CustomerEntity(); @@ -83,7 +80,7 @@ public void shouldConvertToTarget() { assertThat( customerDto.getGender() ).isEqualTo( GenderDto.M ); } - @Test + @ProcessorTest public void shouldHaveConstructorInjection() { generatedSource.forMapper( CustomerJsr330ConstructorMapper.class ) .content() diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java index 551821df05..fc3bd09561 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java @@ -8,18 +8,16 @@ import javax.inject.Inject; import javax.inject.Named; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -44,33 +42,32 @@ FieldJsr330Config.class }) @IssueKey("571") -@RunWith(AnnotationProcessorTestRunner.class) @ComponentScan(basePackageClasses = CustomerJsr330FieldMapper.class) @Configuration public class Jsr330FieldMapperTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); @Inject @Named private CustomerJsr330FieldMapper customerMapper; private ConfigurableApplicationContext context; - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldConvertToTarget() { // given CustomerEntity customerEntity = new CustomerEntity(); @@ -86,7 +83,7 @@ public void shouldConvertToTarget() { assertThat( customerDto.getGender() ).isEqualTo( GenderDto.M ); } - @Test + @ProcessorTest public void shouldHaveFieldInjection() { generatedSource.forMapper( CustomerJsr330FieldMapper.class ) .content() diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/SpringDefaultMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/SpringDefaultMapperTest.java index c695214937..ef1b79b34b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/SpringDefaultMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/SpringDefaultMapperTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.injectionstrategy.spring._default; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -41,32 +39,31 @@ GenderSpringDefaultMapper.class }) @IssueKey("571") -@RunWith(AnnotationProcessorTestRunner.class) @ComponentScan(basePackageClasses = CustomerSpringDefaultMapper.class) @Configuration public class SpringDefaultMapperTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); @Autowired private CustomerSpringDefaultMapper customerMapper; private ConfigurableApplicationContext context; - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldConvertToTarget() { // given CustomerEntity customerEntity = new CustomerEntity(); @@ -82,7 +79,7 @@ public void shouldConvertToTarget() { assertThat( customerDto.getGender() ).isEqualTo( GenderDto.M ); } - @Test + @ProcessorTest public void shouldHaveFieldInjection() { generatedSource.forMapper( CustomerSpringDefaultMapper.class ) .content() diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java index 7608963a6d..8043c24305 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java @@ -10,22 +10,20 @@ import java.util.Date; import java.util.TimeZone; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordEntity; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -53,7 +51,6 @@ CustomerSpringCompileOptionConstructorMapper.class, GenderSpringCompileOptionConstructorMapper.class } ) -@RunWith(AnnotationProcessorTestRunner.class) @ProcessorOption( name = "mapstruct.defaultInjectionStrategy", value = "constructor") @ComponentScan(basePackageClasses = CustomerSpringCompileOptionConstructorMapper.class) @Configuration @@ -61,38 +58,38 @@ public class SpringCompileOptionConstructorMapperTest { private static TimeZone originalTimeZone; - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); @Autowired private CustomerRecordSpringCompileOptionConstructorMapper customerRecordMapper; private ConfigurableApplicationContext context; - @BeforeClass + @BeforeAll public static void setDefaultTimeZoneToCet() { originalTimeZone = TimeZone.getDefault(); TimeZone.setDefault( TimeZone.getTimeZone( "Europe/Berlin" ) ); } - @AfterClass + @AfterAll public static void restoreOriginalTimeZone() { TimeZone.setDefault( originalTimeZone ); } - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldConvertToTarget() throws Exception { // given CustomerEntity customerEntity = new CustomerEntity(); @@ -119,7 +116,7 @@ private Date createDate(String date) throws ParseException { return sdf.parse( date ); } - @Test + @ProcessorTest public void shouldConstructorInjectionFromCompileOption() { generatedSource.forMapper( CustomerSpringCompileOptionConstructorMapper.class ) .content() diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java index 267ad8dd33..1346ac1ee8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java @@ -10,13 +10,11 @@ import java.util.Date; import java.util.TimeZone; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordDto; @@ -24,8 +22,8 @@ import org.mapstruct.ap.test.injectionstrategy.shared.Gender; import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -54,45 +52,44 @@ ConstructorSpringConfig.class } ) @IssueKey( "571" ) -@RunWith(AnnotationProcessorTestRunner.class) @ComponentScan(basePackageClasses = CustomerSpringConstructorMapper.class) @Configuration public class SpringConstructorMapperTest { private static TimeZone originalTimeZone; - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); @Autowired private CustomerRecordSpringConstructorMapper customerRecordMapper; private ConfigurableApplicationContext context; - @BeforeClass + @BeforeAll public static void setDefaultTimeZoneToCet() { originalTimeZone = TimeZone.getDefault(); TimeZone.setDefault( TimeZone.getTimeZone( "Europe/Berlin" ) ); } - @AfterClass + @AfterAll public static void restoreOriginalTimeZone() { TimeZone.setDefault( originalTimeZone ); } - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldConvertToTarget() throws Exception { // given CustomerEntity customerEntity = new CustomerEntity(); @@ -114,7 +111,7 @@ public void shouldConvertToTarget() throws Exception { assertThat( customerRecordDto.getRegistrationDate().toString() ).isEqualTo( "1982-08-31T10:20:56.000+02:00" ); } - @Test + @ProcessorTest public void shouldHaveConstructorInjection() { generatedSource.forMapper( CustomerSpringConstructorMapper.class ) .content() diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/SpringFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/SpringFieldMapperTest.java index 12cb9577dc..8ee03f4bfb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/SpringFieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/SpringFieldMapperTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.injectionstrategy.spring.field; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.Gender; import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -42,32 +40,31 @@ FieldSpringConfig.class }) @IssueKey("571") -@RunWith(AnnotationProcessorTestRunner.class) @ComponentScan(basePackageClasses = CustomerSpringFieldMapper.class) @Configuration public class SpringFieldMapperTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); @Autowired private CustomerSpringFieldMapper customerMapper; private ConfigurableApplicationContext context; - @Before + @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); context.getAutowireCapableBeanFactory().autowireBean( this ); } - @After + @AfterEach public void springDown() { if ( context != null ) { context.close(); } } - @Test + @ProcessorTest public void shouldConvertToTarget() { // given CustomerEntity customerEntity = new CustomerEntity(); @@ -83,7 +80,7 @@ public void shouldConvertToTarget() { assertThat( customerDto.getGender() ).isEqualTo( GenderDto.M ); } - @Test + @ProcessorTest public void shouldHaveFieldInjection() { generatedSource.forMapper( CustomerSpringFieldMapper.class ) .content() diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/StreamMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/StreamMappingTest.java index a6fa793d57..a15468e5e1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/StreamMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/StreamMappingTest.java @@ -5,19 +5,17 @@ */ package org.mapstruct.ap.test.java8stream; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.stream.Stream; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ Source.class, @@ -29,10 +27,9 @@ StringHolder.class }) @IssueKey( "962" ) -@RunWith(AnnotationProcessorTestRunner.class) public class StreamMappingTest { - @Test + @ProcessorTest public void shouldMapNullList() { Source source = new Source(); @@ -42,7 +39,7 @@ public void shouldMapNullList() { assertThat( target.getStringList() ).isNull(); } - @Test + @ProcessorTest public void shouldReverseMapNullList() { Target target = new Target(); @@ -52,7 +49,7 @@ public void shouldReverseMapNullList() { assertThat( source.getStringStream() ).isNull(); } - @Test + @ProcessorTest public void shouldMapList() { Source source = new Source(); source.setStringStream( Stream.of( "Bob", "Alice" ) ); @@ -63,7 +60,7 @@ public void shouldMapList() { assertThat( target.getStringList() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest public void shouldMapListWithoutSetter() { Source source = new Source(); source.setStringStream2( Stream.of( "Bob", "Alice" ) ); @@ -74,7 +71,7 @@ public void shouldMapListWithoutSetter() { assertThat( target.getStringListNoSetter() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest public void shouldReverseMapList() { Target target = new Target(); target.setStringList( Arrays.asList( "Bob", "Alice" ) ); @@ -85,7 +82,7 @@ public void shouldReverseMapList() { assertThat( source.getStringStream() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest public void shouldMapArrayList() { Source source = new Source(); source.setStringArrayStream( new ArrayList<>( Arrays.asList( "Bob", "Alice" ) ).stream() ); @@ -96,7 +93,7 @@ public void shouldMapArrayList() { assertThat( target.getStringArrayList() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest public void shouldReverseMapArrayList() { Target target = new Target(); target.setStringArrayList( new ArrayList<>( Arrays.asList( "Bob", "Alice" ) ) ); @@ -107,7 +104,7 @@ public void shouldReverseMapArrayList() { assertThat( source.getStringArrayStream() ).containsExactly( "Bob", "Alice" ); } - @Test + @ProcessorTest public void shouldMapSet() { Source source = new Source(); source.setStringStreamToSet( new HashSet<>( Arrays.asList( "Bob", "Alice" ) ).stream() ); @@ -118,7 +115,7 @@ public void shouldMapSet() { assertThat( target.getStringSet() ).contains( "Bob", "Alice" ); } - @Test + @ProcessorTest public void shouldReverseMapSet() { Target target = new Target(); target.setStringSet( new HashSet<>( Arrays.asList( "Bob", "Alice" ) ) ); @@ -129,7 +126,7 @@ public void shouldReverseMapSet() { assertThat( source.getStringStreamToSet() ).contains( "Bob", "Alice" ); } - @Test + @ProcessorTest public void shouldMapListToCollection() { Source source = new Source(); source.setIntegerStream( Stream.of( 1, 2 ) ); @@ -140,7 +137,7 @@ public void shouldMapListToCollection() { assertThat( target.getIntegerCollection() ).containsOnly( 1, 2 ); } - @Test + @ProcessorTest public void shouldReverseMapListToCollection() { Target target = new Target(); target.setIntegerCollection( Arrays.asList( 1, 2 ) ); @@ -151,7 +148,7 @@ public void shouldReverseMapListToCollection() { assertThat( source.getIntegerStream() ).containsOnly( 1, 2 ); } - @Test + @ProcessorTest public void shouldMapIntegerSetToStringSet() { Source source = new Source(); source.setAnotherIntegerStream( new HashSet<>( Arrays.asList( 1, 2 ) ).stream() ); @@ -162,7 +159,7 @@ public void shouldMapIntegerSetToStringSet() { assertThat( target.getAnotherStringSet() ).containsOnly( "1", "2" ); } - @Test + @ProcessorTest public void shouldReverseMapIntegerSetToStringSet() { Target target = new Target(); target.setAnotherStringSet( new HashSet<>( Arrays.asList( "1", "2" ) ) ); @@ -173,7 +170,7 @@ public void shouldReverseMapIntegerSetToStringSet() { assertThat( source.getAnotherIntegerStream() ).containsOnly( 1, 2 ); } - @Test + @ProcessorTest public void shouldMapSetOfEnumToStringSet() { Source source = new Source(); source.setColours( Stream.of( Colour.BLUE, Colour.GREEN ) ); @@ -184,7 +181,7 @@ public void shouldMapSetOfEnumToStringSet() { assertThat( target.getColours() ).containsOnly( "BLUE", "GREEN" ); } - @Test + @ProcessorTest public void shouldReverseMapSetOfEnumToStringSet() { Target target = new Target(); target.setColours( new HashSet<>( Arrays.asList( "BLUE", "GREEN" ) ) ); @@ -195,7 +192,7 @@ public void shouldReverseMapSetOfEnumToStringSet() { assertThat( source.getColours() ).containsOnly( Colour.GREEN, Colour.BLUE ); } - @Test + @ProcessorTest public void shouldMapIntegerStreamToNumberSet() { Set numbers = SourceTargetMapper.INSTANCE .integerStreamToNumberSet( Stream.of( 123, 456 ) ); @@ -204,7 +201,7 @@ public void shouldMapIntegerStreamToNumberSet() { assertThat( numbers ).containsOnly( 123, 456 ); } - @Test + @ProcessorTest public void shouldMapNonGenericList() { Source source = new Source(); source.setStringStream3( new ArrayList<>( Arrays.asList( "Bob", "Alice" ) ).stream() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamsTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamsTest.java index c91889e063..027f619c98 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamsTest.java @@ -10,13 +10,11 @@ import java.util.TreeSet; import java.util.stream.Stream; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -25,7 +23,6 @@ * @author Filip Hrisafov */ @IssueKey("962") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Source.class, Target.class, @@ -36,10 +33,10 @@ }) public class StreamsTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest public void shouldNotContainFunctionIdentity() { generatedSource.forMapper( StreamMapper.class ) .content() @@ -47,7 +44,7 @@ public void shouldNotContainFunctionIdentity() { .doesNotContain( "Function.identity()" ); } - @Test + @ProcessorTest public void shouldMapSourceStream() { List someInts = Arrays.asList( 1, 2, 3 ); Stream stream = someInts.stream(); @@ -84,7 +81,7 @@ public void shouldMapSourceStream() { assertThat( target.getTargetElements().get( 0 ).getSource() ).isEqualTo( "source1" ); } - @Test + @ProcessorTest public void shouldMapTargetStream() { List someInts = Arrays.asList( 1, 2, 3 ); Stream stream = someInts.stream(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/context/StreamWithContextTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/context/StreamWithContextTest.java index 91c1a41256..74fbeb10b8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/context/StreamWithContextTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/context/StreamWithContextTest.java @@ -8,11 +8,9 @@ import java.util.Collection; import java.util.stream.Stream; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -21,10 +19,9 @@ */ @WithClasses({ StreamContext.class, StreamWithContextMapper.class }) @IssueKey("962") -@RunWith(AnnotationProcessorTestRunner.class) public class StreamWithContextTest { - @Test + @ProcessorTest public void shouldApplyAfterMapping() { Stream stringStream = StreamWithContextMapper.INSTANCE.intStreamToStringStream( Stream.of( 1, 2, 3, 5 ) ); @@ -32,7 +29,7 @@ public void shouldApplyAfterMapping() { assertThat( stringStream ).containsOnly( "1", "2" ); } - @Test + @ProcessorTest public void shouldApplyBeforeMappingOnArray() { Integer[] integers = new Integer[] { 1, 3 }; Stream stringStream = StreamWithContextMapper.INSTANCE.arrayToStream( integers ); @@ -40,7 +37,7 @@ public void shouldApplyBeforeMappingOnArray() { assertThat( stringStream ).containsOnly( 30, 3 ); } - @Test + @ProcessorTest public void shouldApplyBeforeAndAfterMappingOnCollection() { Collection stringsStream = StreamWithContextMapper.INSTANCE.streamToCollection( Stream.of( 10, 20, 40 ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/DefaultStreamImplementationTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/DefaultStreamImplementationTest.java index ae64592c0e..91be53fe64 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/DefaultStreamImplementationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/DefaultStreamImplementationTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.java8stream.defaultimplementation; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -17,11 +15,11 @@ import java.util.TreeSet; import java.util.stream.Stream; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ Source.class, @@ -31,10 +29,9 @@ SourceTargetMapper.class }) @IssueKey("962") -@RunWith(AnnotationProcessorTestRunner.class) public class DefaultStreamImplementationTest { - @Test + @ProcessorTest public void shouldUseDefaultImplementationForNavigableSet() { NavigableSet target = SourceTargetMapper.INSTANCE.streamToNavigableSet( createSourceFooStream() ); @@ -43,7 +40,7 @@ public void shouldUseDefaultImplementationForNavigableSet() { assertThat( target ).isInstanceOf( TreeSet.class ); } - @Test + @ProcessorTest public void shouldUseDefaultImplementationForCollection() { Collection target = SourceTargetMapper.INSTANCE.streamToCollection( createSourceFooStream() ); @@ -52,7 +49,7 @@ public void shouldUseDefaultImplementationForCollection() { assertThat( target ).isInstanceOf( ArrayList.class ); } - @Test + @ProcessorTest public void shouldUseDefaultImplementationForIterable() { Iterable target = SourceTargetMapper.INSTANCE.streamToIterable( createSourceFooStream() ); @@ -61,7 +58,7 @@ public void shouldUseDefaultImplementationForIterable() { assertThat( target ).isInstanceOf( ArrayList.class ); } - @Test + @ProcessorTest public void shouldUseDefaultImplementationForList() { List target = SourceTargetMapper.INSTANCE.streamToList( createSourceFooStream() ); @@ -69,7 +66,7 @@ public void shouldUseDefaultImplementationForList() { assertThat( target ).isInstanceOf( ArrayList.class ); } - @Test + @ProcessorTest public void shouldUseDefaultImplementationForSet() { Set target = SourceTargetMapper.INSTANCE.streamToSet( createSourceFooStream() ); @@ -78,7 +75,7 @@ public void shouldUseDefaultImplementationForSet() { assertThat( target ).isInstanceOf( HashSet.class ); } - @Test + @ProcessorTest public void shouldUseDefaultImplementationForSortedSet() { SortedSet target = SourceTargetMapper.INSTANCE.streamToSortedSet( createSourceFooStream() ); @@ -87,7 +84,7 @@ public void shouldUseDefaultImplementationForSortedSet() { assertThat( target ).isInstanceOf( TreeSet.class ); } - @Test + @ProcessorTest public void shouldUseTargetParameterForMapping() { List target = new ArrayList<>(); SourceTargetMapper.INSTANCE.sourceFoosToTargetFoosUsingTargetParameter( @@ -98,7 +95,7 @@ public void shouldUseTargetParameterForMapping() { assertResultList( target ); } - @Test + @ProcessorTest public void shouldUseTargetParameterForArrayMapping() { TargetFoo[] target = new TargetFoo[3]; SourceTargetMapper.INSTANCE.streamToArrayUsingTargetParameter( @@ -110,7 +107,7 @@ public void shouldUseTargetParameterForArrayMapping() { assertThat( target ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ), null ); } - @Test + @ProcessorTest public void shouldUseTargetParameterForArrayMappingAndSmallerArray() { TargetFoo[] target = new TargetFoo[1]; SourceTargetMapper.INSTANCE.streamToArrayUsingTargetParameter( @@ -122,7 +119,7 @@ public void shouldUseTargetParameterForArrayMappingAndSmallerArray() { assertThat( target ).containsOnly( new TargetFoo( "Bob" ) ); } - @Test + @ProcessorTest public void shouldUseAndReturnTargetParameterForArrayMapping() { TargetFoo[] target = new TargetFoo[3]; TargetFoo[] result = @@ -133,7 +130,7 @@ public void shouldUseAndReturnTargetParameterForArrayMapping() { assertThat( target ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ), null ); } - @Test + @ProcessorTest public void shouldUseAndReturnTargetParameterForArrayMappingAndSmallerArray() { TargetFoo[] target = new TargetFoo[1]; TargetFoo[] result = @@ -144,7 +141,7 @@ public void shouldUseAndReturnTargetParameterForArrayMappingAndSmallerArray() { assertThat( target ).containsOnly( new TargetFoo( "Bob" ) ); } - @Test + @ProcessorTest public void shouldUseAndReturnTargetParameterForMapping() { List target = new ArrayList<>(); Iterable result = @@ -155,7 +152,7 @@ public void shouldUseAndReturnTargetParameterForMapping() { assertResultList( target ); } - @Test + @ProcessorTest public void shouldUseDefaultImplementationForListWithoutSetter() { Source source = new Source(); source.setFooStream( createSourceFooStream() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/NoSetterStreamMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/NoSetterStreamMappingTest.java index 813b9d87e1..f82a522bf5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/NoSetterStreamMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/NoSetterStreamMappingTest.java @@ -5,16 +5,14 @@ */ package org.mapstruct.ap.test.java8stream.defaultimplementation; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.List; import java.util.stream.Stream; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisfaov @@ -22,10 +20,9 @@ */ @WithClasses({ NoSetterMapper.class, NoSetterSource.class, NoSetterTarget.class }) @IssueKey("962") -@RunWith(AnnotationProcessorTestRunner.class) public class NoSetterStreamMappingTest { - @Test + @ProcessorTest public void compilesAndMapsCorrectly() { NoSetterSource source = new NoSetterSource(); source.setListValues( Stream.of( "foo", "bar" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java index e177b8a9a3..f3e540b7ca 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java @@ -7,16 +7,14 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.NoProperties; import org.mapstruct.ap.test.WithProperties; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Test for illegal mappings between collection/stream types, iterable and non-iterable types etc. @@ -31,10 +29,9 @@ * @author Filip Hrisafov */ @IssueKey("962") -@RunWith(AnnotationProcessorTestRunner.class) public class ErroneousStreamMappingTest { - @Test + @ProcessorTest @WithClasses({ ErroneousStreamToNonStreamMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -52,7 +49,7 @@ public class ErroneousStreamMappingTest { public void shouldFailToGenerateImplementationBetweenStreamAndNonStreamOrIterable() { } - @Test + @ProcessorTest @WithClasses({ ErroneousStreamToPrimitivePropertyMapper.class, Source.class, Target.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -67,7 +64,7 @@ public void shouldFailToGenerateImplementationBetweenStreamAndNonStreamOrIterabl public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { } - @Test + @ProcessorTest @WithClasses({ EmptyStreamMappingMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -92,7 +89,7 @@ public void shouldFailToGenerateImplementationBetweenCollectionAndPrimitive() { public void shouldFailOnEmptyIterableAnnotationStreamMappings() { } - @Test + @ProcessorTest @WithClasses({ ErroneousStreamToStreamNoElementMappingFound.class, NoProperties.class, WithProperties.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -108,7 +105,7 @@ public void shouldFailOnEmptyIterableAnnotationStreamMappings() { public void shouldFailOnNoElementMappingFoundForStreamToStream() { } - @Test + @ProcessorTest @IssueKey("993") @WithClasses({ ErroneousStreamToStreamNoElementMappingFoundDisabledAuto.class }) @ExpectedCompilationOutcome( @@ -124,7 +121,7 @@ public void shouldFailOnNoElementMappingFoundForStreamToStream() { public void shouldFailOnNoElementMappingFoundForStreamToStreamWithDisabledAuto() { } - @Test + @ProcessorTest @WithClasses({ ErroneousListToStreamNoElementMappingFound.class, NoProperties.class, WithProperties.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -140,7 +137,7 @@ public void shouldFailOnNoElementMappingFoundForStreamToStreamWithDisabledAuto() public void shouldFailOnNoElementMappingFoundForListToStream() { } - @Test + @ProcessorTest @IssueKey("993") @WithClasses({ ErroneousListToStreamNoElementMappingFoundDisabledAuto.class }) @ExpectedCompilationOutcome( @@ -156,7 +153,7 @@ public void shouldFailOnNoElementMappingFoundForListToStream() { public void shouldFailOnNoElementMappingFoundForListToStreamWithDisabledAuto() { } - @Test + @ProcessorTest @WithClasses({ ErroneousStreamToListNoElementMappingFound.class, NoProperties.class, WithProperties.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -172,7 +169,7 @@ public void shouldFailOnNoElementMappingFoundForListToStreamWithDisabledAuto() { public void shouldFailOnNoElementMappingFoundForStreamToList() { } - @Test + @ProcessorTest @IssueKey("993") @WithClasses({ ErroneousStreamToListNoElementMappingFoundDisabledAuto.class }) @ExpectedCompilationOutcome( diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java index bcd35e5b5e..2ad882891f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java @@ -5,35 +5,32 @@ */ package org.mapstruct.ap.test.java8stream.forged; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test for mappings between collection and stream types, * * @author Filip Hrisafov */ @IssueKey("962") -@RunWith(AnnotationProcessorTestRunner.class) public class ForgedStreamMappingTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses({ StreamMapper.class, Source.class, Target.class }) public void shouldForgeNewIterableMappingMethod() { @@ -56,7 +53,7 @@ public void shouldForgeNewIterableMappingMethod() { .doesNotContain( "Stream.empty()" ); } - @Test + @ProcessorTest @WithClasses({ ErroneousStreamNonMappableStreamMapper.class, ErroneousNonMappableStreamSource.class, @@ -78,7 +75,7 @@ public void shouldForgeNewIterableMappingMethod() { public void shouldGenerateNonMappableMethodForSetMapping() { } - @Test + @ProcessorTest @WithClasses({ StreamMapper.class, Source.class, Target.class }) public void shouldForgeNewIterableMappingMethodReturnNullOnNullSource() { @@ -96,7 +93,7 @@ public void shouldForgeNewIterableMappingMethodReturnNullOnNullSource() { assertThat( source2.getFooStream3() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ StreamMapperNullValueMappingReturnDefault.class, Source.class, Target.class }) public void shouldForgeNewIterableMappingMethodReturnEmptyOnNullSource() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/streamtononiterable/StreamToNonIterableMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/streamtononiterable/StreamToNonIterableMappingTest.java index 6472ce6db3..26c8e88324 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/streamtononiterable/StreamToNonIterableMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/streamtononiterable/StreamToNonIterableMappingTest.java @@ -5,22 +5,19 @@ */ package org.mapstruct.ap.test.java8stream.streamtononiterable; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.stream.Stream; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; @WithClasses({ Source.class, Target.class, SourceTargetMapper.class, StringListMapper.class }) @IssueKey("962") -@RunWith(AnnotationProcessorTestRunner.class) public class StreamToNonIterableMappingTest { - @Test + @ProcessorTest public void shouldMapStringStreamToStringUsingCustomMapper() { Source source = new Source(); source.setNames( Stream.of( "Alice", "Bob", "Jim" ) ); @@ -30,7 +27,7 @@ public void shouldMapStringStreamToStringUsingCustomMapper() { assertThat( target.getNames() ).isEqualTo( "Alice-Bob-Jim" ); } - @Test + @ProcessorTest public void shouldReverseMapStringStreamToStringUsingCustomMapper() { Target target = new Target(); target.setNames( "Alice-Bob-Jim" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/WildCardTest.java index acf89c5b93..a039951495 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/WildCardTest.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.java8stream.wildcard; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ @IssueKey("962") -@RunWith(AnnotationProcessorTestRunner.class) public class WildCardTest { - @Test + @ProcessorTest @WithClasses({ ExtendsBoundSourceTargetMapper.class, ExtendsBoundSource.class, @@ -43,7 +40,7 @@ public void shouldGenerateExtendsBoundSourceForgedStreamMethod() { } - @Test + @ProcessorTest @WithClasses({ SourceSuperBoundTargetMapper.class, Source.class, @@ -63,7 +60,7 @@ public void shouldGenerateSuperBoundTargetForgedIterableMethod() { } - @Test + @ProcessorTest @WithClasses({ ErroneousIterableSuperBoundSourceMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -77,7 +74,7 @@ public void shouldGenerateSuperBoundTargetForgedIterableMethod() { public void shouldFailOnSuperBoundSource() { } - @Test + @ProcessorTest @WithClasses({ ErroneousIterableExtendsBoundTargetMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -91,7 +88,7 @@ public void shouldFailOnSuperBoundSource() { public void shouldFailOnExtendsBoundTarget() { } - @Test + @ProcessorTest @WithClasses({ ErroneousIterableTypeVarBoundMapperOnMethod.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -105,7 +102,7 @@ public void shouldFailOnExtendsBoundTarget() { public void shouldFailOnTypeVarSource() { } - @Test + @ProcessorTest @WithClasses({ ErroneousIterableTypeVarBoundMapperOnMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/mapperconfig/ConfigTest.java b/processor/src/test/java/org/mapstruct/ap/test/mapperconfig/ConfigTest.java index d128e6f77e..1f7fe423f5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mapperconfig/ConfigTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mapperconfig/ConfigTest.java @@ -5,16 +5,14 @@ */ package org.mapstruct.ap.test.mapperconfig; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -31,10 +29,9 @@ CustomMapperViaMapperConfig.class, SourceTargetMapper.class } ) -@RunWith(AnnotationProcessorTestRunner.class) public class ConfigTest { - @Test + @ProcessorTest @WithClasses( { Target.class, SourceTargetMapper.class } ) public void shouldUseCustomMapperViaMapperForFooToEntity() { @@ -43,7 +40,7 @@ public void shouldUseCustomMapperViaMapperForFooToEntity() { assertThat( target.getFoo().getCreatedBy() ).isEqualTo( CustomMapperViaMapper.class.getSimpleName() ); } - @Test + @ProcessorTest @WithClasses( { Target.class, SourceTargetMapper.class } ) public void shouldUseCustomMapperViaMapperConfigForFooToDto() { @@ -52,7 +49,7 @@ public void shouldUseCustomMapperViaMapperConfigForFooToDto() { assertThat( source.getFoo().getCreatedBy() ).isEqualTo( CustomMapperViaMapperConfig.class.getSimpleName() ); } - @Test + @ProcessorTest @WithClasses( { TargetNoFoo.class, SourceTargetMapperWarn.class } ) @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { @@ -63,7 +60,7 @@ public void shouldUseCustomMapperViaMapperConfigForFooToDto() { public void shouldUseWARNViaMapper() { } - @Test + @ProcessorTest @WithClasses( { TargetNoFoo.class, SourceTargetMapperErroneous.class } ) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/CompositionTest.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/CompositionTest.java index 6850c6f4ea..0a5a361313 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/CompositionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcomposition/CompositionTest.java @@ -7,11 +7,9 @@ import java.util.Date; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -28,10 +26,9 @@ StorageMapper.class, ToEntity.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class CompositionTest { - @Test + @ProcessorTest public void shouldCompose() { Date now = new Date(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java index 5324f3f2cd..d542801799 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java @@ -10,14 +10,12 @@ import java.util.HashMap; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; @@ -37,13 +35,12 @@ UseDirect.class, UseComplex.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class MappingControlTest { /** * Baseline Test, normal, direct allowed */ - @Test + @ProcessorTest @WithClasses(DirectMapper.class) public void directSelectionAllowed() { @@ -58,7 +55,7 @@ public void directSelectionAllowed() { /** * Test the deep cloning annotation */ - @Test + @ProcessorTest @WithClasses(CloningMapper.class) public void testDeepCloning() { @@ -75,7 +72,7 @@ public void testDeepCloning() { /** * Test the deep cloning annotation with lists */ - @Test + @ProcessorTest @WithClasses(CloningListMapper.class) public void testDeepCloningListsAndMaps() { @@ -121,7 +118,7 @@ public void testDeepCloningListsAndMaps() { * MapStruct gets too creative when we allow complex (2 step mappings) to convert if we also allow * it to forge methods (which is contradiction with the fact that we do not allow methods on this mapper) */ - @Test + @ProcessorTest @WithClasses(ErroneousDirectMapper.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -138,7 +135,7 @@ public void directSelectionNotAllowed() { /** * Baseline Test, normal, method allowed */ - @Test + @ProcessorTest @WithClasses(MethodMapper.class) public void methodSelectionAllowed() { Fridge fridge = MethodMapper.INSTANCE.map( createFridgeDTO() ); @@ -148,7 +145,7 @@ public void methodSelectionAllowed() { assertThat( fridge.getBeerCount() ).isEqualTo( 5 ); } - @Test + @ProcessorTest @WithClasses(ErroneousMethodMapper.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -165,7 +162,7 @@ public void methodSelectionNotAllowed() { /** * Baseline Test, normal, conversion allowed */ - @Test + @ProcessorTest @WithClasses(ConversionMapper.class) public void conversionSelectionAllowed() { Fridge fridge = ConversionMapper.INSTANCE.map( createFridgeDTO().getShelve().getCoolBeer() ); @@ -174,7 +171,7 @@ public void conversionSelectionAllowed() { assertThat( fridge.getBeerCount() ).isEqualTo( 5 ); } - @Test + @ProcessorTest @WithClasses(ErroneousConversionMapper.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -191,7 +188,7 @@ public void conversionSelectionNotAllowed() { /** * Baseline Test, normal, complex mapping allowed */ - @Test + @ProcessorTest @WithClasses(ComplexMapper.class) public void complexSelectionAllowed() { Fridge fridge = ComplexMapper.INSTANCE.map( createFridgeDTO() ); @@ -200,7 +197,7 @@ public void complexSelectionAllowed() { assertThat( fridge.getBeerCount() ).isEqualTo( 5 ); } - @Test + @ProcessorTest @WithClasses(ErroneousComplexMapper.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -214,7 +211,7 @@ public void complexSelectionAllowed() { public void complexSelectionNotAllowed() { } - @Test + @ProcessorTest @WithClasses({ Config.class, ErroneousComplexMapperWithConfig.class }) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java index f9ad0b435b..079b2f3020 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/namesuggestion/SuggestMostSimilarNameTest.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.namesuggestion; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.namesuggestion.erroneous.PersonAgeMapper; import org.mapstruct.ap.test.namesuggestion.erroneous.PersonGarageWrongSourceMapper; import org.mapstruct.ap.test.namesuggestion.erroneous.PersonGarageWrongTargetMapper; import org.mapstruct.ap.test.namesuggestion.erroneous.PersonNameMapper; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Person.class, PersonDto.class, Garage.class, GarageDto.class, ColorRgb.class, ColorRgbDto.class }) public class SuggestMostSimilarNameTest { - @Test + @ProcessorTest @WithClasses({ PersonAgeMapper.class }) @@ -39,7 +36,7 @@ public class SuggestMostSimilarNameTest { public void testAgeSuggestion() { } - @Test + @ProcessorTest @WithClasses({ PersonNameMapper.class }) @@ -55,7 +52,7 @@ public void testAgeSuggestion() { public void testNameSuggestion() { } - @Test + @ProcessorTest @WithClasses({ PersonGarageWrongTargetMapper.class }) @@ -85,7 +82,7 @@ public void testNameSuggestion() { public void testGarageTargetSuggestion() { } - @Test + @ProcessorTest @WithClasses({ PersonGarageWrongSourceMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/naming/VariableNamingTest.java b/processor/src/test/java/org/mapstruct/ap/test/naming/VariableNamingTest.java index c70f21b2cd..4b5c7982ea 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/naming/VariableNamingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/naming/VariableNamingTest.java @@ -5,9 +5,6 @@ */ package org.mapstruct.ap.test.naming; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - import java.util.Arrays; import java.util.Calendar; import java.util.Date; @@ -15,11 +12,12 @@ import java.util.HashMap; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * Test for naming of variables/members which conflict with keywords or parameter names. @@ -28,10 +26,9 @@ */ @WithClasses({ SourceTargetMapper.class, While.class, Break.class, Source.class }) @IssueKey("53") -@RunWith(AnnotationProcessorTestRunner.class) public class VariableNamingTest { - @Test + @ProcessorTest public void shouldGenerateImplementationsOfMethodsWithProblematicVariableNmes() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/naming/spi/CustomNamingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/naming/spi/CustomNamingStrategyTest.java index d443d952ba..78608dd7ba 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/naming/spi/CustomNamingStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/naming/spi/CustomNamingStrategyTest.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.naming.spi; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.spi.AccessorNamingStrategy; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test do demonstrate the usage of custom implementations of {@link AccessorNamingStrategy}. * * @author Andreas Gudian */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ GolfPlayer.class, GolfPlayerDto.class, GolfPlayerMapper.class }) @WithServiceImplementation(CustomAccessorNamingStrategy.class) public class CustomNamingStrategyTest { - @Test + @ProcessorTest public void shouldApplyCustomNamingStrategy() { GolfPlayer player = new GolfPlayer() .withName( "Jared" ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java index bc9bded0cc..7331a5cc2d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DisablingNestedSimpleBeansMappingTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.nestedbeans; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -22,7 +20,6 @@ Roof.class, RoofDto.class, RoofType.class, ExternalRoofType.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class DisablingNestedSimpleBeansMappingTest { @WithClasses({ @@ -37,7 +34,7 @@ public class DisablingNestedSimpleBeansMappingTest { "Consider to declare/implement a mapping method: \"RoofDto map(Roof value)\"." ) }) - @Test + @ProcessorTest public void shouldUseDisabledMethodGenerationOnMapper() { } @@ -54,7 +51,7 @@ public void shouldUseDisabledMethodGenerationOnMapper() { "Consider to declare/implement a mapping method: \"RoofDto map(Roof value)\"." ) }) - @Test + @ProcessorTest public void shouldUseDisabledMethodGenerationOnMapperConfig() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java index 3117f17fc9..fb10616577 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java @@ -5,8 +5,6 @@ */ package org.mapstruct.ap.test.nestedbeans; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.nestedbeans.unmappable.BaseCollectionElementPropertyMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.BaseDeepListMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.BaseDeepMapKeyMapper; @@ -59,11 +57,11 @@ import org.mapstruct.ap.test.nestedbeans.unmappable.warn.UnmappableWarnDeepMapValueMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.warn.UnmappableWarnDeepNestingMapper; import org.mapstruct.ap.test.nestedbeans.unmappable.warn.UnmappableWarnValuePropertyMapper; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; @WithClasses({ Car.class, CarDto.class, Color.class, ColorDto.class, @@ -81,7 +79,6 @@ BaseDeepNestingMapper.class, BaseValuePropertyMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class DottedErrorMessageTest { private static final String PROPERTY = "property"; @@ -89,7 +86,7 @@ public class DottedErrorMessageTest { private static final String MAP_KEY = "Map key"; private static final String MAP_VALUE = "Map value"; - @Test + @ProcessorTest @WithClasses({ UnmappableDeepNestingMapper.class }) @@ -106,7 +103,7 @@ public class DottedErrorMessageTest { public void testDeepNestedBeans() { } - @Test + @ProcessorTest @WithClasses({ UnmappableDeepListMapper.class }) @@ -123,7 +120,7 @@ public void testDeepNestedBeans() { public void testIterables() { } - @Test + @ProcessorTest @WithClasses({ UnmappableDeepMapKeyMapper.class }) @@ -140,7 +137,7 @@ public void testIterables() { public void testMapKeys() { } - @Test + @ProcessorTest @WithClasses({ UnmappableDeepMapValueMapper.class }) @@ -157,7 +154,7 @@ public void testMapKeys() { public void testMapValues() { } - @Test + @ProcessorTest @WithClasses({ UnmappableCollectionElementPropertyMapper.class }) @@ -174,7 +171,7 @@ public void testMapValues() { public void testCollectionElementProperty() { } - @Test + @ProcessorTest @WithClasses({ UnmappableValuePropertyMapper.class }) @@ -191,7 +188,7 @@ public void testCollectionElementProperty() { public void testMapValueProperty() { } - @Test + @ProcessorTest @WithClasses({ UnmappableEnumMapper.class }) @@ -211,7 +208,7 @@ public void testMapValueProperty() { public void testMapEnumProperty() { } - @Test + @ProcessorTest @WithClasses({ UnmappableWarnDeepNestingMapper.class, UnmappableWarnDeepListMapper.class, @@ -258,7 +255,7 @@ public void testMapEnumProperty() { public void testWarnUnmappedTargetProperties() { } - @Test + @ProcessorTest @WithClasses({ UnmappableIgnoreDeepNestingMapper.class, UnmappableIgnoreDeepListMapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/MultipleForgedMethodsTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/MultipleForgedMethodsTest.java index ecd371d3f6..9231ed46e2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/MultipleForgedMethodsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/MultipleForgedMethodsTest.java @@ -5,9 +5,9 @@ */ package org.mapstruct.ap.test.nestedbeans; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; +import java.util.Arrays; +import java.util.HashMap; + import org.mapstruct.ap.test.nestedbeans.maps.AntonymsDictionary; import org.mapstruct.ap.test.nestedbeans.maps.AntonymsDictionaryDto; import org.mapstruct.ap.test.nestedbeans.maps.AutoMapMapper; @@ -16,22 +16,20 @@ import org.mapstruct.ap.test.nestedbeans.multiplecollections.Garage; import org.mapstruct.ap.test.nestedbeans.multiplecollections.GarageDto; import org.mapstruct.ap.test.nestedbeans.multiplecollections.MultipleListMapper; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -import java.util.Arrays; -import java.util.HashMap; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * This test is for a case when several identical methods could be generated, what is an easy edge case to miss. */ -@RunWith(AnnotationProcessorTestRunner.class) public class MultipleForgedMethodsTest { @WithClasses({ Word.class, WordDto.class, AntonymsDictionaryDto.class, AntonymsDictionary.class, AutoMapMapper.class }) - @Test + @ProcessorTest public void testNestedMapsAutoMap() { HashMap dtoAntonyms = new HashMap<>(); @@ -52,8 +50,10 @@ public void testNestedMapsAutoMap() { AntonymsDictionary mappedAntonymsDictionary = AutoMapMapper.INSTANCE.entityToDto( new AntonymsDictionaryDto( dtoAntonyms ) ); - Assert.assertEquals( "Mapper did not map dto to entity correctly", new AntonymsDictionary( entityAntonyms ), - mappedAntonymsDictionary + assertEquals( + new AntonymsDictionary( entityAntonyms ), + mappedAntonymsDictionary, + "Mapper did not map dto to entity correctly" ); } @@ -61,7 +61,7 @@ public void testNestedMapsAutoMap() { MultipleListMapper.class, Garage.class, GarageDto.class, Car.class, CarDto.class, Wheel.class, WheelDto.class }) - @Test + @ProcessorTest public void testMultipleCollections() { GarageDto dto = new GarageDto( Arrays.asList( new CarDto( @@ -97,7 +97,7 @@ public void testMultipleCollections() { GarageDto mappedDto = MultipleListMapper.INSTANCE.convert( entity ); - Assert.assertEquals( "Mapper did not map entity to dto correctly", dto, mappedDto ); + assertEquals( dto, mappedDto, "Mapper did not map entity to dto correctly" ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/NestedSimpleBeansMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/NestedSimpleBeansMappingTest.java index 84d78cdd1c..b78da21860 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/NestedSimpleBeansMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/NestedSimpleBeansMappingTest.java @@ -9,11 +9,9 @@ import java.util.stream.Collectors; import org.assertj.core.groups.Tuple; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -33,17 +31,16 @@ UserDtoMapperSmart.class, UserDtoUpdateMapperSmart.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class NestedSimpleBeansMappingTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( UserDtoMapperClassic.class, UserDtoMapperSmart.class, UserDtoUpdateMapperSmart.class ); - @Test + @ProcessorTest public void shouldHaveAllFields() { // If this test fails that means something new was added to the structure of the User/UserDto. // Make sure that the other tests are also updated (the new field is asserted) @@ -68,7 +65,7 @@ public void shouldHaveAllFields() { assertThat( RoofDto.class ).hasOnlyDeclaredFields( roofFields ); } - @Test + @ProcessorTest public void shouldMapNestedBeans() { User user = TestData.createUser(); @@ -80,7 +77,7 @@ public void shouldMapNestedBeans() { assertUserDto( smartMapping, user ); } - @Test + @ProcessorTest public void shouldMapUpdateNestedBeans() { User user = TestData.createUser(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/RecursionTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/RecursionTest.java index 90f5553278..e8caa784b1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/RecursionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/RecursionTest.java @@ -9,24 +9,21 @@ import java.util.Iterator; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.nestedbeans.recursive.RecursionMapper; import org.mapstruct.ap.test.nestedbeans.recursive.TreeRecursionMapper; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; -@RunWith(AnnotationProcessorTestRunner.class) public class RecursionTest { @WithClasses({ RecursionMapper.class }) - @Test + @ProcessorTest @IssueKey("1103") public void testRecursiveAutoMap() { RecursionMapper.RootDto rootDto = new RecursionMapper.RootDto( @@ -61,7 +58,7 @@ private void assertChildEquals(RecursionMapper.ChildDto childDto, RecursionMappe @WithClasses({ TreeRecursionMapper.class }) - @Test + @ProcessorTest @IssueKey("1103") public void testRecursiveTreeAutoMap() { TreeRecursionMapper.RootDto rootDto = new TreeRecursionMapper.RootDto( diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exceptions/NestedMappingsWithExceptionTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exceptions/NestedMappingsWithExceptionTest.java index 509ca1e146..2a5f10ad22 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exceptions/NestedMappingsWithExceptionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exceptions/NestedMappingsWithExceptionTest.java @@ -5,15 +5,13 @@ */ package org.mapstruct.ap.test.nestedbeans.exceptions; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.nestedbeans.exceptions._target.DeveloperDto; import org.mapstruct.ap.test.nestedbeans.exceptions._target.ProjectDto; import org.mapstruct.ap.test.nestedbeans.exceptions.source.Developer; import org.mapstruct.ap.test.nestedbeans.exceptions.source.Project; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -28,10 +26,9 @@ EntityFactory.class }) @IssueKey("1304") -@RunWith(AnnotationProcessorTestRunner.class) public class NestedMappingsWithExceptionTest { - @Test + @ProcessorTest public void shouldGenerateCodeThatCompiles() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java index 19abea74b3..ae4a7d945a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/ErroneousJavaInternalTest.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.nestedbeans.exclusions; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -22,7 +20,6 @@ Target.class, ErroneousJavaInternalMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1154") public class ErroneousJavaInternalTest { @@ -51,7 +48,7 @@ public class ErroneousJavaInternalTest { "Consider to declare/implement a mapping method: " + "\"List map(List value)\".") }) - @Test + @ProcessorTest public void shouldNotNestIntoJavaPackageObjects() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java index 45cc9a2343..4fb90cf6ab 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/exclusions/custom/ErroneousCustomExclusionTest.java @@ -5,15 +5,13 @@ */ package org.mapstruct.ap.test.nestedbeans.exclusions.custom; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -24,7 +22,6 @@ ErroneousCustomExclusionMapper.class }) @WithServiceImplementation( CustomMappingExclusionProvider.class ) -@RunWith(AnnotationProcessorTestRunner.class) @IssueKey("1154") public class ErroneousCustomExclusionTest { @@ -37,7 +34,7 @@ public class ErroneousCustomExclusionTest { "Consider to declare/implement a mapping method: \"Target.NestedTarget map(Source.NestedSource value)\".") } ) - @Test + @ProcessorTest public void shouldFailToCreateMappingForExcludedClass() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/mixed/AutomappingAndNestedTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/mixed/AutomappingAndNestedTest.java index 3c381cfb4a..80922ae5c6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/mixed/AutomappingAndNestedTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/mixed/AutomappingAndNestedTest.java @@ -5,11 +5,7 @@ */ package org.mapstruct.ap.test.nestedbeans.mixed; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.nestedbeans.mixed._target.FishDto; import org.mapstruct.ap.test.nestedbeans.mixed._target.FishTankDto; import org.mapstruct.ap.test.nestedbeans.mixed._target.FishTankWithNestedDocumentDto; @@ -30,10 +26,12 @@ import org.mapstruct.ap.test.nestedbeans.mixed.source.WaterQuality; import org.mapstruct.ap.test.nestedbeans.mixed.source.WaterQualityReport; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; + /** * * @author Sjaak Derksen @@ -64,18 +62,17 @@ FishTankMapperWithDocument.class }) @IssueKey("1057") -@RunWith(AnnotationProcessorTestRunner.class) public class AutomappingAndNestedTest { - @Rule - public GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( FishTankMapper.class, FishTankMapperConstant.class, FishTankMapperExpression.class, FishTankMapperWithDocument.class ); - @Test + @ProcessorTest public void shouldAutomapAndHandleSourceAndTargetPropertyNesting() { // -- prepare @@ -117,7 +114,7 @@ public void shouldAutomapAndHandleSourceAndTargetPropertyNesting() { .isEqualTo( source.getQuality().getReport().getOrganisationName() ); } - @Test + @ProcessorTest public void shouldAutomapAndHandleSourceAndTargetPropertyNestingReverse() { // -- prepare @@ -156,7 +153,7 @@ public void shouldAutomapAndHandleSourceAndTargetPropertyNestingReverse() { .isEqualTo( source.getQuality().getReport().getVerdict() ); } - @Test + @ProcessorTest public void shouldAutomapAndHandleSourceAndTargetPropertyNestingAndConstant() { // -- prepare @@ -185,7 +182,7 @@ public void shouldAutomapAndHandleSourceAndTargetPropertyNestingAndConstant() { } - @Test + @ProcessorTest public void shouldAutomapAndHandleSourceAndTargetPropertyNestingAndExpresion() { // -- prepare @@ -210,7 +207,7 @@ public void shouldAutomapAndHandleSourceAndTargetPropertyNestingAndExpresion() { assertThat( target.getQuality().getReport().getOrganisation().getName() ).isEqualTo( "Dunno" ); } - @Test + @ProcessorTest public void shouldAutomapIntermediateLevelAndMapConstant() { // -- prepare diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java index 3738f56282..375f524d68 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java @@ -5,14 +5,11 @@ */ package org.mapstruct.ap.test.nestedmethodcall; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; - import javax.xml.bind.JAXBElement; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConstants; @@ -20,13 +17,13 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for the nested invocation of mapping methods. @@ -34,25 +31,24 @@ * @author Sjaak Derksen */ @IssueKey("134") -@RunWith(AnnotationProcessorTestRunner.class) public class NestedMappingMethodInvocationTest { public static final QName QNAME = new QName( "dont-care" ); private Locale originalLocale; - @Before + @BeforeEach public void setDefaultLocale() { originalLocale = Locale.getDefault(); Locale.setDefault( Locale.GERMAN ); } - @After + @AfterEach public void tearDown() { Locale.setDefault( originalLocale ); } - @Test + @ProcessorTest @WithClasses( { OrderTypeToOrderDtoMapper.class, OrderDto.class, @@ -73,7 +69,7 @@ public void shouldMapViaMethodAndMethod() throws DatatypeConfigurationException assertThat( target.getOrderDetails().getDescription() ).containsExactly( "elem1", "elem2" ); } - @Test + @ProcessorTest @WithClasses( { SourceTypeTargetDtoMapper.class, SourceType.class, @@ -89,7 +85,7 @@ public void shouldMapViaMethodAndConversion() { assertThat( target.getDate() ).isEqualTo( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); } - @Test + @ProcessorTest @WithClasses( { SourceTypeTargetDtoMapper.class, SourceType.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleNestedPropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleNestedPropertiesTest.java index 98faca0cca..ae5833794e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleNestedPropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleNestedPropertiesTest.java @@ -5,38 +5,36 @@ */ package org.mapstruct.ap.test.nestedproperties.simple; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.nestedproperties.simple._target.TargetObject; import org.mapstruct.ap.test.nestedproperties.simple.source.SourceProps; import org.mapstruct.ap.test.nestedproperties.simple.source.SourceRoot; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Sebastian Hasait */ @WithClasses({ SourceRoot.class, SourceProps.class, TargetObject.class }) @IssueKey("407") -@RunWith(AnnotationProcessorTestRunner.class) public class SimpleNestedPropertiesTest { - @Test + @ProcessorTest @WithClasses({ SimpleMapper.class }) public void testNull() { TargetObject targetObject = SimpleMapper.MAPPER.toTargetObject( null ); - assertNull( targetObject ); + assertThat( targetObject ).isNull(); } - @Test + @ProcessorTest @WithClasses({ SimpleMapper.class }) public void testViaNull() { SourceRoot sourceRoot = new SourceRoot(); @@ -57,7 +55,7 @@ public void testViaNull() { assertNull( targetObject.getStringValue() ); } - @Test + @ProcessorTest @WithClasses({ SimpleMapper.class }) public void testFilled() { SourceRoot sourceRoot = new SourceRoot(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsource/exceptions/NestedExceptionTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsource/exceptions/NestedExceptionTest.java index 09f47ccebb..97206784aa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsource/exceptions/NestedExceptionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsource/exceptions/NestedExceptionTest.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.nestedsource.exceptions; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @@ -24,10 +22,9 @@ ResourceMapper.class }) @IssueKey("1332") -@RunWith(AnnotationProcessorTestRunner.class) public class NestedExceptionTest { - @Test + @ProcessorTest public void shouldGenerateCodeThatCompiles() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsource/parameter/NormalizingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsource/parameter/NormalizingTest.java index 696d983dc6..de9a821527 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsource/parameter/NormalizingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsource/parameter/NormalizingTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.nestedsource.parameter; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen */ @WithClasses({ FontDto.class, LetterDto.class, LetterEntity.class, LetterMapper.class }) @IssueKey("836") -@RunWith(AnnotationProcessorTestRunner.class) public class NormalizingTest { - @Test + @ProcessorTest public void shouldGenerateImplementationForPropertyNamesOnly() { FontDto fontIn = new FontDto(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java index 07d0a38611..2a0a80cbda 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/NestedSourcePropertiesTest.java @@ -5,15 +5,9 @@ */ package org.mapstruct.ap.test.nestedsourceproperties; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import java.util.Arrays; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.nestedsourceproperties._target.AdderUsageObserver; import org.mapstruct.ap.test.nestedsourceproperties._target.ChartEntry; import org.mapstruct.ap.test.nestedsourceproperties._target.ChartPositions; @@ -23,25 +17,26 @@ import org.mapstruct.ap.test.nestedsourceproperties.source.Song; import org.mapstruct.ap.test.nestedsourceproperties.source.Studio; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Sjaak Derksen */ @WithClasses({ Song.class, Artist.class, Chart.class, Label.class, Studio.class, ChartEntry.class }) @IssueKey("65") -@RunWith(AnnotationProcessorTestRunner.class) public class NestedSourcePropertiesTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntry.class }) public void shouldGenerateImplementationForPropertyNamesOnly() { generatedSource.addComparisonToFixtureFor( ArtistToChartEntry.class ); @@ -73,7 +68,7 @@ public void shouldGenerateImplementationForPropertyNamesOnly() { assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntry.class }) public void shouldGenerateImplementationForMultipleParam() { @@ -108,7 +103,7 @@ public void shouldGenerateImplementationForMultipleParam() { assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntry.class }) public void shouldPickPropertyNameOverParameterName() { @@ -127,7 +122,7 @@ public void shouldPickPropertyNameOverParameterName() { assertThat( chartEntry.getSongTitle() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryAdder.class, ChartPositions.class, AdderUsageObserver.class }) public void shouldUseAddAsTargetAccessor() { @@ -142,10 +137,10 @@ public void shouldUseAddAsTargetAccessor() { assertThat( positions ).isNotNull(); assertThat( positions.getPositions() ).containsExactly( 3L, 5L ); - assertTrue( AdderUsageObserver.isUsed() ); + assertThat( AdderUsageObserver.isUsed() ).isTrue(); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryGetter.class, ChartPositions.class, AdderUsageObserver.class }) public void shouldUseGetAsTargetAccessor() { @@ -160,10 +155,10 @@ public void shouldUseGetAsTargetAccessor() { assertThat( positions ).isNotNull(); assertThat( positions.getPositions() ).containsExactly( 3L, 5L ); - assertFalse( AdderUsageObserver.isUsed() ); + assertThat( AdderUsageObserver.isUsed() ).isFalse(); } - @Test + @ProcessorTest @IssueKey("838") @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ReversingNestedSourcePropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ReversingNestedSourcePropertiesTest.java index db27970b51..2010a205de 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ReversingNestedSourcePropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ReversingNestedSourcePropertiesTest.java @@ -5,11 +5,6 @@ */ package org.mapstruct.ap.test.nestedsourceproperties; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.nestedsourceproperties._target.BaseChartEntry; import org.mapstruct.ap.test.nestedsourceproperties._target.ChartEntry; import org.mapstruct.ap.test.nestedsourceproperties._target.ChartEntryComposed; @@ -23,18 +18,19 @@ import org.mapstruct.ap.test.nestedsourceproperties.source.SourceDtoFactory; import org.mapstruct.ap.test.nestedsourceproperties.source.Studio; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen */ @IssueKey("389") @WithClasses({ Song.class, Artist.class, Chart.class, Label.class, Studio.class, ChartEntry.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ReversingNestedSourcePropertiesTest { - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryReverse.class }) public void shouldGenerateNestedReverse() { @@ -63,7 +59,7 @@ public void shouldGenerateNestedReverse() { assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryWithIgnoresReverse.class }) public void shouldIgnoreEverytingBelowArtist() { @@ -86,7 +82,7 @@ public void shouldIgnoreEverytingBelowArtist() { assertThat( song2.getArtist() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryUpdateReverse.class }) public void shouldGenerateNestedUpdateReverse() { @@ -116,7 +112,7 @@ public void shouldGenerateNestedUpdateReverse() { assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); } - @Test + @ProcessorTest @WithClasses( { ArtistToChartEntryWithFactoryReverse.class, SourceDtoFactory.class } ) public void shouldGenerateNestedReverseWithFactory() { @@ -151,7 +147,7 @@ public void shouldGenerateNestedReverseWithFactory() { } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryComposedReverse.class, ChartEntryComposed.class, ChartEntryLabel.class }) public void shouldGenerateNestedComposedReverse() { @@ -181,7 +177,7 @@ public void shouldGenerateNestedComposedReverse() { assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryWithMappingReverse.class, ChartEntryWithMapping.class }) public void shouldGenerateNestedWithMappingReverse() { @@ -210,7 +206,7 @@ public void shouldGenerateNestedWithMappingReverse() { assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" ); } - @Test + @ProcessorTest @WithClasses({ ArtistToChartEntryWithConfigReverse.class, ArtistToChartEntryConfig.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedtargetproperties/NestedProductPropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedtargetproperties/NestedProductPropertiesTest.java index 44a73abd4b..b10c51ab39 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedtargetproperties/NestedProductPropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedtargetproperties/NestedProductPropertiesTest.java @@ -5,9 +5,7 @@ */ package org.mapstruct.ap.test.nestedtargetproperties; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.nestedsourceproperties._target.ChartEntry; import org.mapstruct.ap.test.nestedsourceproperties.source.Artist; import org.mapstruct.ap.test.nestedsourceproperties.source.Chart; @@ -15,8 +13,8 @@ import org.mapstruct.ap.test.nestedsourceproperties.source.Song; import org.mapstruct.ap.test.nestedsourceproperties.source.Studio; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -36,16 +34,15 @@ ChartEntryToArtistUpdate.class } ) @IssueKey("389") -@RunWith(AnnotationProcessorTestRunner.class) public class NestedProductPropertiesTest { - @Rule - public GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( ChartEntryToArtist.class, ChartEntryToArtistUpdate.class ); - @Test + @ProcessorTest public void shouldMapNestedTarget() { ChartEntry chartEntry = new ChartEntry(); @@ -74,7 +71,7 @@ public void shouldMapNestedTarget() { } - @Test + @ProcessorTest public void shouldMapNestedComposedTarget() { ChartEntry chartEntry1 = new ChartEntry(); @@ -105,7 +102,7 @@ public void shouldMapNestedComposedTarget() { } - @Test + @ProcessorTest public void shouldReverseNestedTarget() { ChartEntry chartEntry = new ChartEntry(); @@ -128,7 +125,7 @@ public void shouldReverseNestedTarget() { assertThat( result.getSongTitle() ).isEqualTo( "Purple Rain" ); } - @Test + @ProcessorTest public void shouldMapNestedTargetWitUpdate() { ChartEntry chartEntry = new ChartEntry(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nonvoidsetter/NonVoidSettersTest.java b/processor/src/test/java/org/mapstruct/ap/test/nonvoidsetter/NonVoidSettersTest.java index 6745f52c02..883464ebfe 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nonvoidsetter/NonVoidSettersTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nonvoidsetter/NonVoidSettersTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.nonvoidsetter; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for using non-void setters (fluent style) in the target. @@ -19,10 +17,9 @@ * @author Gunnar Morling */ @WithClasses({ Actor.class, ActorDto.class, ActorMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class NonVoidSettersTest { - @Test + @ProcessorTest @IssueKey("353") public void shouldMapAttributeWithoutSetterInSourceType() { ActorDto target = ActorMapper.INSTANCE.actorToActorDto( new Actor( 3, "Hickory Black" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullcheck/NullCheckTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullcheck/NullCheckTest.java index 290a34f733..816db7a1dc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullcheck/NullCheckTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullcheck/NullCheckTest.java @@ -5,13 +5,12 @@ */ package org.mapstruct.ap.test.nullcheck; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Test for correct handling of null checks. @@ -28,10 +27,9 @@ Source.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class NullCheckTest { - @Test(expected = NullPointerException.class) + @ProcessorTest @IssueKey("214") public void shouldThrowNullptrWhenCustomMapperIsInvoked() { @@ -40,10 +38,11 @@ public void shouldThrowNullptrWhenCustomMapperIsInvoked() { source.setSomeInteger( 7 ); source.setSomeLong( 2L ); - SourceTargetMapper.INSTANCE.sourceToTarget( source ); + assertThatThrownBy( () -> SourceTargetMapper.INSTANCE.sourceToTarget( source ) ) + .isInstanceOf( NullPointerException.class ); } - @Test + @ProcessorTest @IssueKey("214") public void shouldSurroundTypeConversionWithNullCheck() { @@ -58,7 +57,7 @@ public void shouldSurroundTypeConversionWithNullCheck() { } - @Test + @ProcessorTest @IssueKey("214") public void shouldSurroundArrayListConstructionWithNullCheck() { @@ -72,7 +71,7 @@ public void shouldSurroundArrayListConstructionWithNullCheck() { assertThat( target.getSomeList() ).isNull(); } - @Test + @ProcessorTest @IssueKey("237") public void shouldSurroundConversionPassedToMappingMethodWithNullCheck() { @@ -86,7 +85,7 @@ public void shouldSurroundConversionPassedToMappingMethodWithNullCheck() { assertThat( target.getSomeInteger() ).isNull(); } - @Test + @ProcessorTest @IssueKey("231") public void shouldSurroundConversionFromWrappedPassedToMappingMethodWithPrimitiveArgWithNullCheck() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullcheck/strategy/NullValueCheckTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullcheck/strategy/NullValueCheckTest.java index 957b3e7b1a..256d3f7d92 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullcheck/strategy/NullValueCheckTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullcheck/strategy/NullValueCheckTest.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.nullcheck.strategy; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -21,10 +19,9 @@ HouseMapperConfig.class, HouseMapperWithConfig.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class NullValueCheckTest { - @Test + @ProcessorTest public void testDefinedOnMapper() { HouseEntity entity = HouseMapper.INSTANCE.mapWithNvcsOnMapper( new HouseDto() ); @@ -35,7 +32,7 @@ public void testDefinedOnMapper() { } - @Test + @ProcessorTest public void testDefinedOnBean() { HouseEntity entity = HouseMapper.INSTANCE.mapWithNvcsOnBean( new HouseDto() ); @@ -46,7 +43,7 @@ public void testDefinedOnBean() { } - @Test + @ProcessorTest public void testDefinedOnMapping() { HouseEntity entity = HouseMapper.INSTANCE.mapWithNvcsOnMapping( new HouseDto() ); @@ -57,7 +54,7 @@ public void testDefinedOnMapping() { } - @Test + @ProcessorTest public void testDefinedOnConfig() { HouseEntity entity = HouseMapperWithConfig.INSTANCE.mapWithNvcsOnMapper( new HouseDto() ); @@ -68,7 +65,7 @@ public void testDefinedOnConfig() { } - @Test + @ProcessorTest public void testDefinedOnConfigAndBean() { HouseEntity entity = HouseMapperWithConfig.INSTANCE.mapWithNvcsOnBean( new HouseDto() ); @@ -79,7 +76,7 @@ public void testDefinedOnConfigAndBean() { } - @Test + @ProcessorTest public void testDefinedOnConfigAndMapping() { HouseEntity entity = HouseMapperWithConfig.INSTANCE.mapWithNvcsOnMapping( new HouseDto() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java index 5c6caf6b1f..4bd9324a9a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java @@ -5,22 +5,20 @@ */ package org.mapstruct.ap.test.nullvaluemapping; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; import org.mapstruct.ap.test.nullvaluemapping._target.DriverAndCarDto; import org.mapstruct.ap.test.nullvaluemapping.source.Car; import org.mapstruct.ap.test.nullvaluemapping.source.Driver; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for the strategies for mapping {@code null} values, given via {@code NullValueMapping} etc. @@ -38,15 +36,14 @@ CentralConfig.class, CarMapperSettingOnConfig.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class NullValueMappingTest { - @Test + @ProcessorTest public void shouldProvideMapperInstance() { assertThat( CarMapper.INSTANCE ).isNotNull(); } - @Test + @ProcessorTest public void shouldMapExpressionAndConstantRegardlessNullArg() { //given Car car = new Car( "Morris", 2 ); @@ -72,7 +69,7 @@ public void shouldMapExpressionAndConstantRegardlessNullArg() { assertThat( carDto2.getCatalogId() ).isNotEmpty(); } - @Test + @ProcessorTest public void shouldMapExpressionAndConstantRegardlessNullArgSeveralSources() { //given Car car = new Car( "Morris", 2 ); @@ -98,7 +95,7 @@ public void shouldMapExpressionAndConstantRegardlessNullArgSeveralSources() { assertThat( carDto2.getCatalogId() ).isNotEmpty(); } - @Test + @ProcessorTest public void shouldMapIterableWithNullArg() { //given @@ -119,7 +116,7 @@ public void shouldMapIterableWithNullArg() { assertThat( carDtos2.isEmpty() ).isTrue(); } - @Test + @ProcessorTest @SuppressWarnings({ "rawtypes", "unchecked" }) public void shouldMapMapWithNullArg() { @@ -143,7 +140,7 @@ public void shouldMapMapWithNullArg() { assertThat( carDtoMap2.isEmpty() ).isTrue(); } - @Test + @ProcessorTest public void shouldMapExpressionAndConstantRegardlessNullArgOnMapper() { //when @@ -157,7 +154,7 @@ public void shouldMapExpressionAndConstantRegardlessNullArgOnMapper() { assertThat( carDto.getCatalogId() ).isNotEmpty(); } - @Test + @ProcessorTest public void shouldMapIterableWithNullArgOnMapper() { //when @@ -168,7 +165,7 @@ public void shouldMapIterableWithNullArgOnMapper() { assertThat( carDtos.isEmpty() ).isTrue(); } - @Test + @ProcessorTest public void shouldMapMapWithNullArgOnMapper() { //when @@ -178,7 +175,7 @@ public void shouldMapMapWithNullArgOnMapper() { assertThat( carDtoMap ).isNull(); } - @Test + @ProcessorTest public void shouldMapExpressionAndConstantRegardlessNullArgOnConfig() { //when @@ -192,7 +189,7 @@ public void shouldMapExpressionAndConstantRegardlessNullArgOnConfig() { assertThat( carDto.getCatalogId() ).isNotEmpty(); } - @Test + @ProcessorTest public void shouldMapIterableWithNullArgOnConfig() { //when @@ -203,7 +200,7 @@ public void shouldMapIterableWithNullArgOnConfig() { assertThat( carDtos.isEmpty() ).isTrue(); } - @Test + @ProcessorTest public void shouldMapMapWithNullArgOnConfig() { //when @@ -213,7 +210,7 @@ public void shouldMapMapWithNullArgOnConfig() { assertThat( carDtoMap ).isNull(); } - @Test + @ProcessorTest public void shouldApplyConfiguredStrategyForMethodWithSeveralSourceParams() { //when DriverAndCarDto result = CarMapper.INSTANCE.driverAndCarToDto( null, null ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java index 014c2c8220..dfe2e14d6f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java @@ -8,14 +8,12 @@ import java.util.Arrays; import java.util.function.BiConsumer; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -23,7 +21,6 @@ * @author Sjaak Derksen */ @IssueKey("1306") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Address.class, Customer.class, @@ -34,7 +31,7 @@ }) public class NullValuePropertyMappingTest { - @Test + @ProcessorTest @WithClasses(CustomerMapper.class) public void testStrategyAppliedOnForgedMethod() { @@ -56,31 +53,31 @@ public void testStrategyAppliedOnForgedMethod() { assertThat( userDTO.getDetails() ).containsExactly( "green hair" ); } - @Test + @ProcessorTest @WithClasses({ NvpmsConfig.class, CustomerNvpmsOnConfigMapper.class }) public void testHierarchyIgnoreOnConfig() { testConfig( CustomerNvpmsOnConfigMapper.INSTANCE::map ); } - @Test + @ProcessorTest @WithClasses(CustomerNvpmsOnMapperMapper.class) public void testHierarchyIgnoreOnMapping() { testConfig( CustomerNvpmsOnMapperMapper.INSTANCE::map ); } - @Test + @ProcessorTest @WithClasses(CustomerNvpmsOnBeanMappingMethodMapper.class) public void testHierarchyIgnoreOnBeanMappingMethod() { testConfig( CustomerNvpmsOnBeanMappingMethodMapper.INSTANCE::map ); } - @Test + @ProcessorTest @WithClasses(CustomerNvpmsPropertyMappingMapper.class) public void testHierarchyIgnoreOnPropertyMappingMethod() { testConfig( CustomerNvpmsPropertyMappingMapper.INSTANCE::map ); } - @Test + @ProcessorTest @WithClasses(CustomerDefaultMapper.class) public void testStrategyDefaultAppliedOnForgedMethod() { @@ -102,7 +99,7 @@ public void testStrategyDefaultAppliedOnForgedMethod() { assertThat( userDTO.getDetails() ).isEmpty(); } - @Test + @ProcessorTest @WithClasses(ErroneousCustomerMapper1.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -118,7 +115,7 @@ public void testStrategyDefaultAppliedOnForgedMethod() { public void testBothDefaultValueAndNvpmsDefined() { } - @Test + @ProcessorTest @WithClasses(ErroneousCustomerMapper2.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -134,7 +131,7 @@ public void testBothDefaultValueAndNvpmsDefined() { public void testBothExpressionAndNvpmsDefined() { } - @Test + @ProcessorTest @WithClasses(ErroneousCustomerMapper3.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -150,7 +147,7 @@ public void testBothExpressionAndNvpmsDefined() { public void testBothDefaultExpressionAndNvpmsDefined() { } - @Test + @ProcessorTest @WithClasses(ErroneousCustomerMapper4.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -166,7 +163,7 @@ public void testBothDefaultExpressionAndNvpmsDefined() { public void testBothConstantAndNvpmsDefined() { } - @Test + @ProcessorTest @WithClasses(ErroneousCustomerMapper5.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/oneway/OnewayTest.java b/processor/src/test/java/org/mapstruct/ap/test/oneway/OnewayTest.java index 7794b406d8..f69497f3a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/oneway/OnewayTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/oneway/OnewayTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.oneway; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for propagation of attribute without setter in source and getter in @@ -20,10 +18,9 @@ * @author Gunnar Morling */ @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class OnewayTest { - @Test + @ProcessorTest @IssueKey("17") public void shouldMapAttributeWithoutSetterInSourceType() { Source source = new Source(); @@ -34,7 +31,7 @@ public void shouldMapAttributeWithoutSetterInSourceType() { assertThat( target.retrieveFoo() ).isEqualTo( Long.valueOf( 42 ) ); } - @Test + @ProcessorTest @IssueKey("41") public void shouldReverseMapAttributeWithoutSetterInTargetType() { Target target = new Target(); @@ -45,7 +42,7 @@ public void shouldReverseMapAttributeWithoutSetterInTargetType() { assertThat( source.retrieveBar() ).isEqualTo( 23 ); } - @Test + @ProcessorTest @IssueKey("104") public void shouldMapMappedAttributeWithoutSetterInSourceType() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedMapperTest.java index ee34f53046..f8ea91034e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedMapperTest.java @@ -5,19 +5,17 @@ */ package org.mapstruct.ap.test.references; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * @author Andreas Gudian @@ -28,9 +26,8 @@ SourceTargetMapper.class, Target.class, BaseType.class, SomeType.class, SomeOtherType.class, GenericWrapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class ReferencedMapperTest { - @Test + @ProcessorTest public void referencedMappersAreInstatiatedCorrectly() { Target target = SourceTargetMapper.INSTANCE.sourceToTarget( createSource() ); @@ -55,7 +52,7 @@ private Source createSource() { return source; } - @Test + @ProcessorTest @IssueKey( "136" ) public void shouldUseGenericFactoryForIterable() { List result = SourceTargetMapper.INSTANCE.fromStringList( Arrays.asList( "foo1", "foo2" ) ); @@ -63,7 +60,7 @@ public void shouldUseGenericFactoryForIterable() { assertThat( result ).extracting( "value" ).containsExactly( "foo1", "foo2" ); } - @Test + @ProcessorTest @IssueKey( "136" ) public void shouldUseGenericFactoryForMap() { Map source = new HashMap<>(); @@ -77,7 +74,7 @@ public void shouldUseGenericFactoryForMap() { entry( new SomeType( "foo2" ), new SomeOtherType( "bar2" ) ) ); } - @Test + @ProcessorTest @IssueKey( "136" ) @WithClasses({ SourceTargetMapperWithPrimitives.class, SourceWithWrappers.class, TargetWithPrimitives.class }) public void shouldMapPrimitivesWithCustomMapper() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/samename/SeveralReferencedMappersWithSameSimpleNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/references/samename/SeveralReferencedMappersWithSameSimpleNameTest.java index 3623be1937..68ae703769 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/references/samename/SeveralReferencedMappersWithSameSimpleNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/references/samename/SeveralReferencedMappersWithSameSimpleNameTest.java @@ -5,17 +5,15 @@ */ package org.mapstruct.ap.test.references.samename; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.references.samename.a.AnotherSourceTargetMapper; import org.mapstruct.ap.test.references.samename.a.CustomMapper; import org.mapstruct.ap.test.references.samename.model.Source; import org.mapstruct.ap.test.references.samename.model.Target; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for referring several mappers with the same simple name. @@ -32,10 +30,9 @@ Jsr330SourceTargetMapper.class, AnotherSourceTargetMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class SeveralReferencedMappersWithSameSimpleNameTest { - @Test + @ProcessorTest public void severalMappersWithSameSimpleNameCanBeReferenced() { Source source = new Source(); source.setFoo( 123 ); @@ -48,7 +45,7 @@ public void severalMappersWithSameSimpleNameCanBeReferenced() { assertThat( target.getBar() ).isEqualTo( "912" ); } - @Test + @ProcessorTest public void mapperInSamePackageAndAnotherMapperWithSameNameInAnotherPackageCanBeReferenced() { Source source = new Source(); source.setFoo( 123 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/statics/StaticsTest.java b/processor/src/test/java/org/mapstruct/ap/test/references/statics/StaticsTest.java index 645f3fc2c6..7ab323c5a5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/references/statics/StaticsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/references/statics/StaticsTest.java @@ -5,34 +5,27 @@ */ package org.mapstruct.ap.test.references.statics; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.references.statics.nonused.NonUsedMapper; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; + /** * * @author Sjaak Derksen */ @IssueKey( "410" ) @WithClasses( { Beer.class, BeerDto.class, Category.class } ) -@RunWith(AnnotationProcessorTestRunner.class) public class StaticsTest { - private final GeneratedSource generatedSource = new GeneratedSource(); - - @Rule - public GeneratedSource getGeneratedSource() { - return generatedSource; - } + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses( { BeerMapper.class, CustomMapper.class } ) public void shouldUseStaticMethod() { @@ -44,7 +37,7 @@ public void shouldUseStaticMethod() { assertThat( result.getCategory() ).isEqualTo( Category.STRONG ); // why settle for less? } - @Test + @ProcessorTest @WithClasses( { BeerMapperWithNonUsedMapper.class, NonUsedMapper.class } ) public void shouldNotImportNonUsed() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java b/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java index 2e1598f8aa..32654e3c7e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/reverse/InheritInverseConfigurationTest.java @@ -7,18 +7,16 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.reverse.erroneous.SourceTargetMapperAmbiguous1; import org.mapstruct.ap.test.reverse.erroneous.SourceTargetMapperAmbiguous2; import org.mapstruct.ap.test.reverse.erroneous.SourceTargetMapperAmbiguous3; import org.mapstruct.ap.test.reverse.erroneous.SourceTargetMapperNonMatchingName; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -27,10 +25,9 @@ */ @IssueKey("252") @WithClasses({ Source.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class InheritInverseConfigurationTest { - @Test + @ProcessorTest @WithClasses({ SourceTargetMapper.class }) public void shouldInheritInverseConfigurationMultipleCandidates() { @@ -53,7 +50,7 @@ public void shouldInheritInverseConfigurationMultipleCandidates() { assertThat( source.getPropertyToIgnoreDownstream() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperAmbiguous1.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -72,7 +69,7 @@ public void shouldInheritInverseConfigurationMultipleCandidates() { public void shouldRaiseAmbiguousReverseMethodError() { } - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperAmbiguous2.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -91,7 +88,7 @@ public void shouldRaiseAmbiguousReverseMethodError() { public void shouldRaiseAmbiguousReverseMethodErrorWrongName() { } - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperAmbiguous3.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -112,7 +109,7 @@ public void shouldRaiseAmbiguousReverseMethodErrorWrongName() { public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() { } - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperNonMatchingName.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java index 2e86c96055..c8e7e00711 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/ConversionTest.java @@ -9,15 +9,13 @@ import java.math.BigDecimal; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.NoProperties; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * Tests for the invocation of generic methods for mapping bean properties. @@ -30,10 +28,9 @@ TypeA.class, TypeB.class, TypeC.class }) @IssueKey(value = "79") -@RunWith(AnnotationProcessorTestRunner.class) public class ConversionTest { - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) public void shouldApplyGenericTypeMapper() { @@ -71,7 +68,7 @@ public void shouldApplyGenericTypeMapper() { assertThat( target.getFooWildCardSuperTypeBCorrect() ).isEqualTo( typeB ); } - @Test + @ProcessorTest @WithClasses({ ErroneousSource1.class, ErroneousTarget1.class, ErroneousSourceTargetMapper1.class }) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -84,7 +81,7 @@ public void shouldApplyGenericTypeMapper() { public void shouldFailOnUpperBound() { } - @Test + @ProcessorTest @WithClasses({ ErroneousSource2.class, ErroneousTarget2.class, ErroneousSourceTargetMapper2.class }) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -100,7 +97,7 @@ public void shouldFailOnUpperBound() { public void shouldFailOnWildCardBound() { } - @Test + @ProcessorTest @WithClasses({ ErroneousSource3.class, ErroneousTarget3.class, ErroneousSourceTargetMapper3.class }) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -116,7 +113,7 @@ public void shouldFailOnWildCardBound() { public void shouldFailOnWildCardMultipleBounds() { } - @Test + @ProcessorTest @WithClasses({ ErroneousSource4.class, ErroneousTarget4.class, ErroneousSourceTargetMapper4.class }) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { @@ -132,7 +129,7 @@ public void shouldFailOnWildCardMultipleBounds() { public void shouldFailOnSuperBounds1() { } - @Test + @ProcessorTest @WithClasses({ ErroneousSource6.class, ErroneousTarget6.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/JaxbFactoryMethodSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/JaxbFactoryMethodSelectionTest.java index 02eabb31b1..02705a097a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/JaxbFactoryMethodSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/JaxbFactoryMethodSelectionTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.selection.jaxb; -import static org.assertj.core.api.Assertions.assertThat; - import javax.xml.bind.annotation.XmlElementDecl; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.selection.jaxb.test1.OrderType; import org.mapstruct.ap.test.selection.jaxb.test2.ObjectFactory; import org.mapstruct.ap.test.selection.jaxb.test2.OrderShippingDetailsType; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for the selection of JAXB mapping and factory methods based on the "name" and "scope" attributes @@ -30,10 +28,9 @@ OrderDto.class, OrderShippingDetailsDto.class, OrderType.class, OrderShippingDetailsType.class, OrderMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class JaxbFactoryMethodSelectionTest { - @Test + @ProcessorTest public void shouldMatchOnNameAndOrScope() { OrderType target = OrderMapper.INSTANCE.targetToSource( createSource() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/UnderscoreSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/UnderscoreSelectionTest.java index 3dce6c35fd..f94dd7c94c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/UnderscoreSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/UnderscoreSelectionTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.selection.jaxb; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.selection.jaxb.underscores.ObjectFactory; import org.mapstruct.ap.test.selection.jaxb.underscores.SubType; import org.mapstruct.ap.test.selection.jaxb.underscores.SuperType; import org.mapstruct.ap.test.selection.jaxb.underscores.UnderscoreMapper; import org.mapstruct.ap.test.selection.jaxb.underscores.UnderscoreType; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Ensure factory method selection works for classes generated from schemas using element names with underscores @@ -25,10 +23,9 @@ */ @IssueKey( "726" ) @WithClasses( { UnderscoreType.class, ObjectFactory.class, SuperType.class, SubType.class, UnderscoreMapper.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class UnderscoreSelectionTest { - @Test + @ProcessorTest public void selectingUnderscorePropertiesWorks() { SubType target = UnderscoreMapper.INSTANCE.map( createSource() ); assertThat( target.getInheritedUnderscore().getValue() ).isEqualTo( "hi" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/GenericArrayTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/GenericArrayTest.java index 0b6dbc1137..ce1a14f3b1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/GenericArrayTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/array/GenericArrayTest.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.selection.methodgenerics.array; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; /** * @author Sjaak Derksen * */ -@RunWith(AnnotationProcessorTestRunner.class) public class GenericArrayTest { - @Test + @ProcessorTest @WithClasses( ReturnTypeIsTypeVarArrayMapper.class ) public void testGenericReturnTypeVar() { @@ -33,7 +30,7 @@ public void testGenericReturnTypeVar() { assertThat( target.getProp() ).containsExactly( "test" ); } - @Test + @ProcessorTest @WithClasses( SourceTypeIsTypeVarArrayMapper.class ) public void testGenericSourceTypeVar() { @@ -47,7 +44,7 @@ public void testGenericSourceTypeVar() { } - @Test + @ProcessorTest @WithClasses( BothParameterizedMapper.class ) public void testBothParameterized() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/BoundsTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/BoundsTest.java index c181cfe0b9..2ff9b078a8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/BoundsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/bounds/BoundsTest.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.selection.methodgenerics.bounds; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; /** * @author Sjaak Derksen * */ -@RunWith( AnnotationProcessorTestRunner.class ) public class BoundsTest { - @Test + @ProcessorTest @WithClasses( SourceTypeIsBoundedTypeVarMapper.class ) public void testGenericSourceTypeVar() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java index b11162bb33..b900fe21cc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.selection.methodgenerics.multiple; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - import java.util.Collections; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * @author Sjaak Derksen * */ -@RunWith( AnnotationProcessorTestRunner.class) public class MultipleTypeVarTest { - @Test + @ProcessorTest @WithClasses( ReturnTypeHasMultipleTypeVarOneGenericMapper.class ) public void testGenericSourceTypeVarOneGeneric() { @@ -37,7 +34,7 @@ public void testGenericSourceTypeVarOneGeneric() { assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) ); } - @Test + @ProcessorTest @WithClasses( ReturnTypeHasMultipleTypeVarBothGenericMapper.class ) public void testGenericReturnTypeVarBothGeneric() { @@ -53,7 +50,7 @@ public void testGenericReturnTypeVarBothGeneric() { assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) ); } - @Test + @ProcessorTest @WithClasses( SourceTypeHasMultipleTypeVarBothGenericMapper.class ) public void testGenericSourceTypeVarBothGeneric() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/NestedGenericsTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/NestedGenericsTest.java index c177fb45e8..6073845c61 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/NestedGenericsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/nestedgenerics/NestedGenericsTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.selection.methodgenerics.nestedgenerics; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Collections; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen * */ -@RunWith( AnnotationProcessorTestRunner.class) public class NestedGenericsTest { - @Test + @ProcessorTest @WithClasses( ReturnTypeHasNestedTypeVarMapper.class ) public void testGenericReturnTypeVar() { @@ -33,7 +30,7 @@ public void testGenericReturnTypeVar() { assertThat( target.getProp().get( 0 ) ).contains( "test" ); } - @Test + @ProcessorTest @WithClasses( SourceTypeHasNestedTypeVarMapper.class ) public void testGenericSourceTypeVar() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/PlainTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/PlainTest.java index 8f7c878810..1fd5c0a202 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/PlainTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/plain/PlainTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.selection.methodgenerics.plain; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - import java.util.Collections; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; /** * @author Sjaak Derksen * */ -@RunWith(AnnotationProcessorTestRunner.class) public class PlainTest { - @Test + @ProcessorTest @WithClasses( ReturnTypeIsTypeVarMapper.class ) public void testGenericReturnTypeVar() { @@ -33,7 +30,7 @@ public void testGenericReturnTypeVar() { assertThat( target.getProp() ).isEqualTo( "test" ); } - @Test + @ProcessorTest @WithClasses( SourceTypeIsTypeVarMapper.class ) public void testGenericSourceTypeVar() { @@ -46,7 +43,7 @@ public void testGenericSourceTypeVar() { } - @Test + @ProcessorTest @WithClasses( BothParameterizedMapper.class ) public void testBothParameterized() { @@ -60,7 +57,7 @@ public void testBothParameterized() { } - @Test + @ProcessorTest @WithClasses( ReturnTypeIsRawTypeMapper.class ) public void testRaw() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/TargetTypeTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/TargetTypeTest.java index 47037ec6be..67a9a616fb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/TargetTypeTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/targettype/TargetTypeTest.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.selection.methodgenerics.targettype; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen * */ -@RunWith(AnnotationProcessorTestRunner.class) public class TargetTypeTest { - @Test + @ProcessorTest @WithClasses( PlainTargetTypeMapper.class ) public void testPlain() { @@ -30,7 +27,7 @@ public void testPlain() { assertThat( target.getProp().toPlainString() ).isEqualTo( "15" ); } - @Test + @ProcessorTest @WithClasses( NestedTargetTypeMapper.class ) public void testNestedTypeVar() { NestedTargetTypeMapper.Target target = diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java index 1df99d4d9a..a228304c72 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java @@ -5,12 +5,9 @@ */ package org.mapstruct.ap.test.selection.methodgenerics.wildcards; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.Compiler; -import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; import static org.assertj.core.api.Assertions.assertThat; @@ -18,10 +15,9 @@ * @author Sjaak Derksen * */ -@RunWith(AnnotationProcessorTestRunner.class) public class WildCardTest { - @Test + @ProcessorTest @WithClasses( SourceWildCardExtendsMapper.class ) public void testExtendsRelation() { @@ -41,10 +37,9 @@ public void testExtendsRelation() { assertThat( target.getPropC() ).isEqualTo( typeC ); } - @Test - @WithClasses( IntersectionMapper.class ) // Eclipse does not handle intersection types correctly (TODO: worthwhile to investigate?) - @DisabledOnCompiler( Compiler.ECLIPSE ) + @ProcessorTest(Compiler.JDK) + @WithClasses( IntersectionMapper.class ) public void testIntersectionRelation() { // prepare source diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/primitives/PrimitiveVsWrappedSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/primitives/PrimitiveVsWrappedSelectionTest.java index 7832a89c9f..4b82054283 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/primitives/PrimitiveVsWrappedSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/primitives/PrimitiveVsWrappedSelectionTest.java @@ -5,13 +5,11 @@ */ package org.mapstruct.ap.test.selection.primitives; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -19,10 +17,9 @@ */ @IssueKey("205") @WithClasses({ Source.class, Target.class, MyLong.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class PrimitiveVsWrappedSelectionTest { - @Test + @ProcessorTest @WithClasses( { WrappedMapper.class, PrimitiveMapper.class, SourceTargetMapper.class } ) public void shouldAlwaysSelectWrappedAndExplicitlyTypeConvertWrappedtoPrimitive() { @@ -43,7 +40,7 @@ public void shouldAlwaysSelectWrappedAndExplicitlyTypeConvertWrappedtoPrimitive( } - @Test + @ProcessorTest @WithClasses( { PrimitiveMapper.class, SourceTargetMapperPrimitive.class } ) public void shouldSelectPrimitiveMapperAlsoForWrapped() { @@ -64,7 +61,7 @@ public void shouldSelectPrimitiveMapperAlsoForWrapped() { } - @Test + @ProcessorTest @WithClasses( { WrappedMapper.class, SourceTargetMapperWrapped.class } ) public void shouldSelectWrappedMapperAlsoForPrimitive() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java index 17c3c77863..9699b67f63 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java @@ -11,8 +11,6 @@ import java.util.Map; import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.selection.qualifier.annotation.CreateGermanRelease; import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman; import org.mapstruct.ap.test.selection.qualifier.annotation.NonQualifierAnnotated; @@ -28,11 +26,11 @@ import org.mapstruct.ap.test.selection.qualifier.handwritten.Titles; import org.mapstruct.ap.test.selection.qualifier.handwritten.YetAnotherMapper; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; @@ -49,10 +47,9 @@ SomeOtherMapper.class, NonQualifierAnnotated.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class QualifierTest { - @Test + @ProcessorTest @WithClasses( { Titles.class, Facts.class, @@ -90,7 +87,7 @@ public void shouldMatchClassAndMethod() { ); } - @Test + @ProcessorTest @WithClasses({ YetAnotherMapper.class, ErroneousMapper.class @@ -116,7 +113,7 @@ public void shouldMatchClassAndMethod() { public void shouldNotProduceMatchingMethod() { } - @Test + @ProcessorTest @WithClasses( { MapperWithoutQualifiedBy.class, Facts.class, @@ -136,7 +133,7 @@ public void shouldNotUseQualifierAnnotatedMethod() { } - @Test + @ProcessorTest @WithClasses( { MovieFactoryMapper.class, ReleaseFactory.class, @@ -171,7 +168,7 @@ public void testFactorySelectionWithQualifier() { ); } - @Test + @ProcessorTest @IssueKey("342") @WithClasses({ ErroneousMovieFactoryMapper.class diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java index 48295ed4fb..fe4ec6f7e6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/errors/QualfierMessageTest.java @@ -5,22 +5,19 @@ */ package org.mapstruct.ap.test.selection.qualifier.errors; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static javax.tools.Diagnostic.Kind.ERROR; @IssueKey("2135") -@RunWith(AnnotationProcessorTestRunner.class) public class QualfierMessageTest { - @Test + @ProcessorTest @WithClasses(ErroneousMessageByAnnotationMapper.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -42,7 +39,7 @@ public class QualfierMessageTest { public void testNoQualifyingMethodByAnnotationFound() { } - @Test + @ProcessorTest @WithClasses(ErroneousMessageByNamedMapper.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -64,7 +61,7 @@ public void testNoQualifyingMethodByAnnotationFound() { public void testNoQualifyingMethodByNamedFound() { } - @Test + @ProcessorTest @WithClasses(ErroneousMessageByAnnotationAndNamedMapper.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -88,7 +85,7 @@ public void testNoQualifyingMethodByNamedFound() { public void testNoQualifyingMethodByAnnotationAndNamedFound() { } - @Test + @ProcessorTest @WithClasses(ErroneousMessageByNamedWithIterableMapper.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/hybrid/HybridTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/hybrid/HybridTest.java index fc9c48d3a5..3b8d708806 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/hybrid/HybridTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/hybrid/HybridTest.java @@ -5,18 +5,16 @@ */ package org.mapstruct.ap.test.selection.qualifier.hybrid; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman; import org.mapstruct.ap.test.selection.qualifier.annotation.NonQualifierAnnotated; import org.mapstruct.ap.test.selection.qualifier.annotation.TitleTranslator; import org.mapstruct.ap.test.selection.qualifier.handwritten.SomeOtherMapper; import org.mapstruct.ap.test.selection.qualifier.handwritten.Titles; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -33,10 +31,9 @@ EnglishToGerman.class, TitleTranslator.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class HybridTest { - @Test + @ProcessorTest public void shouldMatchClassAndMethod() { SourceRelease foreignMovies = new SourceRelease(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/IterableAndQualifiersTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/IterableAndQualifiersTest.java index c2465b255e..d55c9f2d00 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/IterableAndQualifiersTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/iterable/IterableAndQualifiersTest.java @@ -10,11 +10,9 @@ import java.util.ArrayList; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.factory.Mappers; /** @@ -34,10 +32,9 @@ TopologyFeatureDto.class, TopologyFeatureEntity.class, } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class IterableAndQualifiersTest { - @Test + @ProcessorTest @WithClasses(TopologyMapper.class) public void testGenerationBasedOnQualifier() { @@ -71,7 +68,7 @@ public void testGenerationBasedOnQualifier() { } - @Test + @ProcessorTest @WithClasses(TopologyWithoutIterableMappingMapper.class) public void testIterableGeneratorBasedOnQualifier() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/named/NamedTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/named/NamedTest.java index 3de7b1515e..bcb306d9e8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/named/NamedTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/named/NamedTest.java @@ -5,16 +5,11 @@ */ package org.mapstruct.ap.test.selection.qualifier.named; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.selection.qualifier.annotation.CreateGermanRelease; import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman; import org.mapstruct.ap.test.selection.qualifier.annotation.NonQualifierAnnotated; @@ -28,8 +23,11 @@ import org.mapstruct.ap.test.selection.qualifier.handwritten.SomeOtherMapper; import org.mapstruct.ap.test.selection.qualifier.handwritten.Titles; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * @@ -43,10 +41,9 @@ SomeOtherMapper.class, NonQualifierAnnotated.class } ) -@RunWith( AnnotationProcessorTestRunner.class ) public class NamedTest { - @Test + @ProcessorTest @WithClasses( { Titles.class, Facts.class, @@ -84,7 +81,7 @@ public void shouldMatchClassAndMethod() { ); } - @Test + @ProcessorTest @WithClasses( { MovieFactoryMapper.class, ReleaseFactory.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java index 388532bbe8..a6a4f1ad05 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java @@ -11,14 +11,12 @@ import java.util.Map; import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -34,10 +32,9 @@ Apple.class, AppleDto.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class InheritanceSelectionTest { - @Test + @ProcessorTest @WithClasses({ ConflictingFruitFactory.class, ErroneousFruitMapper.class, Banana.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -56,7 +53,7 @@ public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() } @IssueKey("1283") - @Test + @ProcessorTest @WithClasses({ ErroneousResultTypeNoAccessibleConstructorMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -71,7 +68,7 @@ public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() public void testResultTypeHasNoSuitableAccessibleConstructor() { } - @Test + @ProcessorTest @WithClasses({ ConflictingFruitFactory.class, ResultTypeSelectingFruitMapper.class, Banana.class }) public void testResultTypeBasedFactoryMethodSelection() { @@ -82,7 +79,7 @@ public void testResultTypeBasedFactoryMethodSelection() { } - @Test + @ProcessorTest @IssueKey("434") @WithClasses({ ResultTypeConstructingFruitMapper.class }) public void testResultTypeBasedConstructionOfResult() { @@ -93,7 +90,7 @@ public void testResultTypeBasedConstructionOfResult() { assertThat( fruit.getType() ).isEqualTo( "constructed-by-constructor" ); } - @Test + @ProcessorTest @IssueKey("657") @WithClasses({ ResultTypeConstructingFruitInterfaceMapper.class }) public void testResultTypeBasedConstructionOfResultForInterface() { @@ -104,7 +101,7 @@ public void testResultTypeBasedConstructionOfResultForInterface() { assertThat( fruit.getType() ).isEqualTo( "constructed-by-constructor" ); } - @Test + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @@ -121,7 +118,7 @@ public void testResultTypeBasedConstructionOfResultForInterface() { public void testResultTypeBasedConstructionOfResultForInterfaceErroneous() { } - @Test + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @@ -136,7 +133,7 @@ public void testResultTypeBasedConstructionOfResultForInterfaceErroneous() { public void testResultTypeBasedConstructionOfResultNonAssignable() { } - @Test + @ProcessorTest @IssueKey("433") @WithClasses({ FruitFamilyMapper.class, @@ -160,7 +157,7 @@ public void testShouldSelectResultTypeInCaseOfAmbiguity() { } - @Test + @ProcessorTest @IssueKey("433") @WithClasses({ FruitFamilyMapper.class, @@ -182,7 +179,7 @@ public void testShouldSelectResultTypeInCaseOfAmbiguityForIterable() { assertThat( result.get( 0 ).getType() ).isEqualTo( "AppleDto" ); } - @Test + @ProcessorTest @IssueKey("433") @WithClasses({ FruitFamilyMapper.class, @@ -210,7 +207,7 @@ public void testShouldSelectResultTypeInCaseOfAmbiguityForMap() { } - @Test + @ProcessorTest @IssueKey("73") @WithClasses({ Citrus.class, @@ -227,7 +224,7 @@ public void testShouldUseConstructorFromResultTypeForInterface() { assertThat( citrus.getType() ).isEqualTo( "orange" ); } - @Test + @ProcessorTest @IssueKey("73") @WithClasses({ Citrus.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java index 27c33d3bd7..92dcbfe0a4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/TwoStepMappingTest.java @@ -5,18 +5,15 @@ */ package org.mapstruct.ap.test.selection.twosteperror; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -@RunWith( AnnotationProcessorTestRunner.class ) public class TwoStepMappingTest { - @Test + @ProcessorTest @WithClasses(ErroneousMapperMM.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = @Diagnostic( @@ -33,7 +30,7 @@ public class TwoStepMappingTest { public void methodAndMethodTest() { } - @Test + @ProcessorTest @WithClasses( ErroneousMapperCM.class ) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @@ -56,7 +53,7 @@ public void methodAndMethodTest() { public void conversionAndMethodTest() { } - @Test + @ProcessorTest @WithClasses( ErroneousMapperMC.class ) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/WildCardTest.java index 362e00331c..e38089eaa5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/typegenerics/WildCardTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.selection.typegenerics; -import static org.assertj.core.api.Assertions.assertThat; - import java.math.BigInteger; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen * */ -@RunWith(AnnotationProcessorTestRunner.class) public class WildCardTest { - @Test + @ProcessorTest @WithClasses( SourceWildCardExtendsMapper.class ) public void testWildCard() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/WildCardTest.java index 405a22320e..4724748a91 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/wildcards/WildCardTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.selection.wildcards; -import static org.assertj.core.api.Assertions.assertThat; - import java.math.BigInteger; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen * */ -@RunWith(AnnotationProcessorTestRunner.class) public class WildCardTest { - @Test + @ProcessorTest @WithClasses( SourceWildCardExtendsMapper.class ) public void testWildCardAsSourceType() { @@ -38,7 +35,7 @@ public void testWildCardAsSourceType() { assertThat( target.getProp() ).isEqualTo( "5" ); } - @Test + @ProcessorTest @WithClasses( ReturnTypeWildCardExtendsMapper.class ) public void testWildCardAsReturnType() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java index ce386ab7b0..bb31ba1b20 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/ConstantsTest.java @@ -5,15 +5,13 @@ */ package org.mapstruct.ap.test.source.constants; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; @@ -22,18 +20,17 @@ * * @author Sjaak Derksen */ -@RunWith( AnnotationProcessorTestRunner.class ) @WithClasses( { ConstantsMapper.class, ConstantsTarget.class } ) public class ConstantsTest { - @Rule - public final GeneratedSource generatedSrc = + @RegisterExtension + final GeneratedSource generatedSrc = new GeneratedSource().addComparisonToFixtureFor( ConstantsMapper.class ); - @Test + @ProcessorTest public void testNumericConstants() { ConstantsTarget target = ConstantsMapper.INSTANCE.mapFromConstants( "dummy" ); @@ -58,7 +55,7 @@ public void testNumericConstants() { assertThat( target.getDoubleBoxedZero() ).isEqualTo( 0.0 ); } - @Test + @ProcessorTest @IssueKey("1458") @WithClasses({ ConstantsTarget.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java index 3b307c233c..b331a2efba 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java @@ -5,32 +5,28 @@ */ package org.mapstruct.ap.test.source.constants; -import static org.assertj.core.api.Assertions.assertThat; - import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) public class SourceConstantsTest { - @Test + @ProcessorTest @IssueKey("187, 305") @WithClasses({ Source.class, @@ -57,7 +53,7 @@ public void shouldMapSameSourcePropertyToSeveralTargetProperties() throws ParseE assertThat( target.getCountry() ).isEqualTo( CountryEnum.THE_NETHERLANDS ); } - @Test + @ProcessorTest @IssueKey("187") @WithClasses({ Source.class, @@ -76,7 +72,7 @@ public void shouldMapTargetToSourceWithoutWhining() throws ParseException { assertThat( target.getPropertyThatShouldBeMapped() ).isEqualTo( "SomeProperty" ); } - @Test + @ProcessorTest @IssueKey("187") @WithClasses({ Source.class, @@ -102,7 +98,7 @@ public void shouldMapTargetToSourceWithoutWhining() throws ParseException { public void errorOnSourceAndConstant() throws ParseException { } - @Test + @ProcessorTest @IssueKey("187") @WithClasses({ Source.class, @@ -129,7 +125,7 @@ public void errorOnSourceAndConstant() throws ParseException { public void errorOnConstantAndExpression() throws ParseException { } - @Test + @ProcessorTest @IssueKey("187") @WithClasses({ Source.class, @@ -155,7 +151,7 @@ public void errorOnConstantAndExpression() throws ParseException { public void errorOnSourceAndExpression() throws ParseException { } - @Test + @ProcessorTest @IssueKey("255") @WithClasses({ Source1.class, @@ -177,7 +173,7 @@ public void shouldMapSameSourcePropertyToSeveralTargetPropertiesFromSeveralSourc assertThat( target.getSomeConstant() ).isEqualTo( "stringConstant" ); } - @Test + @ProcessorTest @IssueKey("700") @WithClasses({ Source.class, @@ -202,7 +198,7 @@ public void shouldMapSameSourcePropertyToSeveralTargetPropertiesFromSeveralSourc public void errorOnNonExistingEnumConstant() throws ParseException { } - @Test + @ProcessorTest @IssueKey("1401") @WithClasses({ Source.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java index 24b2ee3538..3df90421b3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.source.defaultExpressions.java; -import org.junit.Test; -import org.junit.runner.RunWith; +import java.util.Date; + +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; - -import java.util.Date; import static org.assertj.core.api.Assertions.assertThat; /** * @author Jeffrey Smyth */ -@RunWith(AnnotationProcessorTestRunner.class) public class JavaDefaultExpressionTest { - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) public void testJavaDefaultExpressionWithValues() { Source source = new Source(); @@ -37,7 +34,7 @@ public void testJavaDefaultExpressionWithValues() { assertThat( target.getSourceDate() ).isEqualTo( source.getDate() ); } - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) public void testJavaDefaultExpressionWithNoValues() { Source source = new Source(); @@ -49,7 +46,7 @@ public void testJavaDefaultExpressionWithNoValues() { assertThat( target.getSourceDate() ).isEqualTo( new Date( 30L ) ); } - @Test + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @@ -70,7 +67,7 @@ public void testJavaDefaultExpressionWithNoValues() { public void testJavaDefaultExpressionExpression() { } - @Test + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @@ -91,7 +88,7 @@ public void testJavaDefaultExpressionExpression() { public void testJavaDefaultExpressionConstant() { } - @Test + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { @@ -112,7 +109,7 @@ public void testJavaDefaultExpressionConstant() { public void testJavaDefaultExpressionDefaultValue() { } - @Test + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java index 68d19376de..be80644606 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java @@ -5,30 +5,27 @@ */ package org.mapstruct.ap.test.source.expressions.java; -import static org.assertj.core.api.Assertions.assertThat; - import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.source.expressions.java.mapper.TimeAndFormat; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen */ -@RunWith(AnnotationProcessorTestRunner.class) public class JavaExpressionTest { - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, TimeAndFormat.class, SourceTargetMapper.class }) public void testJavaExpressionInsertion() throws ParseException { Source source = new Source(); @@ -47,7 +44,7 @@ public void testJavaExpressionInsertion() throws ParseException { } @IssueKey( "255" ) - @Test + @ProcessorTest @WithClasses({ Source.class, Source2.class, @@ -79,7 +76,7 @@ private Date getTime(String format, String date) throws ParseException { return dateFormat.parse( date ); } - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, TimeAndFormat.class, SourceTargetMapper.class }) public void testJavaExpressionInsertionWithExistingTarget() throws ParseException { Source source = new Source(); @@ -100,7 +97,7 @@ public void testJavaExpressionInsertionWithExistingTarget() throws ParseExceptio } @IssueKey( "278" ) - @Test + @ProcessorTest @WithClasses({ SourceBooleanWorkAround.class, TargetBooleanWorkAround.class, @@ -120,7 +117,7 @@ public void testBooleanGetterWorkAround() throws ParseException { } @IssueKey( "305" ) - @Test + @ProcessorTest @WithClasses({ SourceList.class, TargetList.class, @@ -136,7 +133,7 @@ public void testGetterOnly() throws ParseException { } @IssueKey( "1851" ) - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/ignore/IgnoreUnmappedSourcePropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/ignore/IgnoreUnmappedSourcePropertiesTest.java index f8b6b7306a..5c3bafa231 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/ignore/IgnoreUnmappedSourcePropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/ignore/IgnoreUnmappedSourcePropertiesTest.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.test.source.ignore; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov @@ -19,11 +17,10 @@ Person.class, PersonDto.class }) -@RunWith( AnnotationProcessorTestRunner.class ) @IssueKey("1317") public class IgnoreUnmappedSourcePropertiesTest { - @Test + @ProcessorTest public void shouldNotReportErrorOnIgnoredUnmappedSourceProperties() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java index 63aaf8b654..c4c68097f3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/manysourcearguments/ManySourceArgumentsTest.java @@ -5,20 +5,18 @@ */ package org.mapstruct.ap.test.source.manysourcearguments; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for propagation of attribute without setter in source and getter in @@ -27,15 +25,14 @@ * @author Gunnar Morling */ @IssueKey("31") -@RunWith(AnnotationProcessorTestRunner.class) public class ManySourceArgumentsTest { - @Before + @BeforeEach public void reset() { ReferencedMapper.setBeforeMappingCalled( false ); } - @Test + @ProcessorTest @WithClasses( { Person.class, Address.class, @@ -58,7 +55,7 @@ public void shouldMapSeveralSourceAttributesToCombinedTarget() { assertThat( ReferencedMapper.isBeforeMappingCalled() ).isTrue(); } - @Test + @ProcessorTest @WithClasses( { Person.class, Address.class, @@ -80,7 +77,7 @@ public void shouldMapSeveralSourceAttributesToCombinedTargetWithTargetParameter( assertThat( ReferencedMapper.isBeforeMappingCalled() ).isTrue(); } - @Test + @ProcessorTest @WithClasses( { Person.class, Address.class, @@ -100,7 +97,7 @@ public void shouldSetAttributesFromNonNullParameters() { assertThat( deliveryAddress.getStreet() ).isNull(); } - @Test + @ProcessorTest @WithClasses( { Person.class, Address.class, @@ -114,7 +111,7 @@ public void shouldReturnNullIfAllParametersAreNull() { assertThat( deliveryAddress ).isNull(); } - @Test + @ProcessorTest @WithClasses( { Person.class, Address.class, @@ -135,7 +132,7 @@ public void shouldMapSeveralSourceAttributesAndParameters() { } @IssueKey( "1593" ) - @Test + @ProcessorTest @WithClasses( { Person.class, Address.class, @@ -157,7 +154,7 @@ public void shouldUseConfig() { } - @Test + @ProcessorTest @WithClasses({ ErroneousSourceTargetMapper.class, Address.class, DeliveryAddress.class }) @ProcessorOption(name = "mapstruct.unmappedTargetPolicy", value = "IGNORE") @ExpectedCompilationOutcome( @@ -179,7 +176,7 @@ public void shouldUseConfig() { public void shouldFailToGenerateMappingsForAmbiguousSourceProperty() { } - @Test + @ProcessorTest @WithClasses({ ErroneousSourceTargetMapper2.class, Address.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/manytargetproperties/SourceToManyTargetPropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/manytargetproperties/SourceToManyTargetPropertiesTest.java index 2da3dc8723..8ab34ceec9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/manytargetproperties/SourceToManyTargetPropertiesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/manytargetproperties/SourceToManyTargetPropertiesTest.java @@ -5,17 +5,15 @@ */ package org.mapstruct.ap.test.source.manytargetproperties; -import static org.assertj.core.api.Assertions.assertThat; - import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for the generation of implementation of abstract base classes. @@ -26,10 +24,9 @@ Source.class, Target.class, SourceTargetMapper.class, TimeAndFormat.class, TimeAndFormatMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class SourceToManyTargetPropertiesTest { - @Test + @ProcessorTest @IssueKey("94") public void shouldMapSameSourcePropertyToSeveralTargetProperties() { Source source = new Source(); @@ -42,7 +39,7 @@ public void shouldMapSameSourcePropertyToSeveralTargetProperties() { assertThat( target.getName2() ).isEqualTo( "Bob" ); } - @Test + @ProcessorTest @IssueKey("94") public void shouldMapSameSourcePropertyToSeveralTargetPropertiesInvokingOtherMapper() throws ParseException { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/PresenceCheckTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/PresenceCheckTest.java index eaa9e85159..c790750f00 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/PresenceCheckTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/PresenceCheckTest.java @@ -5,12 +5,11 @@ */ package org.mapstruct.ap.test.source.nullvaluecheckstrategy; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * @@ -25,10 +24,9 @@ RockFestivalMapperOveridingConfig.class, Stage.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class PresenceCheckTest { - @Test + @ProcessorTest public void testCallingMappingMethodWithNullSource() { RockFestivalSource source = new RockFestivalSource(); @@ -41,7 +39,7 @@ public void testCallingMappingMethodWithNullSource() { } - @Test + @ProcessorTest public void testCallingMappingMethodWithNullSourceWithConfig() { RockFestivalSource source = new RockFestivalSource(); @@ -54,10 +52,11 @@ public void testCallingMappingMethodWithNullSourceWithConfig() { } - @Test( expected = IllegalArgumentException.class ) + @ProcessorTest public void testCallingMappingMethodWithNullSourceOveridingConfig() { RockFestivalSource source = new RockFestivalSource(); - RockFestivalMapperOveridingConfig.INSTANCE.map( source ); + assertThatThrownBy( () -> RockFestivalMapperOveridingConfig.INSTANCE.map( source ) ) + .isInstanceOf( IllegalArgumentException.class ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckMappingTest.java index c25dad70b5..f03172358d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckMappingTest.java @@ -7,20 +7,17 @@ import java.util.Collections; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) public class PresenceCheckMappingTest { - @Test + @ProcessorTest @WithClasses({ CollectionPresenceMapper.class }) @@ -31,7 +28,7 @@ public void collectionPresenceCheckShouldBeUsedByDefault() { assertThat( target.getPlayers() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ CollectionWithNullValueCheckMapper.class }) @@ -44,7 +41,7 @@ public void collectionPresenceCheckShouldBeUsedByWhenNullValueCheckIsAlways() { assertThat( target.getPlayers() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ CollectionWithNonDirectMapper.class }) @@ -56,7 +53,7 @@ public void nonDirectCollectionPresenceCheckShouldBeUsedByDefault() { assertThat( target.getPlayers() ).isNull(); } - @Test + @ProcessorTest @WithClasses({ NonDirectMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckNestedObjectsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckNestedObjectsTest.java index 1f39810831..45fe8d7e98 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckNestedObjectsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckNestedObjectsTest.java @@ -5,13 +5,10 @@ */ package org.mapstruct.ap.test.source.presencecheck.spi; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertFalse; /** * Test for correct handling of source presence checks for nested objects. @@ -25,10 +22,9 @@ SoccerTeamTargetWithPresenceCheck.class, Referee.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class PresenceCheckNestedObjectsTest { - @Test + @ProcessorTest public void testNestedWithSourcesAbsentOnNestingLevel() { SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); @@ -42,7 +38,7 @@ public void testNestedWithSourcesAbsentOnNestingLevel() { SoccerTeamTargetWithPresenceCheck target = SoccerTeamMapperNestedObjects.INSTANCE.mapNested( soccerTeamSource ); assertThat( target.getGoalKeeperName() ).isNull(); - assertFalse( target.hasGoalKeeperName() ); + assertThat( target.hasGoalKeeperName() ).isFalse(); assertThat( target.getReferee() ).isNotNull(); assertThat( target.getReferee().getName() ).isNull(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckTest.java index 8758ea8e9d..d99bfa343e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckTest.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.source.presencecheck.spi; -import static org.assertj.core.api.Assertions.assertThat; - import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test for correct handling of source presence checks. @@ -28,10 +26,9 @@ GoalKeeper.class, SoccerTeamTarget.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class PresenceCheckTest { - @Test + @ProcessorTest public void testWithSourcesPresent() { Source source = new Source(); @@ -49,7 +46,7 @@ public void testWithSourcesPresent() { assertThat( target.getSomeArray() ).isEqualTo( new String[]{ "x", "y"} ); } - @Test + @ProcessorTest public void testWithSourcesAbsent() { Source source = new Source(); @@ -67,7 +64,7 @@ public void testWithSourcesAbsent() { assertThat( target.getSomeArray() ).isNull(); } - @Test + @ProcessorTest public void testUpdateMethodWithSourcesPresent() { Source source = new Source(); @@ -86,7 +83,7 @@ public void testUpdateMethodWithSourcesPresent() { assertThat( target.getSomeArray() ).isEqualTo( new String[]{ "x", "y"} ); } - @Test + @ProcessorTest public void testUpdateMethodWithSourcesAbsent() { Source source = new Source(); @@ -105,7 +102,7 @@ public void testUpdateMethodWithSourcesAbsent() { assertThat( target.getSomeArray() ).isNull(); } - @Test + @ProcessorTest public void testWithSourcesPresentAndDefault() { Source source = new Source(); @@ -123,7 +120,7 @@ public void testWithSourcesPresentAndDefault() { assertThat( target.getSomeArray() ).isEqualTo( new String[]{ "x", "y"} ); } - @Test + @ProcessorTest public void testWithSourcesAbsentAndDefault() { Source source = new Source(); @@ -141,7 +138,7 @@ public void testWithSourcesAbsentAndDefault() { assertThat( target.getSomeArray() ).isEqualTo( new String[]{ "u", "v"} ); } - @Test + @ProcessorTest public void testAdderWithSourcesPresent() { SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); @@ -152,7 +149,7 @@ public void testAdderWithSourcesPresent() { assertThat( target.getPlayers() ).containsExactly( "pele", "cruyf" ); } - @Test + @ProcessorTest public void testAdderWithSourcesAbsent() { SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); @@ -163,7 +160,7 @@ public void testAdderWithSourcesAbsent() { assertThat( target.getPlayers() ).isNull(); } - @Test + @ProcessorTest public void testNestedWithSourcesPresent() { SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); @@ -176,7 +173,7 @@ public void testNestedWithSourcesPresent() { assertThat( target.getGoalKeeperName() ).isEqualTo( "Buffon" ); } - @Test + @ProcessorTest public void testNestedWithSourcesAbsentOnRootLevel() { SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); @@ -187,7 +184,7 @@ public void testNestedWithSourcesAbsentOnRootLevel() { assertThat( target.getGoalKeeperName() ).isNull(); } - @Test + @ProcessorTest public void testNestedWithSourcesAbsentOnNestingLevel() { SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/targetthis/TargetThisMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/targetthis/TargetThisMappingTest.java index 6ef6c29532..5c4f43ba3d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/targetthis/TargetThisMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/targetthis/TargetThisMappingTest.java @@ -5,20 +5,17 @@ */ package org.mapstruct.ap.test.targetthis; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Dainius Figoras */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses( { OrderDTO.class, CustomerDTO.class, @@ -30,7 +27,7 @@ } ) public class TargetThisMappingTest { - @Test + @ProcessorTest @WithClasses( SimpleMapper.class ) public void testTargetingThis() { CustomerDTO ce = new CustomerDTO(); @@ -49,7 +46,7 @@ public void testTargetingThis() { assertThat( c.getStatus() ).isEqualTo( ce.getItem().getStatus() ); } - @Test + @ProcessorTest @WithClasses( NestedMapper.class ) public void testTargetingThisWithNestedLevels() { CustomerDTO customerDTO = new CustomerDTO(); @@ -70,7 +67,7 @@ public void testTargetingThisWithNestedLevels() { assertThat( c.getStatus() ).isEqualTo( customerDTO.getItem().getStatus() ); } - @Test + @ProcessorTest @WithClasses( SimpleMapperWithIgnore.class ) public void testTargetingThisWithIgnore() { CustomerDTO ce = new CustomerDTO(); @@ -89,7 +86,7 @@ public void testTargetingThisWithIgnore() { assertThat( c.getStatus() ).isEqualTo( ce.getItem().getStatus() ); } - @Test + @ProcessorTest @WithClasses( ErroneousNestedMapper.class ) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -107,7 +104,7 @@ public void testTargetingThisWithIgnore() { public void testNestedDuplicates() { } - @Test + @ProcessorTest @WithClasses( ConfictsResolvedNestedMapper.class ) public void testWithConflictsResolved() { @@ -128,7 +125,7 @@ public void testWithConflictsResolved() { assertThat( c.getId() ).isEqualTo( orderDTO.getCustomer().getItem().getId() ); } - @Test + @ProcessorTest @WithClasses( FlatteningMapper.class ) public void testFlattening() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java b/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java index f5eda7024d..0b50591a7a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/template/InheritConfigurationTest.java @@ -5,33 +5,29 @@ */ package org.mapstruct.ap.test.template; - -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.template.erroneous.SourceTargetMapperAmbiguous1; import org.mapstruct.ap.test.template.erroneous.SourceTargetMapperAmbiguous2; import org.mapstruct.ap.test.template.erroneous.SourceTargetMapperAmbiguous3; import org.mapstruct.ap.test.template.erroneous.SourceTargetMapperNonMatchingName; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Sjaak Derksen */ @IssueKey("383") @WithClasses({ Source.class, NestedSource.class, Target.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class InheritConfigurationTest { - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperSingle.class }) public void shouldInheritConfigurationSingleCandidates() { @@ -58,7 +54,7 @@ public void shouldInheritConfigurationSingleCandidates() { assertThat( updatedTarget.getConstantProp() ).isEqualTo( "constant" ); } - @Test + @ProcessorTest @WithClasses( { SourceTargetMapperMultiple.class } ) public void shouldInheritConfigurationMultipleCandidates() { @@ -86,7 +82,7 @@ public void shouldInheritConfigurationMultipleCandidates() { } - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperSeveralArgs.class }) public void shouldInheritConfigurationSeveralArgs() { @@ -114,7 +110,7 @@ public void shouldInheritConfigurationSeveralArgs() { } - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperAmbiguous1.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -134,7 +130,7 @@ public void shouldInheritConfigurationSeveralArgs() { public void shouldRaiseAmbiguousReverseMethodError() { } - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperAmbiguous2.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -154,7 +150,7 @@ public void shouldRaiseAmbiguousReverseMethodError() { public void shouldRaiseAmbiguousReverseMethodErrorWrongName() { } - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperAmbiguous3.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -176,7 +172,7 @@ public void shouldRaiseAmbiguousReverseMethodErrorWrongName() { public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() { } - @Test + @ProcessorTest @WithClasses({ SourceTargetMapperNonMatchingName.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java index 0adcb9e0ff..59d44a6baf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java @@ -5,29 +5,26 @@ */ package org.mapstruct.ap.test.unmappedsource; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.test.unmappedtarget.Source; +import org.mapstruct.ap.test.unmappedtarget.Target; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; -import org.mapstruct.ap.test.unmappedtarget.Source; -import org.mapstruct.ap.test.unmappedtarget.Target; + +import static org.assertj.core.api.Assertions.assertThat; /** * Tests expected diagnostics for unmapped source properties. * * @author Gunnar Morling */ -@RunWith(AnnotationProcessorTestRunner.class) public class UnmappedSourceTest { - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, @@ -53,7 +50,7 @@ public void shouldLeaveUnmappedSourcePropertyUnset() { assertThat( target.getBar() ).isEqualTo( 0 ); } - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, ErroneousStrictSourceTargetMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java index 4a31fb93ea..a36ec31436 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java @@ -5,19 +5,17 @@ */ package org.mapstruct.ap.test.unmappedtarget; -import static org.assertj.core.api.Assertions.assertThat; - import javax.tools.Diagnostic.Kind; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; /** * Tests expected diagnostics for unmapped target properties. @@ -25,10 +23,9 @@ * @author Gunnar Morling */ @IssueKey("35") -@RunWith(AnnotationProcessorTestRunner.class) public class UnmappedProductTest { - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, @@ -54,7 +51,7 @@ public void shouldLeaveUnmappedTargetPropertyUnset() { assertThat( target.getBar() ).isEqualTo( 0 ); } - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, ErroneousStrictSourceTargetMapper.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -72,7 +69,7 @@ public void shouldLeaveUnmappedTargetPropertyUnset() { public void shouldRaiseErrorDueToUnsetTargetProperty() { } - @Test + @ProcessorTest @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) @ProcessorOption(name = "mapstruct.unmappedTargetPolicy", value = "ERROR") @ExpectedCompilationOutcome( diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java index d7e5bdea43..d681f0eabe 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/UpdateMethodsTest.java @@ -5,15 +5,13 @@ */ package org.mapstruct.ap.test.updatemethods; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; @@ -23,7 +21,6 @@ * @author Sjaak Derksen */ @IssueKey("160") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ OrganizationDto.class, OrganizationEntity.class, @@ -41,10 +38,10 @@ }) public class UpdateMethodsTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses( { OrganizationMapper.class } ) @@ -73,7 +70,7 @@ public void testPreferUpdateMethod() { assertThat( organizationEntity.getCompany().getDepartment().getName() ).isEqualTo( "finance" ); } - @Test + @ProcessorTest @WithClasses( { OrganizationMapper.class } ) @@ -98,7 +95,7 @@ public void testUpdateMethodClearsExistingValues() { assertThat( organizationEntity.getTypeNr().getNumber() ).isEqualTo( 5 ); } - @Test + @ProcessorTest @WithClasses({ OrganizationMapper.class }) @@ -123,7 +120,7 @@ public void testPreferUpdateMethodSourceObjectNotDefined() { assertThat( organizationEntity.getCompany().getDepartment().getName() ).isEqualTo( "finance" ); } - @Test + @ProcessorTest @WithClasses( { CompanyMapper.class, DepartmentInBetween.class @@ -144,7 +141,7 @@ public void testPreferUpdateMethodEncapsulatingCreateMethod() { } - @Test + @ProcessorTest @WithClasses( { ErroneousOrganizationMapper1.class, OrganizationWithoutCompanyGetterEntity.class @@ -160,7 +157,7 @@ public void testPreferUpdateMethodEncapsulatingCreateMethod() { ) public void testShouldFailOnPropertyMappingNoPropertyGetter() { } - @Test + @ProcessorTest @WithClasses({ ErroneousOrganizationMapper2.class, OrganizationWithoutTypeGetterEntity.class @@ -181,7 +178,7 @@ public void testShouldFailOnPropertyMappingNoPropertyGetter() { } public void testShouldFailOnConstantMappingNoPropertyGetter() { } - @Test + @ProcessorTest @WithClasses({ CompanyMapper1.class, DepartmentInBetween.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ManyArgumentsUpdateMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ManyArgumentsUpdateMappingTest.java index 8a6ac225a6..081bd8bc01 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ManyArgumentsUpdateMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/manysourcearguments/ManyArgumentsUpdateMappingTest.java @@ -8,11 +8,9 @@ import java.time.LocalDate; import java.time.Month; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -20,7 +18,6 @@ * @author Filip Hrisafov */ @IssueKey("2274") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ ExampleMapper.class, ExampleMember.class, @@ -29,7 +26,7 @@ }) public class ManyArgumentsUpdateMappingTest { - @Test + @ProcessorTest public void shouldUpdateToNullIfSourceParametersAreNull() { ExampleTarget target = new ExampleTarget(); target.setBirthday( LocalDate.of( 2020, Month.NOVEMBER, 15 ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/selection/ExternalSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/selection/ExternalSelectionTest.java index 8c10656ae4..23f75115b8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/updatemethods/selection/ExternalSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/updatemethods/selection/ExternalSelectionTest.java @@ -9,9 +9,7 @@ import java.util.HashMap; import java.util.Map; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.updatemethods.BossDto; import org.mapstruct.ap.test.updatemethods.BossEntity; import org.mapstruct.ap.test.updatemethods.CompanyDto; @@ -25,8 +23,8 @@ import org.mapstruct.ap.test.updatemethods.SecretaryDto; import org.mapstruct.ap.test.updatemethods.SecretaryEntity; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; 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; @@ -36,7 +34,6 @@ * @author Sjaak Derksen */ @IssueKey("160") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ OrganizationMapper1.class, ExternalMapper.class, @@ -57,14 +54,14 @@ }) public class ExternalSelectionTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( OrganizationMapper1.class, ExternalMapper.class, DepartmentMapper.class ); - @Test + @ProcessorTest @WithClasses({ OrganizationMapper1.class }) @@ -76,7 +73,7 @@ public void shouldSelectGeneratedExternalMapper() { OrganizationMapper1.INSTANCE.toCompanyEntity( dto, entity ); } - @Test + @ProcessorTest @WithClasses({ OrganizationMapper3.class }) @@ -89,7 +86,7 @@ public void shouldSelectGeneratedExternalMapperWithImportForPropertyType() { OrganizationMapper3.INSTANCE.toBossEntity( dto, entity ); } - @Test + @ProcessorTest @WithClasses({ OrganizationMapper2.class }) @@ -101,7 +98,7 @@ public void shouldSelectGeneratedHandWrittenExternalMapper() { OrganizationMapper2.INSTANCE.toCompanyEntity( dto, entity ); } - @Test + @ProcessorTest @IssueKey( "487" ) public void shouldSelectGeneratedExternalMapperForIterablesAndMaps() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java index 1e93f4b00d..30eeda4737 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumMappingTest.java @@ -7,17 +7,15 @@ import javax.tools.Diagnostic.Kind; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.value.ExternalOrderType; import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; @@ -32,17 +30,16 @@ OrderMapper.class, SpecialOrderMapper.class, DefaultOrderMapper.class, OrderEntity.class, OrderType.class, OrderDto.class, ExternalOrderType.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class EnumToEnumMappingTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( DefaultOrderMapper.class, OrderMapper.class, SpecialOrderMapper.class ); - @Test + @ProcessorTest public void shouldGenerateEnumMappingMethod() { ExternalOrderType target = OrderMapper.INSTANCE.orderTypeToExternalOrderType( OrderType.B2B ); assertThat( target ).isEqualTo( ExternalOrderType.B2B ); @@ -51,7 +48,7 @@ public void shouldGenerateEnumMappingMethod() { assertThat( target ).isEqualTo( ExternalOrderType.RETAIL ); } - @Test + @ProcessorTest public void shouldConsiderConstantMappings() { ExternalOrderType target = OrderMapper.INSTANCE.orderTypeToExternalOrderType( OrderType.EXTRA ); assertThat( target ).isEqualTo( ExternalOrderType.SPECIAL ); @@ -63,7 +60,7 @@ public void shouldConsiderConstantMappings() { assertThat( target ).isEqualTo( ExternalOrderType.DEFAULT ); } - @Test + @ProcessorTest public void shouldInvokeEnumMappingMethodForPropertyMapping() { OrderEntity order = new OrderEntity(); order.setOrderType( OrderType.EXTRA ); @@ -73,7 +70,7 @@ public void shouldInvokeEnumMappingMethodForPropertyMapping() { assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.SPECIAL ); } - @Test + @ProcessorTest public void shouldApplyReverseMappings() { OrderType result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); @@ -90,7 +87,7 @@ public void shouldApplyReverseMappings() { } - @Test + @ProcessorTest public void shouldApplyNullMapping() { OrderEntity order = new OrderEntity(); order.setOrderType( null ); @@ -100,7 +97,7 @@ public void shouldApplyNullMapping() { assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.DEFAULT ); } - @Test + @ProcessorTest public void shouldApplyTargetIsNullMapping() { OrderEntity order = new OrderEntity(); order.setOrderType( OrderType.STANDARD ); @@ -110,7 +107,7 @@ public void shouldApplyTargetIsNullMapping() { assertThat( orderDto.getOrderType() ).isNull(); } - @Test + @ProcessorTest public void shouldApplyDefaultMappings() { OrderEntity order = new OrderEntity(); @@ -140,7 +137,7 @@ public void shouldApplyDefaultMappings() { assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.RETAIL ); } - @Test + @ProcessorTest public void shouldApplyDefaultReverseMappings() { OrderType result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); @@ -157,7 +154,7 @@ public void shouldApplyDefaultReverseMappings() { } - @Test + @ProcessorTest public void shouldMappAllUnmappedToDefault() { OrderEntity order = new OrderEntity(); @@ -189,7 +186,7 @@ public void shouldMappAllUnmappedToDefault() { } @IssueKey("1091") - @Test + @ProcessorTest public void shouldMapAnyRemainingToNullCorrectly() { ExternalOrderType externalOrderType = SpecialOrderMapper.INSTANCE.anyRemainingToNull( OrderType.RETAIL ); assertThat( externalOrderType ) @@ -211,7 +208,7 @@ public void shouldMapAnyRemainingToNullCorrectly() { assertThat( externalOrderType ).isNull(); } - @Test + @ProcessorTest @WithClasses(ErroneousOrderMapperMappingSameConstantTwice.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -225,7 +222,7 @@ public void shouldMapAnyRemainingToNullCorrectly() { public void shouldRaiseErrorIfSameSourceEnumConstantIsMappedTwice() { } - @Test + @ProcessorTest @WithClasses(ErroneousOrderMapperUsingUnknownEnumConstants.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -244,7 +241,7 @@ public void shouldRaiseErrorIfSameSourceEnumConstantIsMappedTwice() { public void shouldRaiseErrorIfUnknownEnumConstantsAreSpecifiedInMapping() { } - @Test + @ProcessorTest @WithClasses(ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -259,7 +256,7 @@ public void shouldRaiseErrorIfUnknownEnumConstantsAreSpecifiedInMapping() { public void shouldRaiseErrorIfSourceConstantWithoutMatchingConstantInTargetTypeIsNotMapped() { } - @Test + @ProcessorTest @WithClasses(ErroneousOrderMapperDuplicateANY.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java index 15f0d7964d..0ee778304c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2enum/EnumToEnumThrowExceptionMappingTest.java @@ -5,16 +5,14 @@ */ package org.mapstruct.ap.test.value.enum2enum; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.value.ExternalOrderType; import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -27,11 +25,10 @@ OrderEntity.class, OrderType.class, ExternalOrderType.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class EnumToEnumThrowExceptionMappingTest { @IssueKey("2339") - @Test + @ProcessorTest @WithClasses(DefaultOrderThrowExceptionMapper.class) public void shouldBeAbleToMapAnyUnmappedToThrowException() { @@ -43,7 +40,7 @@ public void shouldBeAbleToMapAnyUnmappedToThrowException() { } @IssueKey("2339") - @Test + @ProcessorTest @WithClasses({ DefaultOrderThrowExceptionMapper.class, ErroneousOrderMapperThrowExceptionAsSourceType.class }) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -59,7 +56,7 @@ public void shouldRaiseErrorWhenThrowExceptionUsedAsSourceType() { } @IssueKey("2339") - @Test + @ProcessorTest @WithClasses({OrderThrowExceptionMapper.class, OrderDto.class}) public void shouldIgnoreThrowExceptionWhenInverseValueMappings() { @@ -68,7 +65,7 @@ public void shouldIgnoreThrowExceptionWhenInverseValueMappings() { } @IssueKey("2339") - @Test + @ProcessorTest @WithClasses({SpecialThrowExceptionMapper.class, OrderDto.class}) public void shouldBeAbleToMapAnyRemainingToThrowException() { @@ -80,7 +77,7 @@ public void shouldBeAbleToMapAnyRemainingToThrowException() { } @IssueKey("2339") - @Test + @ProcessorTest @WithClasses({SpecialThrowExceptionMapper.class, OrderDto.class}) public void shouldBeAbleToMapNullToThrowException() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java index 145721b83d..cdf0fd8e99 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/enum2string/EnumToStringMappingTest.java @@ -5,24 +5,21 @@ */ package org.mapstruct.ap.test.value.enum2string; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; @IssueKey( "1557" ) @WithClasses({ OrderType.class, OrderMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class EnumToStringMappingTest { - @Test + @ProcessorTest public void testTheNormalStuff() { assertThat( OrderMapper.INSTANCE.mapNormal( null ) ).isNull(); assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.EXTRA ) ).isEqualTo( "SPECIAL" ); @@ -32,7 +29,7 @@ public void testTheNormalStuff() { assertThat( OrderMapper.INSTANCE.mapNormal( OrderType.RETAIL ) ).isEqualTo( "RETAIL" ); } - @Test + @ProcessorTest public void testRemainingAndNull() { assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( null ) ).isEqualTo( "DEFAULT" ); assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.STANDARD ) ).isNull(); @@ -42,7 +39,7 @@ public void testRemainingAndNull() { assertThat( OrderMapper.INSTANCE.withAnyUnmappedAndNull( OrderType.EXTRA ) ).isEqualTo( "SPECIAL" ); } - @Test + @ProcessorTest @WithClasses(ErroneousOrderMapperUsingAnyRemaining.class) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/ErroneousEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/ErroneousEnumMappingTest.java index 45d374f217..424947fd98 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/ErroneousEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/erroneous/ErroneousEnumMappingTest.java @@ -5,23 +5,20 @@ */ package org.mapstruct.ap.test.value.erroneous; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.value.CustomIllegalArgumentException; import org.mapstruct.ap.test.value.ExternalOrderType; import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; /** * @author Filip Hrisafov */ @IssueKey("2169") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ CustomIllegalArgumentException.class, ExternalOrderType.class, @@ -29,7 +26,7 @@ }) public class ErroneousEnumMappingTest { - @Test + @ProcessorTest @WithClasses({ EmptyEnumMappingMapper.class }) @@ -46,7 +43,7 @@ public class ErroneousEnumMappingTest { public void shouldCompileErrorWhenEnumMappingIsEmpty() { } - @Test + @ProcessorTest @WithClasses({ NoConfigurationEnumMappingMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionTest.java index fbbf702854..76ef4866c2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/exception/CustomUnexpectedValueMappingExceptionTest.java @@ -5,22 +5,19 @@ */ package org.mapstruct.ap.test.value.exception; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.value.CustomIllegalArgumentException; import org.mapstruct.ap.test.value.ExternalOrderType; import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; /** * @author Filip Hrisafov */ @IssueKey("2169") -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ Config.class, CustomUnexpectedValueMappingExceptionDefinedInMapper.class, @@ -33,10 +30,10 @@ }) public class CustomUnexpectedValueMappingExceptionTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest public void shouldGenerateCustomUnexpectedValueMappingException() { generatedSource.addComparisonToFixtureFor( CustomUnexpectedValueMappingExceptionDefinedInMapper.class, diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java index d5de5e872f..c27128edb6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.value.nametransformation; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ CheeseType.class, CheeseTypeSuffixed.class, @@ -28,7 +25,7 @@ }) public class EnumNameTransformationStrategyTest { - @Test + @ProcessorTest @WithClasses({ CheeseSuffixMapper.class }) @@ -43,7 +40,7 @@ public void shouldApplySuffixAndStripSuffixOnEnumToEnumMapping() { .isEqualTo( CheeseType.BRIE ); } - @Test + @ProcessorTest @WithClasses({ CheesePrefixMapper.class }) @@ -58,7 +55,7 @@ public void shouldApplyPrefixAndStripPrefixOnEnumToEnumMapping() { .isEqualTo( CheeseType.BRIE ); } - @Test + @ProcessorTest @WithClasses({ CheeseEnumToStringSuffixMapper.class }) @@ -71,7 +68,7 @@ public void shouldApplySuffixAndStripSuffixOnEnumToStringMapping() { assertThat( mapper.mapStripSuffix( "DEFAULT" ) ).isEqualTo( CheeseTypeSuffixed.DEFAULT ); } - @Test + @ProcessorTest @WithClasses({ CheeseEnumToStringPrefixMapper.class }) @@ -84,7 +81,7 @@ public void shouldApplyPrefixAndStripPrefixOnEnumToStringMapping() { assertThat( mapper.mapStripPrefix( "DEFAULT" ) ).isEqualTo( CheeseTypePrefixed.DEFAULT ); } - @Test + @ProcessorTest @WithClasses({ ErroneousNameTransformStrategyMapper.class }) @@ -102,7 +99,7 @@ public void shouldApplyPrefixAndStripPrefixOnEnumToStringMapping() { public void shouldGiveCompileErrorWhenUsingUnknownNameTransformStrategy() { } - @Test + @ProcessorTest @WithClasses({ ErroneousNameTransformStrategyMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java index 313a7553bb..e4cc708838 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomEnumMappingStrategyTest.java @@ -5,14 +5,12 @@ */ package org.mapstruct.ap.test.value.spi; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.value.CustomIllegalArgumentException; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; @@ -21,7 +19,6 @@ /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ CheeseType.class, CustomCheeseType.class, @@ -33,10 +30,10 @@ @WithServiceImplementation(CustomEnumMappingStrategy.class) public class CustomEnumMappingStrategyTest { - @Rule - public final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Test + @ProcessorTest @WithClasses({ CustomCheeseMapper.class }) @@ -82,7 +79,7 @@ public void shouldApplyCustomEnumMappingStrategy() { assertThat( mapper.mapStringToCustom( "UNKNOWN" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); } - @Test + @ProcessorTest @WithClasses({ OverridesCustomCheeseMapper.class }) @@ -127,7 +124,7 @@ public void shouldApplyDefinedMappingsInsteadOfCustomEnumMappingStrategy() { assertThat( mapper.mapStringToCustom( "UNKNOWN" ) ).isEqualTo( CustomCheeseType.CUSTOM_BRIE ); } - @Test + @ProcessorTest @IssueKey("2339") @WithClasses({ CustomThrowingCheeseMapper.class diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategyTest.java index 9cb1337bc8..840c659430 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/spi/CustomErroneousEnumMappingStrategyTest.java @@ -5,21 +5,18 @@ */ package org.mapstruct.ap.test.value.spi; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Filip Hrisafov */ -@RunWith(AnnotationProcessorTestRunner.class) @WithClasses({ CheeseType.class, CustomCheeseType.class, @@ -28,7 +25,7 @@ @WithServiceImplementation(CustomErroneousEnumMappingStrategy.class) public class CustomErroneousEnumMappingStrategyTest { - @Test + @ProcessorTest @WithClasses({ CustomCheeseMapper.class }) @@ -55,7 +52,7 @@ public class CustomErroneousEnumMappingStrategyTest { public void shouldThrowCompileErrorWhenDefaultEnumDoesNotExist() { } - @Test + @ProcessorTest @WithClasses({ OverridesCustomCheeseMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java index fea8efac12..7642887aae 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/string2enum/StringToEnumMappingTest.java @@ -5,25 +5,22 @@ */ package org.mapstruct.ap.test.value.string2enum; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.test.value.OrderType; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @IssueKey( "1557" ) @WithClasses({ OrderType.class, OrderMapper.class }) -@RunWith(AnnotationProcessorTestRunner.class) public class StringToEnumMappingTest { - @Test + @ProcessorTest public void testTheNormalStuff() { assertThat( OrderMapper.INSTANCE.mapNormal( null ) ).isNull(); assertThat( OrderMapper.INSTANCE.mapNormal( "SPECIAL" ) ).isEqualTo( OrderType.EXTRA ); @@ -35,14 +32,14 @@ public void testTheNormalStuff() { assertThat( OrderMapper.INSTANCE.mapNormal( "blah" ) ).isEqualTo( OrderType.RETAIL ); } - @Test + @ProcessorTest public void testRemainingAndNull() { assertThat( OrderMapper.INSTANCE.mapWithAnyUnmapped( null ) ).isEqualTo( OrderType.STANDARD ); assertThat( OrderMapper.INSTANCE.mapWithAnyUnmapped( "DEFAULT" ) ).isNull(); assertThat( OrderMapper.INSTANCE.mapWithAnyUnmapped( "BLAH" ) ).isEqualTo( OrderType.RETAIL ); } - @Test + @ProcessorTest @WithClasses(ErroneousOrderMapperUsingNoAnyRemainingAndNoAnyUnmapped.class) @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java b/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java index fd17eed070..ac7add9d80 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/verbose/VerboseTest.java @@ -5,15 +5,13 @@ */ package org.mapstruct.ap.test.verbose; -import org.junit.Test; -import org.junit.runner.RunWith; import org.mapstruct.ap.spi.AccessorNamingStrategy; import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; import org.mapstruct.ap.spi.ImmutablesBuilderProvider; - import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; @@ -21,19 +19,12 @@ import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedNote; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.Compiler; -import org.mapstruct.ap.testutil.runner.DisabledOnCompiler; @IssueKey("37") -@RunWith(AnnotationProcessorTestRunner.class) public class VerboseTest { - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses({ CreateBeanMapping.class, CreateBeanMappingConfig.class }) @ExpectedNote("^MapStruct: Using accessor naming strategy:.*DefaultAccessorNamingStrategy.*$") @@ -43,11 +34,7 @@ public class VerboseTest { public void testGeneralMessages() { } - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @WithServiceImplementation(provides = BuilderProvider.class, value = ImmutablesBuilderProvider.class) @WithServiceImplementation(provides = AccessorNamingStrategy.class, value = ImmutablesAccessorNamingStrategy.class) @ProcessorOption(name = "mapstruct.verbose", value = "true") @@ -59,11 +46,7 @@ public void testGeneralMessages() { public void testGeneralWithOtherSPI() { } - @Test - @DisabledOnCompiler({ - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - }) + @ProcessorTest(Compiler.JDK) @WithServiceImplementation(provides = AstModifyingAnnotationProcessor.class, value = AstModifyingAnnotationProcessorSaysNo.class) @ProcessorOption(name = "mapstruct.verbose", value = "true") @@ -83,11 +66,7 @@ public void testGeneralWithOtherSPI() { public void testDeferred() { } - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses({ CreateBeanMapping.class, CreateBeanMappingConfig.class }) @ExpectedNote("^- MapStruct: creating bean mapping method implementation for.*$") @@ -95,11 +74,7 @@ public void testDeferred() { public void testCreateBeanMapping() { } - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(SelectBeanMapping.class) @ExpectedNote("^- MapStruct: creating bean mapping method implementation for.*$") @@ -107,22 +82,14 @@ public void testCreateBeanMapping() { public void testSelectBeanMapping() { } - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(ValueMapping.class) @ExpectedNote("^- MapStruct: creating value mapping method implementation for.*$") public void testValueMapping() { } - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(CreateIterableMapping.class) @ExpectedNote("^- MapStruct: creating iterable mapping method implementation for.*$") @@ -130,11 +97,7 @@ public void testValueMapping() { public void testVerboseCreateIterableMapping() { } - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(SelectIterableMapping.class) @ExpectedNote("^- MapStruct: creating iterable mapping method implementation for.*$") @@ -142,22 +105,14 @@ public void testVerboseCreateIterableMapping() { public void testVerboseSelectingIterableMapping() { } - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(SelectStreamMapping.class) @ExpectedNote("^- MapStruct: creating stream mapping method implementation for.*$") public void testVerboseSelectingStreamMapping() { } - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(CreateMapMapping.class) @ExpectedNote("^- MapStruct: creating map mapping method implementation for.*$") @@ -166,11 +121,7 @@ public void testVerboseSelectingStreamMapping() { public void testVerboseCreateMapMapping() { } - @Test - @DisabledOnCompiler( { - Compiler.ECLIPSE, - Compiler.ECLIPSE11 - } ) + @ProcessorTest(Compiler.JDK) @ProcessorOption(name = "mapstruct.verbose", value = "true") @WithClasses(SelectMapMapping.class) @ExpectedNote("^- MapStruct: creating map mapping method implementation for.*$") diff --git a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java index 67c716468c..1eec91b951 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java @@ -5,32 +5,25 @@ */ package org.mapstruct.ap.test.versioninfo; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOptions; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; import org.mapstruct.ap.testutil.runner.GeneratedSource; /** * @author Andreas Gudian * */ -@RunWith( AnnotationProcessorTestRunner.class ) @IssueKey( "424" ) @WithClasses( SimpleMapper.class ) public class VersionInfoTest { - private final GeneratedSource generatedSource = new GeneratedSource(); + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); - @Rule - public GeneratedSource getGeneratedSource() { - return generatedSource; - } - - @Test + @ProcessorTest @ProcessorOption( name = "mapstruct.suppressGeneratorVersionInfoComment", value = "true" ) public void includesNoComment() { generatedSource.forMapper( SimpleMapper.class ).content() @@ -38,7 +31,7 @@ public void includesNoComment() { .doesNotContain( "comments = \"version: " ); } - @Test + @ProcessorTest @ProcessorOptions( { @ProcessorOption( name = "mapstruct.suppressGeneratorVersionInfoComment", value = "true" ), @ProcessorOption( name = "mapstruct.suppressGeneratorTimestamp", value = "true" ) @@ -49,7 +42,7 @@ public void includesNoCommentAndNoTimestamp() { .doesNotContain( "comments = \"version: " ); } - @Test + @ProcessorTest public void includesCommentAndTimestamp() { generatedSource.forMapper( SimpleMapper.class ).content() .contains( "date = \"" ) From 627be5308855aa257826beeaccf570390de195f0 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 25 Apr 2021 12:39:04 +0200 Subject: [PATCH 160/597] Migrate mapstruct core tests to JUnit Jupiter --- core/pom.xml | 4 ++-- core/src/test/java/org/mapstruct/factory/MappersTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index b02b809ed7..10c95cf3b5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -22,8 +22,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/core/src/test/java/org/mapstruct/factory/MappersTest.java b/core/src/test/java/org/mapstruct/factory/MappersTest.java index 60371cada8..6118ba09cb 100644 --- a/core/src/test/java/org/mapstruct/factory/MappersTest.java +++ b/core/src/test/java/org/mapstruct/factory/MappersTest.java @@ -7,7 +7,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.test.model.Foo; import org.mapstruct.test.model.SomeClass; From 0cb053df8d0d8cf3f1370f6afa76c98c5c62b3ce Mon Sep 17 00:00:00 2001 From: "jason.bodnar@blackbaud.com" Date: Tue, 23 Mar 2021 13:40:30 -0500 Subject: [PATCH 161/597] #2391 Add implicit conversion between UUID <-> String --- .../chapter-5-data-type-conversions.asciidoc | 7 ++- .../internal/conversion/ConversionUtils.java | 12 ++++ .../ap/internal/conversion/Conversions.java | 3 + .../conversion/UUIDToStringConversion.java | 37 ++++++++++++ .../conversion/uuid/UUIDConversionTest.java | 56 +++++++++++++++++++ .../ap/test/conversion/uuid/UUIDMapper.java | 19 +++++++ .../ap/test/conversion/uuid/UUIDSource.java | 33 +++++++++++ .../ap/test/conversion/uuid/UUIDTarget.java | 30 ++++++++++ 8 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/UUIDToStringConversion.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDTarget.java diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 7800b6a933..24f019fa63 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -115,7 +115,10 @@ public interface CarMapper { * When converting from a `String`, omitting `Mapping#dateFormat`, it leads to usage of the default pattern and date format symbols for the default locale. An exception to this rule is `XmlGregorianCalendar` which results in parsing the `String` according to http://www.w3.org/TR/xmlschema-2/#dateTime[XML Schema 1.0 Part 2, Section 3.2.7-14.1, Lexical Representation]. * Between `java.util.Currency` and `String`. -** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/ISO_4217[ISO-4217] alphabetic code otherwise an `IllegalArgumentException` is thrown +** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/ISO_4217[ISO-4217] alphabetic code otherwise an `IllegalArgumentException` is thrown. + +* Between `java.util.UUID` and `String`. +** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/Universally_unique_identifier[UUID] otherwise an `IllegalArgumentException` is thrown. * Between `String` and `StringBuilder` @@ -326,7 +329,7 @@ public abstract class FishTankMapperWithVolume { ---- ==== -Note the `@Mapping` annotation where `source` field is equal to `"source"`, indicating the parameter name `source` itself in the method `map(FishTank source)` instead of a (target) property in `FishTank`. +Note the `@Mapping` annotation where `source` field is equal to `"source"`, indicating the parameter name `source` itself in the method `map(FishTank source)` instead of a (target) property in `FishTank`. [[invoking-other-mappers]] diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java index 2afcf3c6ad..20fb8a2fee 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java @@ -18,6 +18,7 @@ import java.time.format.DateTimeFormatter; import java.util.Currency; import java.util.Locale; +import java.util.UUID; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.util.JodaTimeConstants; @@ -242,4 +243,15 @@ public static String dateTimeFormat(ConversionContext conversionContext) { public static String stringBuilder(ConversionContext conversionContext) { return typeReferenceName( conversionContext, StringBuilder.class ); } + + /** + * Name for {@link java.util.UUID}. + * + * @param conversionContext Conversion context + * + * @return Name or fully-qualified name. + */ + public static String uuid(ConversionContext conversionContext) { + return typeReferenceName( conversionContext, UUID.class ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 02c478bb70..4b3ab2a818 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.UUID; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; @@ -190,6 +191,8 @@ public Conversions(TypeFactory typeFactory) { // java.util.Currency <~> String register( Currency.class, String.class, new CurrencyToStringConversion() ); + + register( UUID.class, String.class, new UUIDToStringConversion() ); } private void registerJodaConversions() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/UUIDToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/UUIDToStringConversion.java new file mode 100644 index 0000000000..b07d799387 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/UUIDToStringConversion.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.internal.conversion; + +import java.util.Set; +import java.util.UUID; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Collections; + +import static org.mapstruct.ap.internal.conversion.ConversionUtils.uuid; + +/** + * Conversion between {@link java.util.UUID} and {@link String}. + * + * @author Jason Bodnar + */ +public class UUIDToStringConversion extends SimpleConversion { + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toString()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return uuid( conversionContext ) + ".fromString( )"; + } + + @Override + protected Set getFromConversionImportTypes(final ConversionContext conversionContext) { + return Collections.asSet( conversionContext.getTypeFactory().getType( UUID.class ) ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDConversionTest.java new file mode 100644 index 0000000000..51eab29cda --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDConversionTest.java @@ -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.conversion.uuid; + +import java.util.UUID; + +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; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Tests conversions between {@link java.util.UUID} and String. + * + * @author Jason Bodnar + */ +@IssueKey("2391") +@WithClasses({ UUIDSource.class, UUIDTarget.class, UUIDMapper.class }) +public class UUIDConversionTest { + + @ProcessorTest + public void shouldApplyUUIDConversion() { + UUIDSource source = new UUIDSource(); + source.setUUIDA( UUID.randomUUID() ); + + UUIDTarget target = UUIDMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getUUIDA() ).isEqualTo( source.getUUIDA().toString() ); + } + + @ProcessorTest + public void shouldApplyReverseUUIDConversion() { + UUIDTarget target = new UUIDTarget(); + target.setUUIDA( UUID.randomUUID().toString() ); + + UUIDSource source = UUIDMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getUUIDA() ).isEqualTo( UUID.fromString( target.getUUIDA() ) ); + } + + @ProcessorTest + public void shouldHandleInvalidUUIDString() { + UUIDTarget target = new UUIDTarget(); + target.setInvalidUUID( "XXXXXXXXX" ); + + assertThatThrownBy( () -> UUIDMapper.INSTANCE.targetToSource( target ) ) + .isInstanceOf( IllegalArgumentException.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDMapper.java new file mode 100644 index 0000000000..8430b0fdce --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDMapper.java @@ -0,0 +1,19 @@ +/* + * 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.conversion.uuid; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface UUIDMapper { + + UUIDMapper INSTANCE = Mappers.getMapper( UUIDMapper.class ); + + UUIDTarget sourceToTarget(UUIDSource source); + + UUIDSource targetToSource(UUIDTarget target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDSource.java new file mode 100644 index 0000000000..220ce5a76a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDSource.java @@ -0,0 +1,33 @@ +/* + * 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.conversion.uuid; + +import java.util.UUID; + +/** + * @author Jason Bodnar + */ +public class UUIDSource { + private UUID uuidA; + + private UUID invalidUUID; + + public UUID getUUIDA() { + return this.uuidA; + } + + public void setUUIDA(final UUID uuidA) { + this.uuidA = uuidA; + } + + public UUID getInvalidUUID() { + return invalidUUID; + } + + public void setInvalidUUID(final UUID invalidUUID) { + this.invalidUUID = invalidUUID; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDTarget.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDTarget.java new file mode 100644 index 0000000000..4fd2558141 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDTarget.java @@ -0,0 +1,30 @@ +/* + * 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.conversion.uuid; + +/** + * @author Jason Bodnar + */ +public class UUIDTarget { + private String uuidA; + private String invalidUUID; + + public String getUUIDA() { + return this.uuidA; + } + + public void setUUIDA(final String uuidA) { + this.uuidA = uuidA; + } + + public String getInvalidUUID() { + return this.invalidUUID; + } + + public void setInvalidUUID(final String invalidUUID) { + this.invalidUUID = invalidUUID; + } +} From 5c22eee6c327aa893109f098d5a7cc2b5976cdc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 20:09:14 +0000 Subject: [PATCH 162/597] Bump commons-io from 2.6 to 2.7 in /parent Bumps commons-io from 2.6 to 2.7. Signed-off-by: dependabot[bot] --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index c7ac37a41d..c1172cbfe0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -261,7 +261,7 @@ commons-io commons-io - 2.6 + 2.7 From fdf3dcc8efaf0cdc1a2145dd53c1302346e2838a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Bassinello?= Date: Thu, 6 May 2021 15:13:13 -0300 Subject: [PATCH 163/597] #2445 Support for case changing enum transformation strategy Available case transformations: upper, lower, capital --- .../main/java/org/mapstruct/EnumMapping.java | 12 ++++ .../java/org/mapstruct/MappingConstants.java | 8 +++ .../chapter-8-mapping-values.asciidoc | 4 ++ .../ap/internal/gem/MappingConstantsGem.java | 2 + .../spi/CaseEnumTransformationStrategy.java | 59 +++++++++++++++ ...apstruct.ap.spi.EnumTransformationStrategy | 1 + .../mapstruct/ap/test/gem/ConstantTest.java | 1 + .../nametransformation/CheeseCaseMapper.java | 57 +++++++++++++++ .../value/nametransformation/CheeseType.java | 3 +- .../nametransformation/CheeseTypeCapital.java | 16 +++++ .../CheeseTypeCustomSuffix.java | 1 + .../nametransformation/CheeseTypeLower.java | 16 +++++ .../CheeseTypePrefixed.java | 1 + .../CheeseTypeSuffixed.java | 1 + .../EnumNameTransformationStrategyTest.java | 72 ++++++++++++++++++- 15 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseCaseMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCapital.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeLower.java diff --git a/core/src/main/java/org/mapstruct/EnumMapping.java b/core/src/main/java/org/mapstruct/EnumMapping.java index 2f908579e8..ba3a5e0cb0 100644 --- a/core/src/main/java/org/mapstruct/EnumMapping.java +++ b/core/src/main/java/org/mapstruct/EnumMapping.java @@ -104,6 +104,18 @@ * prefix to the source enum *

    1. {@link MappingConstants#STRIP_PREFIX_TRANSFORMATION} - strips the given {@link #configuration()} from * the start of the source enum
    2. + *
    3. + * {@link MappingConstants#CASE_TRANSFORMATION} - applies the given {@link #configuration()} case + * transformation to the source enum. Supported configurations are: + *
        + *
      • upper - Performs upper case transformation to the source enum
      • + *
      • lower - Performs lower case transformation to the source enum
      • + *
      • + * capital - Performs capitalisation of the first character of every word in the source enum + * and everything else to lower case. A word is split by "_". + *
      • + *
      + *
    4. * * * It is possible to use custom name transformation strategies by implementing the {@code diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java index 53528f559e..002f005299 100644 --- a/core/src/main/java/org/mapstruct/MappingConstants.java +++ b/core/src/main/java/org/mapstruct/MappingConstants.java @@ -74,6 +74,14 @@ private MappingConstants() { */ public static final String STRIP_PREFIX_TRANSFORMATION = "stripPrefix"; + /** + * In an {@link EnumMapping} this represent the enum transformation strategy that applies case transformation + * at the source. + * + * @since 1.5 + */ + public static final String CASE_TRANSFORMATION = "case"; + /** * Specifies the component model constants to which the generated mapper should adhere. * It can be used with the annotation {@link Mapper#componentModel()} or {@link MapperConfig#componentModel()} diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index 57b9433a20..24c7324f1d 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -259,6 +259,10 @@ MapStruct provides the following out of the box enum name transformation strateg * _stripSuffix_ - Strips a suffix from the source enum * _prefix_ - Applies a prefix on the source enum * _stripPrefix_ - Strips a prefix from the source enum +* _case_ - Applies case transformation to the source enum. Supported _case_ transformations are: +** _upper_ - Performs upper case transformation to the source enum +** _lower_ - Performs lower case transformation to the source enum +** _capital_ - Performs capitalisation of the first character of every word in the source enum and everything else to lowercase. A word is split by "_" It is also possible to register custom strategies. For more information on how to do that have a look at <> diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java index be8f23b76d..b49b9d17e5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java @@ -31,6 +31,8 @@ private MappingConstantsGem() { public static final String STRIP_PREFIX_TRANSFORMATION = "stripPrefix"; + public static final String CASE_TRANSFORMATION = "case"; + /** * Gem for the class {@link org.mapstruct.MappingConstants.ComponentModel} * diff --git a/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java new file mode 100644 index 0000000000..2f99f56ce4 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.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.spi; + +import java.util.Arrays; +import java.util.Locale; +import java.util.stream.Collectors; + +/** + * Applies case transformation to the source enum + * + * @author jpbassinello + * @since 1.5 + */ +public class CaseEnumTransformationStrategy implements EnumTransformationStrategy { + + private static final String UPPER = "upper"; + private static final String LOWER = "lower"; + private static final String CAPITAL = "capital"; + + @Override + public String getStrategyName() { + return "case"; + } + + @Override + public String transform(String value, String configuration) { + switch ( configuration.toLowerCase() ) { + case UPPER: + return value.toUpperCase( Locale.ROOT ); + case LOWER: + return value.toLowerCase( Locale.ROOT ); + case CAPITAL: + return capitalize( value ); + default: + throw new IllegalArgumentException( + "Unexpected configuration for enum case transformation: " + configuration ); + } + } + + private static String capitalize(String value) { + return Arrays.stream( value.split( "_" ) ) + .map( CaseEnumTransformationStrategy::upperCaseFirst ) + .collect( Collectors.joining( "_" ) ); + } + + private static String upperCaseFirst(String value) { + char[] array = value.toCharArray(); + array[0] = Character.toUpperCase( array[0] ); + for ( int i = 1; i < array.length; i++ ) { + array[i] = Character.toLowerCase( array[i] ); + } + return new String( array ); + } + +} diff --git a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy index 1f52733ad9..264b82d744 100644 --- a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy +++ b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy @@ -6,3 +6,4 @@ org.mapstruct.ap.spi.PrefixEnumTransformationStrategy org.mapstruct.ap.spi.StripPrefixEnumTransformationStrategy org.mapstruct.ap.spi.StripSuffixEnumTransformationStrategy org.mapstruct.ap.spi.SuffixEnumTransformationStrategy +org.mapstruct.ap.spi.CaseEnumTransformationStrategy diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java index 87a22931ca..e3a5b7c264 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java @@ -30,6 +30,7 @@ public void constantsShouldBeEqual() { assertThat( MappingConstants.PREFIX_TRANSFORMATION ).isEqualTo( MappingConstantsGem.PREFIX_TRANSFORMATION ); assertThat( MappingConstants.STRIP_PREFIX_TRANSFORMATION ) .isEqualTo( MappingConstantsGem.STRIP_PREFIX_TRANSFORMATION ); + assertThat( MappingConstants.CASE_TRANSFORMATION ).isEqualTo( MappingConstantsGem.CASE_TRANSFORMATION ); } @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseCaseMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseCaseMapper.java new file mode 100644 index 0000000000..f29766be8f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseCaseMapper.java @@ -0,0 +1,57 @@ +/* + * 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.value.nametransformation; + +import org.mapstruct.EnumMapping; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.factory.Mappers; + +/** + * @author jpbassinello + */ +@Mapper +public interface CheeseCaseMapper { + + CheeseCaseMapper INSTANCE = Mappers.getMapper( CheeseCaseMapper.class ); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "lower") + CheeseTypeLower mapToLower(CheeseType cheese); + + @InheritInverseConfiguration + CheeseType mapToLowerInverse(CheeseTypeLower cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "lower") + CheeseTypeLower mapToLower(CheeseTypeCapital cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "upper") + CheeseType mapToUpper(CheeseTypeLower cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "upper") + CheeseType mapToUpper(CheeseTypeCapital cheese); + + @InheritInverseConfiguration(name = "mapToUpper") + CheeseTypeCapital mapToUpperInverse(CheeseType cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "capital") + CheeseTypeCapital mapToCapital(CheeseTypeLower cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "capital") + CheeseTypeCapital mapToCapital(CheeseType cheese); + + @InheritInverseConfiguration(name = "mapToCapital") + CheeseType mapToCapitalInverse(CheeseTypeCapital cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "lower") + String mapToLowerString(CheeseType cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "upper") + String mapToUpperString(CheeseType cheese); + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "capital") + String mapToCapitalString(CheeseType cheese); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java index d7a1d2e288..d3e9d55b12 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseType.java @@ -11,5 +11,6 @@ public enum CheeseType { BRIE, - ROQUEFORT + ROQUEFORT, + COLBY_JACK } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCapital.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCapital.java new file mode 100644 index 0000000000..d5d890dce0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCapital.java @@ -0,0 +1,16 @@ +/* + * 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.value.nametransformation; + +/** + * @author jpbassinello + */ +public enum CheeseTypeCapital { + + Brie, + Roquefort, + Colby_Jack +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java index b7eccd3d42..7249e8a929 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeCustomSuffix.java @@ -12,4 +12,5 @@ public enum CheeseTypeCustomSuffix { brie_TYPE, roquefort_TYPE, + colby_jack_TYPE } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeLower.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeLower.java new file mode 100644 index 0000000000..c6f064ec03 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeLower.java @@ -0,0 +1,16 @@ +/* + * 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.value.nametransformation; + +/** + * @author jpbassinello + */ +public enum CheeseTypeLower { + + brie, + roquefort, + colby_jack +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java index 5f5b989c7b..171b4e7d36 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypePrefixed.java @@ -13,4 +13,5 @@ public enum CheeseTypePrefixed { DEFAULT, SWISS_BRIE, SWISS_ROQUEFORT, + SWISS_COLBY_JACK } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java index b2b264b78b..06b019c123 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/CheeseTypeSuffixed.java @@ -13,4 +13,5 @@ public enum CheeseTypeSuffixed { DEFAULT, BRIE_CHEESE_TYPE, ROQUEFORT_CHEESE_TYPE, + COLBY_JACK_CHEESE_TYPE } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java index c27128edb6..d5c74dbe83 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java @@ -22,6 +22,8 @@ CheeseTypeSuffixed.class, CheeseTypePrefixed.class, CheeseTypeCustomSuffix.class, + CheeseTypeLower.class, + CheeseTypeCapital.class }) public class EnumNameTransformationStrategyTest { @@ -92,7 +94,7 @@ public void shouldApplyPrefixAndStripPrefixOnEnumToStringMapping() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 20, message = "There is no registered EnumTransformationStrategy for 'custom'. Registered strategies are:" + - " prefix, stripPrefix, stripSuffix, suffix." + " prefix, stripPrefix, stripSuffix, suffix, case." ) } ) @@ -109,4 +111,72 @@ public void shouldUseCustomEnumTransformationStrategy() { .isEqualTo( CheeseTypeCustomSuffix.brie_TYPE ); } + @ProcessorTest + @WithClasses({ + CheeseCaseMapper.class + }) + public void shouldConvertCaseOnEnumToEnumMapping() { + CheeseCaseMapper mapper = CheeseCaseMapper.INSTANCE; + + assertThat( mapper.mapToLower( CheeseType.BRIE ) ) + .isEqualTo( CheeseTypeLower.brie ); + + assertThat( mapper.mapToLowerInverse( CheeseTypeLower.brie ) ) + .isEqualTo( CheeseType.BRIE ); + + assertThat( mapper.mapToLower( CheeseTypeCapital.Colby_Jack ) ) + .isEqualTo( CheeseTypeLower.colby_jack ); + + assertThat( mapper.mapToUpper( CheeseTypeLower.roquefort ) ) + .isEqualTo( CheeseType.ROQUEFORT ); + + assertThat( mapper.mapToUpper( CheeseTypeCapital.Colby_Jack ) ) + .isEqualTo( CheeseType.COLBY_JACK ); + + assertThat( mapper.mapToUpperInverse( CheeseType.COLBY_JACK ) ) + .isEqualTo( CheeseTypeCapital.Colby_Jack ); + + assertThat( mapper.mapToCapital( CheeseTypeLower.brie ) ) + .isEqualTo( CheeseTypeCapital.Brie ); + + assertThat( mapper.mapToCapital( CheeseType.ROQUEFORT ) ) + .isEqualTo( CheeseTypeCapital.Roquefort ); + + assertThat( mapper.mapToCapital( CheeseType.COLBY_JACK ) ) + .isEqualTo( CheeseTypeCapital.Colby_Jack ); + + assertThat( mapper.mapToCapitalInverse( CheeseTypeCapital.Roquefort ) ) + .isEqualTo( CheeseType.ROQUEFORT ); + + assertThat( mapper.mapToCapitalInverse( CheeseTypeCapital.Colby_Jack ) ) + .isEqualTo( CheeseType.COLBY_JACK ); + + assertThat( mapper.mapToCapital( CheeseTypeLower.colby_jack ) ) + .isEqualTo( CheeseTypeCapital.Colby_Jack ); + + } + + @ProcessorTest + @WithClasses({ + CheeseCaseMapper.class + }) + public void shouldConvertCaseOnEnumToStringMapping() { + CheeseCaseMapper mapper = CheeseCaseMapper.INSTANCE; + + assertThat( mapper.mapToLowerString( CheeseType.BRIE ) ) + .isEqualTo( "brie" ); + assertThat( mapper.mapToLowerString( CheeseType.COLBY_JACK ) ) + .isEqualTo( "colby_jack" ); + + assertThat( mapper.mapToUpperString( CheeseType.ROQUEFORT ) ) + .isEqualTo( "ROQUEFORT" ); + assertThat( mapper.mapToUpperString( CheeseType.COLBY_JACK ) ) + .isEqualTo( "COLBY_JACK" ); + + assertThat( mapper.mapToCapitalString( CheeseType.ROQUEFORT ) ) + .isEqualTo( "Roquefort" ); + assertThat( mapper.mapToCapitalString( CheeseType.COLBY_JACK ) ) + .isEqualTo( "Colby_Jack" ); + + } } From 4576103752df896d4548c1d618ab892bf540b628 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 15 May 2021 11:37:27 +0200 Subject: [PATCH 164/597] #2445 Improve error reporting when EnumTransformationStrategy throws an error during transformation --- .../ap/internal/model/ValueMappingMethod.java | 28 ++++++++++++++++--- .../model/source/EnumMappingOptions.java | 6 ++++ .../mapstruct/ap/internal/util/Message.java | 1 + .../EnumNameTransformationStrategyTest.java | 18 ++++++++++++ .../ErroneousCaseTransformStrategyMapper.java | 20 +++++++++++++ 5 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/ErroneousCaseTransformStrategyMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 6e7fe3f0da..0d71ed5506 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -56,6 +56,7 @@ public static class Builder { private ValueMappings valueMappings; private EnumMappingOptions enumMapping; private EnumTransformationStrategyInvoker enumTransformationInvoker; + private boolean enumTransformationIllegalReported = false; public Builder mappingContext(MappingBuilderContext mappingContext) { this.ctx = mappingContext; @@ -144,6 +145,25 @@ private void initializeEnumTransformationStrategy() { } + private String transform(String source) { + try { + return enumTransformationInvoker.transform( source ); + } + catch ( IllegalArgumentException ex ) { + if ( !enumTransformationIllegalReported ) { + enumTransformationIllegalReported = true; + ctx.getMessager().printMessage( + method.getExecutable(), + enumMapping.getMirror(), + Message.ENUMMAPPING_ILLEGAL_TRANSFORMATION, + enumTransformationInvoker.transformationStrategy.getStrategyName(), + ex.getMessage() + ); + } + return source; + } + } + private List enumToEnumMapping(Method method, Type sourceType, Type targetType ) { List mappings = new ArrayList<>(); @@ -175,7 +195,7 @@ private List enumToEnumMapping(Method method, Type sourceType, Typ if ( enumMappingInverse ) { // If the mapping is inverse we have to change the target enum constant targetConstants.put( - enumTransformationInvoker.transform( targetNameEnum ), + transform( targetNameEnum ), targetEnumConstant ); } @@ -189,7 +209,7 @@ private List enumToEnumMapping(Method method, Type sourceType, Typ String sourceNameConstant = getEnumConstant( sourceTypeElement, sourceConstant ); String targetConstant; if ( !enumMappingInverse ) { - targetConstant = enumTransformationInvoker.transform( sourceNameConstant ); + targetConstant = transform( sourceNameConstant ); } else { targetConstant = sourceNameConstant; @@ -251,7 +271,7 @@ private List enumToStringMapping(Method method, Type sourceType ) // all remaining constants are mapped for ( String sourceConstant : unmappedSourceConstants ) { String sourceNameConstant = getEnumConstant( sourceTypeElement, sourceConstant ); - String targetConstant = enumTransformationInvoker.transform( sourceNameConstant ); + String targetConstant = transform( sourceNameConstant ); mappings.add( new MappingEntry( sourceConstant, targetConstant ) ); } } @@ -283,7 +303,7 @@ private List stringToEnumMapping(Method method, Type targetType ) // all remaining constants are mapped for ( String sourceConstant : unmappedSourceConstants ) { String sourceNameConstant = getEnumConstant( targetTypeElement, sourceConstant ); - String stringConstant = enumTransformationInvoker.transform( sourceNameConstant ); + String stringConstant = transform( sourceNameConstant ); if ( !mappedSources.contains( stringConstant ) ) { mappings.add( new MappingEntry( stringConstant, sourceConstant ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java index 86c415a4f1..85734c3083 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java @@ -6,6 +6,8 @@ package org.mapstruct.ap.internal.model.source; import java.util.Map; +import java.util.Optional; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.TypeMirror; @@ -64,6 +66,10 @@ public TypeMirror getUnexpectedValueMappingException() { return next().getUnexpectedValueMappingException(); } + public AnnotationMirror getMirror() { + return Optional.ofNullable( enumMapping ).map( EnumMappingGem::mirror ).orElse( null ); + } + public boolean isInverse() { return inverse; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 50aeb701d8..cb182af6b9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -110,6 +110,7 @@ public enum Message { ENUMMAPPING_INCORRECT_TRANSFORMATION_STRATEGY( "There is no registered EnumTransformationStrategy for '%s'. Registered strategies are: %s." ), ENUMMAPPING_MISSING_CONFIGURATION( "Configuration has to be defined when strategy is defined." ), ENUMMAPPING_NO_ELEMENTS( "'nameTransformationStrategy', 'configuration' and 'unexpectedValueMappingException' are undefined in @EnumMapping, define at least one of them." ), + ENUMMAPPING_ILLEGAL_TRANSFORMATION( "Illegal transformation for '%s' EnumTransformationStrategy. Error: '%s'." ), LIFECYCLEMETHOD_AMBIGUOUS_PARAMETERS( "Lifecycle method has multiple matching parameters (e. g. same type), in this case please ensure to name the parameters in the lifecycle and mapping method identical. This lifecycle method will not be used for the mapping method '%s'.", Diagnostic.Kind.WARNING), diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java index d5c74dbe83..7a29ae40a8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java @@ -101,6 +101,24 @@ public void shouldApplyPrefixAndStripPrefixOnEnumToStringMapping() { public void shouldGiveCompileErrorWhenUsingUnknownNameTransformStrategy() { } + @ProcessorTest + @WithClasses({ + ErroneousCaseTransformStrategyMapper.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + type = ErroneousCaseTransformStrategyMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 18, + message = "Illegal transformation for 'case' EnumTransformationStrategy." + + " Error: 'Unexpected configuration for enum case transformation: unknown'." + ) + } + ) + public void shouldGiveCompileErrorWhenUsingUnknownConfigurationForCaseTransformStrategy() { + } + @ProcessorTest @WithClasses({ ErroneousNameTransformStrategyMapper.class diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/ErroneousCaseTransformStrategyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/ErroneousCaseTransformStrategyMapper.java new file mode 100644 index 0000000000..fe8357a8a6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/ErroneousCaseTransformStrategyMapper.java @@ -0,0 +1,20 @@ +/* + * 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.value.nametransformation; + +import org.mapstruct.EnumMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousCaseTransformStrategyMapper { + + @EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "unknown") + CheeseType map(CheeseType cheese); +} From cc1562c5adacd09afcfd547b3bc340be95c953bd Mon Sep 17 00:00:00 2001 From: Lukas Lazar Date: Wed, 14 Apr 2021 14:39:22 +0200 Subject: [PATCH 165/597] #2132 Add unmappedTargetPolicy to @BeanMapping --- .../main/java/org/mapstruct/BeanMapping.java | 11 ++++++ .../main/asciidoc/chapter-2-set-up.asciidoc | 1 + .../ap/internal/model/BeanMappingMethod.java | 3 ++ .../model/source/BeanMappingOptions.java | 12 +++++++ .../BeanMappingSourceTargetMapper.java | 20 +++++++++++ ...usBeanMappingStrictSourceTargetMapper.java | 23 +++++++++++++ .../unmappedtarget/UnmappedProductTest.java | 34 +++++++++++++++++++ 7 files changed, 104 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/BeanMappingSourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/ErroneousBeanMappingStrictSourceTargetMapper.java diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java index 329d7bd50e..f8c749fbde 100644 --- a/core/src/main/java/org/mapstruct/BeanMapping.java +++ b/core/src/main/java/org/mapstruct/BeanMapping.java @@ -140,6 +140,17 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() */ String[] ignoreUnmappedSourceProperties() default {}; + /** + * How unmapped properties of the target type of a mapping should be reported. + * If no policy is configured, the policy given via {@link MapperConfig#unmappedTargetPolicy()} or + * {@link Mapper#unmappedTargetPolicy()} will be applied, using {@link ReportingPolicy#WARN} by default. + * + * @return The reporting policy for unmapped target properties. + * + * @since 1.5 + */ + ReportingPolicy unmappedTargetPolicy() default ReportingPolicy.WARN; + /** * The information that should be used for the builder mappings. This can be used to define custom build methods * for the builder strategy that one uses. diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index fd27b25e42..d26be72156 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -245,6 +245,7 @@ Supported values are: * `IGNORE`: unmapped target properties are ignored If a policy is given for a specific mapper via `@Mapper#unmappedTargetPolicy()`, the value from the annotation takes precedence. +If a policy is given for a specific bean mapping via `@BeanMapping#unmappedTargetPolicy()`, it takes precedence over both `@Mapper#unmappedTargetPolicy()` and the option. |`WARN` |=== diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 2e82366f8b..324df63cc8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1390,6 +1390,9 @@ private ReportingPolicyGem getUnmappedTargetPolicy() { if ( mappingReferences.isForForgedMethods() ) { return ReportingPolicyGem.IGNORE; } + if ( method.getOptions().getBeanMapping() != null ) { + return method.getOptions().getBeanMapping().unmappedTargetPolicy(); + } return method.getOptions().getMapper().unmappedTargetPolicy(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index d600ed0fc0..78800e6053 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -11,6 +11,8 @@ import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; + +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.TypeUtils; @@ -88,6 +90,7 @@ private static boolean isConsistent(BeanMappingGem gem, ExecutableElement method && !gem.nullValueCheckStrategy().hasValue() && !gem.nullValuePropertyMappingStrategy().hasValue() && !gem.nullValueMappingStrategy().hasValue() + && !gem.unmappedTargetPolicy().hasValue() && !gem.ignoreByDefault().hasValue() && !gem.builder().hasValue() ) { @@ -134,6 +137,15 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { .orElse( next().getNullValueMappingStrategy() ); } + @Override + public ReportingPolicyGem unmappedTargetPolicy() { + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::unmappedTargetPolicy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( ReportingPolicyGem::valueOf ) + .orElse( next().unmappedTargetPolicy() ); + } + @Override public BuilderGem getBuilder() { return Optional.ofNullable( beanMapping ).map( BeanMappingGem::builder ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/BeanMappingSourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/BeanMappingSourceTargetMapper.java new file mode 100644 index 0000000000..4f15e5d6eb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/BeanMappingSourceTargetMapper.java @@ -0,0 +1,20 @@ +/* + * 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.unmappedtarget; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface BeanMappingSourceTargetMapper { + + BeanMappingSourceTargetMapper INSTANCE = Mappers.getMapper( BeanMappingSourceTargetMapper.class ); + + @BeanMapping(unmappedTargetPolicy = ReportingPolicy.WARN) + Target sourceToTarget(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/ErroneousBeanMappingStrictSourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/ErroneousBeanMappingStrictSourceTargetMapper.java new file mode 100644 index 0000000000..77a946e634 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/ErroneousBeanMappingStrictSourceTargetMapper.java @@ -0,0 +1,23 @@ +/* + * 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.unmappedtarget; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousBeanMappingStrictSourceTargetMapper { + + ErroneousBeanMappingStrictSourceTargetMapper INSTANCE = + Mappers.getMapper( ErroneousBeanMappingStrictSourceTargetMapper.class ); + + @BeanMapping(unmappedTargetPolicy = ReportingPolicy.ERROR) + Target sourceToTarget(Source source); + + Source targetToSource(Target target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java index a36ec31436..bd36365413 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedtarget/UnmappedProductTest.java @@ -87,4 +87,38 @@ public void shouldRaiseErrorDueToUnsetTargetProperty() { ) public void shouldRaiseErrorDueToUnsetTargetPropertyWithPolicySetViaProcessorOption() { } + + @ProcessorTest + @IssueKey("2132") + @WithClasses({ Source.class, Target.class, ErroneousBeanMappingStrictSourceTargetMapper.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousBeanMappingStrictSourceTargetMapper.class, + kind = Kind.ERROR, + line = 20, + message = "Unmapped target property: \"bar\"."), + @Diagnostic(type = ErroneousBeanMappingStrictSourceTargetMapper.class, + kind = Kind.WARNING, + line = 22, + message = "Unmapped target property: \"qux\".") + } + ) + public void shouldRaiseErrorDueToUnsetTargetPropertyWithPolicySetViaBeanMapping() { + } + + @ProcessorTest + @IssueKey("2132") + @WithClasses({ Source.class, Target.class, BeanMappingSourceTargetMapper.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = BeanMappingSourceTargetMapper.class, + kind = Kind.WARNING, + line = 19, + message = "Unmapped target property: \"bar\".") + } + ) + public void shouldLeaveUnmappedTargetPropertyUnsetWithWarnPolicySetViaBeanMapping() { + } } From a6ac4f3fd699ec71e7f4b1fcbe0f6e8061c3e1cf Mon Sep 17 00:00:00 2001 From: Ewald volkert Date: Thu, 15 Apr 2021 07:26:06 +0200 Subject: [PATCH 166/597] #2329 use fields for custom date formats with DateTimeFormatter in order to optimise its usage * GetDateTimeFormatterField ** a FieldReference that creates DateTimeFormatters for given dateFormat as mapper fields ** variableName is created using given dateFormat * AbstractJavaTimeToStringConversion provides GetDateTimeFormatterField as required helper field using DateTimeFormatter instances provided as mapper fields by GetDateTimeFormatterField * ConversionProvider might provide supporting fields directly * Refactoring moved/renamed BuiltInFieldReference, BuiltInConstuctorFragment to package model/common * MappingBuilderContext provides access to mapper support fields (that are independent of mapper methods) * MappingResolverImpl / MapperCreationProcessor process supporting fields provided by ConversionProvider * HelperMethod ** extended to supply additional template parameters to be more flexible in freemarker templates ** extended to support mapper field reference / constructor fragment * SupportingMappingMethod ** provide templateParameters to freemarker ** hashCode/equals base on name property instead of template name, as we use one specific template multiple times with different parameters generating multiple methods ** #getSafeField extracted to SupportingField * SupportingField ** removed hashCode/equals based on template name, as we use one specific template multiple times with different parameters generating multiple methods (superclass equals/hashcode is fine for that) ** added support for template parameters to be more flexible when compiling templates * Tests to verify DateTimeFormatter instance field creation --- .../AbstractJavaTimeToStringConversion.java | 19 +- .../conversion/ConversionProvider.java | 14 +- .../conversion/GetDateTimeFormatterField.java | 55 ++ .../internal/model/MappingBuilderContext.java | 10 +- .../model/SupportingConstructorFragment.java | 28 +- .../ap/internal/model/SupportingField.java | 59 +- .../model/SupportingMappingMethod.java | 53 +- .../ConstructorFragment.java} | 7 +- .../internal/model/common/FieldReference.java | 33 + .../builtin => common}/FinalField.java | 6 +- .../AbstractToXmlGregorianCalendar.java | 12 +- .../source/builtin/BuiltInFieldReference.java | 27 - .../model/source/builtin/BuiltInMethod.java | 11 +- ...NewDatatypeFactoryConstructorFragment.java | 4 +- .../processor/MapperCreationProcessor.java | 20 +- .../creation/MappingResolverImpl.java | 29 +- .../conversion/GetDateTimeFormatterField.ftl | 9 + .../{source/builtin => common}/FinalField.ftl | 0 ...PatternDateTimeFormatterGeneratedTest.java | 53 ++ .../java8time/Java8TimeConversionTest.java | 5 + .../Source.java | 40 ++ .../SourceTargetMapper.java | 29 + .../Target.java | 38 ++ .../java8time/SourceTargetMapperImpl.java | 640 ++++++++++++++++++ .../SourceTargetMapperImpl.java | 63 ++ 25 files changed, 1130 insertions(+), 134 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/GetDateTimeFormatterField.java rename processor/src/main/java/org/mapstruct/ap/internal/model/{source/builtin/BuiltInConstructorFragment.java => common/ConstructorFragment.java} (58%) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/common/FieldReference.java rename processor/src/main/java/org/mapstruct/ap/internal/model/{source/builtin => common}/FinalField.java (77%) delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInFieldReference.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/conversion/GetDateTimeFormatterField.ftl rename processor/src/main/resources/org/mapstruct/ap/internal/model/{source/builtin => common}/FinalField.ftl (100%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8CustomPatternDateTimeFormatterGeneratedTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/SourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/Target.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/SourceTargetMapperImpl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractJavaTimeToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractJavaTimeToStringConversion.java index e903c00e59..2f530618c9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractJavaTimeToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractJavaTimeToStringConversion.java @@ -6,9 +6,11 @@ package org.mapstruct.ap.internal.conversion; import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Set; import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.FieldReference; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.internal.util.Strings; @@ -38,10 +40,8 @@ protected String getToExpression(ConversionContext conversionContext) { } private String dateTimeFormatter(ConversionContext conversionContext) { - if ( !Strings.isEmpty( conversionContext.getDateFormat() ) ) { - return ConversionUtils.dateTimeFormatter( conversionContext ) - + ".ofPattern( \"" + conversionContext.getDateFormat() - + "\" )"; + if ( Strings.isNotEmpty( conversionContext.getDateFormat() ) ) { + return GetDateTimeFormatterField.getDateTimeFormatterFieldName( conversionContext.getDateFormat() ); } else { return ConversionUtils.dateTimeFormatter( conversionContext ) + "." + defaultFormatterSuffix(); @@ -88,4 +88,15 @@ protected Set getFromConversionImportTypes(ConversionContext conversionCon return Collections.asSet( conversionContext.getTargetType() ); } + @Override + public List getRequiredHelperFields(ConversionContext conversionContext) { + if ( Strings.isNotEmpty( conversionContext.getDateFormat() ) ) { + return java.util.Collections.singletonList( + new GetDateTimeFormatterField( + conversionContext.getTypeFactory(), + conversionContext.getDateFormat() ) ); + } + + return super.getRequiredHelperFields( conversionContext ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java index 2ff73d3485..31d4d27585 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java @@ -5,11 +5,14 @@ */ package org.mapstruct.ap.internal.conversion; +import java.util.Collections; import java.util.List; + +import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.TypeConversion; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; -import org.mapstruct.ap.internal.model.HelperMethod; +import org.mapstruct.ap.internal.model.common.FieldReference; /** * Implementations create inline {@link TypeConversion}s such as @@ -49,4 +52,13 @@ public interface ConversionProvider { */ List getRequiredHelperMethods(ConversionContext conversionContext); + /** + * @param conversionContext ConversionContext providing optional information required for creating the conversion. + * + * @return any fields when required. + */ + default List getRequiredHelperFields(ConversionContext conversionContext) { + return Collections.emptyList(); + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/GetDateTimeFormatterField.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/GetDateTimeFormatterField.java new file mode 100644 index 0000000000..052f2e7224 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/GetDateTimeFormatterField.java @@ -0,0 +1,55 @@ +/* + * 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.internal.conversion; + +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.ap.internal.model.common.FieldReference; +import org.mapstruct.ap.internal.model.common.FinalField; +import org.mapstruct.ap.internal.model.common.TypeFactory; + +public class GetDateTimeFormatterField extends FinalField implements FieldReference { + + private final String dateFormat; + + public GetDateTimeFormatterField(TypeFactory typeFactory, String dateFormat) { + super( typeFactory.getType( DateTimeFormatter.class ), getDateTimeFormatterFieldName( dateFormat ) ); + this.dateFormat = dateFormat; + } + + @Override + public Map getTemplateParameter() { + Map parameter = new HashMap<>(); + parameter.put( "dateFormat", dateFormat ); + return parameter; + } + + public static String getDateTimeFormatterFieldName(String dateFormat) { + StringBuilder sb = new StringBuilder(); + sb.append( "dateTimeFormatter_" ); + + dateFormat.codePoints().forEach( cp -> { + if ( Character.isJavaIdentifierPart( cp ) ) { + // safe to character to method name as is + sb.append( Character.toChars( cp ) ); + } + else { + // could not be used in method name + sb.append( "_" ); + } + } ); + + sb.append( "_" ); + + int hashCode = dateFormat.hashCode(); + sb.append( hashCode < 0 ? "0" : "1" ); + sb.append( Math.abs( hashCode ) ); + + return sb.toString(); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index a9d4fd0918..fc82017a17 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -15,8 +15,6 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.TypeElement; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.FormattingParameters; @@ -28,8 +26,10 @@ import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.util.AccessorNamingUtils; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Services; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.spi.EnumMappingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.MappingExclusionProvider; @@ -101,6 +101,8 @@ Assignment getTargetAssignment(Method mappingMethod, ForgedMethodHistory descrip Supplier forger); Set getUsedSupportedMappings(); + + Set getUsedSupportedFields(); } private final TypeFactory typeFactory; @@ -241,6 +243,10 @@ public Set getUsedSupportedMappings() { return mappingResolver.getUsedSupportedMappings(); } + public Set getUsedSupportedFields() { + return mappingResolver.getUsedSupportedFields(); + } + /** * @param sourceType from which an automatic sub-mapping needs to be generated * @param targetType to which an automatic sub-mapping needs to be generated diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingConstructorFragment.java b/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingConstructorFragment.java index f25bbfca9f..936a9a51ba 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingConstructorFragment.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingConstructorFragment.java @@ -9,9 +9,9 @@ import java.util.Objects; import java.util.Set; +import org.mapstruct.ap.internal.model.common.ConstructorFragment; import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.source.builtin.BuiltInConstructorFragment; /** * A mapper instance field, initialized as null @@ -20,13 +20,15 @@ */ public class SupportingConstructorFragment extends ModelElement { + private final String variableName; private final String templateName; private final SupportingMappingMethod definingMethod; public SupportingConstructorFragment(SupportingMappingMethod definingMethod, - BuiltInConstructorFragment constructorFragment) { + ConstructorFragment constructorFragment, String variableName) { this.templateName = getTemplateNameForClass( constructorFragment.getClass() ); this.definingMethod = definingMethod; + this.variableName = variableName; } @Override @@ -43,10 +45,15 @@ public SupportingMappingMethod getDefiningMethod() { return definingMethod; } + public String getVariableName() { + return variableName; + } + @Override public int hashCode() { final int prime = 31; int result = 1; + result = prime * result + ( ( variableName == null ) ? 0 : variableName.hashCode() ); result = prime * result + ( ( templateName == null ) ? 0 : templateName.hashCode() ); return result; } @@ -64,10 +71,12 @@ public boolean equals(Object obj) { } SupportingConstructorFragment other = (SupportingConstructorFragment) obj; + if ( !Objects.equals( variableName, other.variableName ) ) { + return false; + } if ( !Objects.equals( templateName, other.templateName ) ) { return false; } - return true; } @@ -80,4 +89,17 @@ public static void addAllFragmentsIn(Set supportingMapp } } } + + public static SupportingConstructorFragment getSafeConstructorFragment(SupportingMappingMethod method, + ConstructorFragment fragment, + Field supportingField) { + if ( fragment == null ) { + return null; + } + + return new SupportingConstructorFragment( + method, + fragment, + supportingField != null ? supportingField.getVariableName() : null ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingField.java b/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingField.java index a92f4c9df4..caf6d6160e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingField.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingField.java @@ -5,10 +5,11 @@ */ package org.mapstruct.ap.internal.model; -import java.util.Objects; +import java.util.Map; import java.util.Set; -import org.mapstruct.ap.internal.model.source.builtin.BuiltInFieldReference; +import org.mapstruct.ap.internal.model.common.FieldReference; +import org.mapstruct.ap.internal.util.Strings; /** * supports the @@ -18,11 +19,14 @@ public class SupportingField extends Field { private final String templateName; + private final Map templateParameter; + private final SupportingMappingMethod definingMethod; - public SupportingField(SupportingMappingMethod definingMethod, BuiltInFieldReference fieldReference, String name) { + public SupportingField(SupportingMappingMethod definingMethod, FieldReference fieldReference, String name) { super( fieldReference.getType(), name, true ); this.templateName = getTemplateNameForClass( fieldReference.getClass() ); + this.templateParameter = fieldReference.getTemplateParameter(); this.definingMethod = definingMethod; } @@ -31,36 +35,12 @@ public String getTemplateName() { return templateName; } - public SupportingMappingMethod getDefiningMethod() { - return definingMethod; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ( ( templateName == null ) ? 0 : templateName.hashCode() ); - return result; + public Map getTemplateParameter() { + return templateParameter; } - @Override - public boolean equals(Object obj) { - if ( this == obj ) { - return true; - } - if ( obj == null ) { - return false; - } - if ( getClass() != obj.getClass() ) { - return false; - } - SupportingField other = (SupportingField) obj; - - if ( !Objects.equals( templateName, other.templateName ) ) { - return false; - } - - return true; + public SupportingMappingMethod getDefiningMethod() { + return definingMethod; } public static void addAllFieldsIn(Set supportingMappingMethods, Set targets) { @@ -71,4 +51,21 @@ public static void addAllFieldsIn(Set supportingMapping } } } + + public static Field getSafeField(SupportingMappingMethod method, FieldReference ref, Set existingFields) { + if ( ref == null ) { + return null; + } + + String name = ref.getVariableName(); + for ( Field existingField : existingFields ) { + if ( existingField.getVariableName().equals( ref.getVariableName() ) + && existingField.getType().equals( ref.getType() ) ) { + // field already exists, use that one + return existingField; + } + } + name = Strings.getSafeVariableName( name, Field.getFieldNames( existingFields ) ); + return new SupportingField( method, ref, name ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingMappingMethod.java index 756330e5d9..9428454c1f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingMappingMethod.java @@ -5,14 +5,13 @@ */ package org.mapstruct.ap.internal.model; +import java.util.Map; import java.util.Objects; import java.util.Set; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.source.builtin.BuiltInFieldReference; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod; import org.mapstruct.ap.internal.model.source.builtin.NewDatatypeFactoryConstructorFragment; -import org.mapstruct.ap.internal.util.Strings; /** * A mapping method which is not based on an actual method declared in the original mapper interface but is added as @@ -21,7 +20,7 @@ * Specific templates all point to this class, for instance: * {@link org.mapstruct.ap.internal.model.source.builtin.XmlGregorianCalendarToCalendar}, * but also used fields and constructor elements, e.g. - * {@link org.mapstruct.ap.internal.model.source.builtin.FinalField} and + * {@link org.mapstruct.ap.internal.model.common.FinalField} and * {@link NewDatatypeFactoryConstructorFragment} * * @author Gunnar Morling @@ -32,49 +31,25 @@ public class SupportingMappingMethod extends MappingMethod { private final Set importTypes; private final Field supportingField; private final SupportingConstructorFragment supportingConstructorFragment; + private final Map templateParameter; public SupportingMappingMethod(BuiltInMethod method, Set existingFields) { super( method ); this.importTypes = method.getImportTypes(); this.templateName = getTemplateNameForClass( method.getClass() ); - if ( method.getFieldReference() != null ) { - this.supportingField = getSafeField( method.getFieldReference(), existingFields ); - } - else { - this.supportingField = null; - } - if ( method.getConstructorFragment() != null ) { - this.supportingConstructorFragment = new SupportingConstructorFragment( - this, - method.getConstructorFragment() - ); - } - else { - this.supportingConstructorFragment = null; - } - } - - private Field getSafeField(BuiltInFieldReference ref, Set existingFields) { - String name = ref.getVariableName(); - for ( Field existingField : existingFields ) { - if ( existingField.getType().equals( ref.getType() ) ) { - // field type already exist, use that one - return existingField; - } - } - for ( Field existingField : existingFields ) { - if ( existingField.getVariableName().equals( ref.getVariableName() ) ) { - // field with name exist, however its a wrong type - name = Strings.getSafeVariableName( name, Field.getFieldNames( existingFields ) ); - } - } - return new SupportingField( this, ref, name ); + this.templateParameter = null; + this.supportingField = SupportingField.getSafeField( this, method.getFieldReference(), existingFields ); + this.supportingConstructorFragment = SupportingConstructorFragment.getSafeConstructorFragment( + this, + method.getConstructorFragment(), + this.supportingField ); } public SupportingMappingMethod(HelperMethod method) { super( method ); this.importTypes = method.getImportTypes(); this.templateName = getTemplateNameForClass( method.getClass() ); + this.templateParameter = null; this.supportingField = null; this.supportingConstructorFragment = null; } @@ -120,11 +95,15 @@ public SupportingConstructorFragment getSupportingConstructorFragment() { return supportingConstructorFragment; } + public Map getTemplateParameter() { + return templateParameter; + } + @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ( ( templateName == null ) ? 0 : templateName.hashCode() ); + result = prime * result + ( ( getName() == null ) ? 0 : getName().hashCode() ); return result; } @@ -141,7 +120,7 @@ public boolean equals(Object obj) { } SupportingMappingMethod other = (SupportingMappingMethod) obj; - if ( !Objects.equals( templateName, other.templateName ) ) { + if ( !Objects.equals( getName(), other.getName() ) ) { return false; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInConstructorFragment.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConstructorFragment.java similarity index 58% rename from processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInConstructorFragment.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/common/ConstructorFragment.java index ae8dbcc7cd..89d2928999 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInConstructorFragment.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConstructorFragment.java @@ -3,10 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.model.source.builtin; +package org.mapstruct.ap.internal.model.common; /** - * ConstructorFragments are 'code snippets' added to the constructor to initialize fields used by {@link BuiltInMethod} + * ConstructorFragments are 'code snippets' added to the constructor to initialize fields used by + * BuiltInMethod/HelperMethod */ -public interface BuiltInConstructorFragment { +public interface ConstructorFragment { } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/FieldReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/FieldReference.java new file mode 100644 index 0000000000..bf1f5be445 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/FieldReference.java @@ -0,0 +1,33 @@ +/* + * 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.internal.model.common; + +import java.util.Map; + +/** + * reference used by BuiltInMethod/HelperMethod to create an additional field in the mapper. + */ +public interface FieldReference { + + /** + * + * @return variable name of the field + */ + String getVariableName(); + + /** + * + * @return type of the field + */ + Type getType(); + + /** + * @return additional template parameters + */ + default Map getTemplateParameter() { + return null; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/FinalField.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/FinalField.java similarity index 77% rename from processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/FinalField.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/common/FinalField.java index 5ce4167117..c3e1ef1f39 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/FinalField.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/FinalField.java @@ -3,16 +3,14 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.internal.model.source.builtin; - -import org.mapstruct.ap.internal.model.common.Type; +package org.mapstruct.ap.internal.model.common; /** * A mapper instance field, initialized as null * * @author Sjaak Derksen */ -public class FinalField implements BuiltInFieldReference { +public class FinalField implements FieldReference { private final Type type; private String variableName; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java index 691d93d0f3..7a2ac5588f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java @@ -5,17 +5,21 @@ */ package org.mapstruct.ap.internal.model.source.builtin; +import static org.mapstruct.ap.internal.util.Collections.asSet; + import java.util.Set; + import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; +import org.mapstruct.ap.internal.model.common.ConstructorFragment; +import org.mapstruct.ap.internal.model.common.FieldReference; +import org.mapstruct.ap.internal.model.common.FinalField; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.Strings; -import static org.mapstruct.ap.internal.util.Collections.asSet; - /** * @author Sjaak Derksen */ @@ -46,12 +50,12 @@ public Type getReturnType() { } @Override - public BuiltInFieldReference getFieldReference() { + public FieldReference getFieldReference() { return new FinalField( dataTypeFactoryType, Strings.decapitalize( DatatypeFactory.class.getSimpleName() ) ); } @Override - public BuiltInConstructorFragment getConstructorFragment() { + public ConstructorFragment getConstructorFragment() { return new NewDatatypeFactoryConstructorFragment( ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInFieldReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInFieldReference.java deleted file mode 100644 index 0eb15f289b..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInFieldReference.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.internal.model.source.builtin; - -import org.mapstruct.ap.internal.model.common.Type; - -/** - * reference used by BuiltInMethod to create an additional field in the mapper. - */ -public interface BuiltInFieldReference { - - /** - * - * @return variable name of the field - */ - String getVariableName(); - - /** - * - * @return type of the field - */ - Type getType(); - -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java index e54a8ea4b8..f46b201576 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java @@ -5,15 +5,20 @@ */ package org.mapstruct.ap.internal.model.source.builtin; +import static org.mapstruct.ap.internal.util.Collections.first; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; + import javax.lang.model.element.ExecutableElement; import org.mapstruct.ap.internal.model.common.Accessibility; +import org.mapstruct.ap.internal.model.common.ConstructorFragment; import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.FieldReference; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.MappingMethodOptions; @@ -21,8 +26,6 @@ import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.util.Strings; -import static org.mapstruct.ap.internal.util.Collections.first; - /** * Represents a "built-in" mapping method which will be added as private method to the generated mapper. Built-in * methods are used in cases where a simple conversation doesn't suffice, e.g. as several lines of source code or a @@ -265,11 +268,11 @@ public MappingMethodOptions getOptions() { return MappingMethodOptions.empty(); } - public BuiltInFieldReference getFieldReference() { + public FieldReference getFieldReference() { return null; } - public BuiltInConstructorFragment getConstructorFragment() { + public ConstructorFragment getConstructorFragment() { return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/NewDatatypeFactoryConstructorFragment.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/NewDatatypeFactoryConstructorFragment.java index a302b59375..e4f00f7d57 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/NewDatatypeFactoryConstructorFragment.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/NewDatatypeFactoryConstructorFragment.java @@ -5,7 +5,9 @@ */ package org.mapstruct.ap.internal.model.source.builtin; -public class NewDatatypeFactoryConstructorFragment implements BuiltInConstructorFragment { +import org.mapstruct.ap.internal.model.common.ConstructorFragment; + +public class NewDatatypeFactoryConstructorFragment implements ConstructorFragment { public NewDatatypeFactoryConstructorFragment() { } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index c887140723..13e0010171 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -22,9 +22,14 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.util.TypeUtils; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.gem.DecoratedWithGem; +import org.mapstruct.ap.internal.gem.InheritConfigurationGem; +import org.mapstruct.ap.internal.gem.InheritInverseConfigurationGem; +import org.mapstruct.ap.internal.gem.MapperGem; +import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.model.BeanMappingMethod; import org.mapstruct.ap.internal.model.ContainerMappingMethod; import org.mapstruct.ap.internal.model.ContainerMappingMethodBuilder; @@ -50,18 +55,13 @@ import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.option.Options; -import org.mapstruct.ap.internal.gem.BuilderGem; -import org.mapstruct.ap.internal.gem.DecoratedWithGem; -import org.mapstruct.ap.internal.gem.InheritConfigurationGem; -import org.mapstruct.ap.internal.gem.InheritInverseConfigurationGem; -import org.mapstruct.ap.internal.gem.MapperGem; -import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; -import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.processor.creation.MappingResolverImpl; import org.mapstruct.ap.internal.util.AccessorNamingUtils; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.version.VersionInformation; import static javax.lang.model.element.Modifier.FINAL; @@ -182,7 +182,7 @@ private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List< // handle fields List fields = new ArrayList<>( mappingContext.getMapperReferences() ); - Set supportingFieldSet = new LinkedHashSet<>(); + Set supportingFieldSet = new LinkedHashSet<>(mappingContext.getUsedSupportedFields()); addAllFieldsIn( mappingContext.getUsedSupportedMappings(), supportingFieldSet ); fields.addAll( supportingFieldSet ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index ca2889e01a..d9bd4d731b 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -32,8 +32,6 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.conversion.ConversionProvider; import org.mapstruct.ap.internal.conversion.Conversions; @@ -49,6 +47,7 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.DefaultConversionContext; +import org.mapstruct.ap.internal.model.common.FieldReference; import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; @@ -60,11 +59,13 @@ import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; import org.mapstruct.ap.internal.util.Collections; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.MessageConstants; import org.mapstruct.ap.internal.util.NativeTypes; import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.internal.util.TypeUtils; /** * The one and only implementation of {@link MappingResolver}. The class has been split into an interface an @@ -98,6 +99,11 @@ public class MappingResolverImpl implements MappingResolver { */ private final Set usedSupportedMappings = new HashSet<>(); + /** + * Private fields which are added to map certain property types. + */ + private final Set usedSupportedFields = new HashSet<>(); + public MappingResolverImpl(FormattingMessager messager, ElementUtils elementUtils, TypeUtils typeUtils, TypeFactory typeFactory, List sourceModel, List mapperReferences, boolean verboseLogging) { @@ -144,6 +150,11 @@ public Set getUsedSupportedMappings() { return usedSupportedMappings; } + @Override + public Set getUsedSupportedFields() { + return usedSupportedFields; + } + private MapperReference findMapperReference(Method method) { for ( MapperReference ref : mapperReferences ) { if ( ref.getType().equals( method.getDeclaringMapper() ) ) { @@ -424,8 +435,20 @@ private ConversionAssignment resolveViaConversion(Type sourceType, Type targetTy ); // add helper methods required in conversion + Set allUsedFields = new HashSet<>( mapperReferences ); + SupportingField.addAllFieldsIn( supportingMethodCandidates, allUsedFields ); + + for ( FieldReference helperField : conversionProvider.getRequiredHelperFields( ctx )) { + Field field = SupportingField.getSafeField( null, helperField, allUsedFields ); + allUsedFields.add( field ); + usedSupportedFields.add( field ); + } + for ( HelperMethod helperMethod : conversionProvider.getRequiredHelperMethods( ctx ) ) { - usedSupportedMappings.add( new SupportingMappingMethod( helperMethod ) ); + SupportingMappingMethod supportingMappingMethod = + new SupportingMappingMethod( helperMethod ); + SupportingField.addAllFieldsIn( Collections.asSet( supportingMappingMethod ), allUsedFields ); + usedSupportedMappings.add( supportingMappingMethod ); } Assignment conversion = conversionProvider.to( ctx ); diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/conversion/GetDateTimeFormatterField.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/conversion/GetDateTimeFormatterField.ftl new file mode 100644 index 0000000000..6efbe7f0ff --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/conversion/GetDateTimeFormatterField.ftl @@ -0,0 +1,9 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.SupportingField" --> +private final <@includeModel object=type/> ${variableName} = <@includeModel object=type/>.ofPattern( "${templateParameter['dateFormat']}" ); \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/source/builtin/FinalField.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/common/FinalField.ftl similarity index 100% rename from processor/src/main/resources/org/mapstruct/ap/internal/model/source/builtin/FinalField.ftl rename to processor/src/main/resources/org/mapstruct/ap/internal/model/common/FinalField.ftl diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8CustomPatternDateTimeFormatterGeneratedTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8CustomPatternDateTimeFormatterGeneratedTest.java new file mode 100644 index 0000000000..443de8aab5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8CustomPatternDateTimeFormatterGeneratedTest.java @@ -0,0 +1,53 @@ +/* + * 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.conversion.java8time; + +import java.time.LocalDateTime; +import java.time.Month; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.conversion.java8time.custompatterndatetimeformattergenerated.Source; +import org.mapstruct.ap.test.conversion.java8time.custompatterndatetimeformattergenerated.SourceTargetMapper; +import org.mapstruct.ap.test.conversion.java8time.custompatterndatetimeformattergenerated.Target; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests generation of DateTimeFormatters as mapper instance fields for conversions to/from Java 8 date and time types. + */ +@WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) +@IssueKey("2329") +public class Java8CustomPatternDateTimeFormatterGeneratedTest { + + @RegisterExtension + GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( SourceTargetMapper.class ); + + @ProcessorTest + public void testDateTimeFormattersGenerated() { + + Source source = new Source(); + source.setLocalDateTime1( LocalDateTime.of( 2021, Month.MAY, 16, 12, 13, 10 ) ); + source.setLocalDateTime2( LocalDateTime.of( 2020, Month.APRIL, 10, 15, 10, 12 ) ); + source.setLocalDateTime3( LocalDateTime.of( 2021, Month.APRIL, 25, 9, 46, 13 ) ); + + Target target = SourceTargetMapper.INSTANCE.map( source ); + + assertThat( target.getLocalDateTime1() ).isEqualTo( "16.05.2021 12:13" ); + assertThat( target.getLocalDateTime2() ).isEqualTo( "10.04.2020 15:10" ); + assertThat( target.getLocalDateTime3() ).isEqualTo( "25.04.2021 09.46" ); + + source = SourceTargetMapper.INSTANCE.map( target ); + + assertThat( source.getLocalDateTime1() ).isEqualTo( LocalDateTime.of( 2021, Month.MAY, 16, 12, 13, 0 ) ); + assertThat( source.getLocalDateTime2() ).isEqualTo( LocalDateTime.of( 2020, Month.APRIL, 10, 15, 10, 0 ) ); + assertThat( source.getLocalDateTime3() ).isEqualTo( LocalDateTime.of( 2021, Month.APRIL, 25, 9, 46, 0 ) ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java index bcd5045646..b95b5d1acd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java @@ -19,9 +19,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; @@ -32,6 +34,9 @@ @IssueKey("121") public class Java8TimeConversionTest { + @RegisterExtension + GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( SourceTargetMapper.class ); + private TimeZone originalTimeZone; @BeforeEach diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/Source.java new file mode 100644 index 0000000000..f83721cbb5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/Source.java @@ -0,0 +1,40 @@ +/* + * 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.conversion.java8time.custompatterndatetimeformattergenerated; + +import java.time.LocalDateTime; + +public class Source { + + private LocalDateTime localDateTime1; + private LocalDateTime localDateTime2; + private LocalDateTime localDateTime3; + + public LocalDateTime getLocalDateTime1() { + return localDateTime1; + } + + public void setLocalDateTime1(LocalDateTime localDateTime1) { + this.localDateTime1 = localDateTime1; + } + + public LocalDateTime getLocalDateTime2() { + return localDateTime2; + } + + public void setLocalDateTime2(LocalDateTime localDateTime2) { + this.localDateTime2 = localDateTime2; + } + + public LocalDateTime getLocalDateTime3() { + return localDateTime3; + } + + public void setLocalDateTime3(LocalDateTime localDateTime3) { + this.localDateTime3 = localDateTime3; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/SourceTargetMapper.java new file mode 100644 index 0000000000..960ceec3b9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/SourceTargetMapper.java @@ -0,0 +1,29 @@ +/* + * 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.conversion.java8time.custompatterndatetimeformattergenerated; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SourceTargetMapper { + + String LOCAL_DATE_TIME_FORMAT = "dd.MM.yyyy HH:mm"; + String VERY_SIMILAR_LOCAL_DATE_TIME_FORMAT = "dd.MM.yyyy HH.mm"; + + SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + + @Mapping(target = "localDateTime1", dateFormat = LOCAL_DATE_TIME_FORMAT) + @Mapping(target = "localDateTime2", dateFormat = LOCAL_DATE_TIME_FORMAT) + @Mapping(target = "localDateTime3", dateFormat = VERY_SIMILAR_LOCAL_DATE_TIME_FORMAT) + Target map(Source source); + + @InheritInverseConfiguration + Source map(Target target); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/Target.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/Target.java new file mode 100644 index 0000000000..7d265ba4aa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/Target.java @@ -0,0 +1,38 @@ +/* + * 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.conversion.java8time.custompatterndatetimeformattergenerated; + +public class Target { + + private String localDateTime1; + private String localDateTime2; + private String localDateTime3; + + public String getLocalDateTime1() { + return localDateTime1; + } + + public void setLocalDateTime1(String localDateTime1) { + this.localDateTime1 = localDateTime1; + } + + public String getLocalDateTime2() { + return localDateTime2; + } + + public void setLocalDateTime2(String localDateTime2) { + this.localDateTime2 = localDateTime2; + } + + public String getLocalDateTime3() { + return localDateTime3; + } + + public void setLocalDateTime3(String localDateTime3) { + this.localDateTime3 = localDateTime3; + } + +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapperImpl.java new file mode 100644 index 0000000000..58beaa19dd --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapperImpl.java @@ -0,0 +1,640 @@ +/* + * 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.conversion.java8time; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-05-15T18:24:04+0200", + comments = "version: , compiler: javac, environment: Java 1.8.0_275 (AdoptOpenJDK)" +) +public class SourceTargetMapperImpl implements SourceTargetMapper { + + private final DateTimeFormatter dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242 = DateTimeFormatter.ofPattern( "dd.MM.yyyy HH:mm" ); + private final DateTimeFormatter dateTimeFormatter_HH_mm_168697690 = DateTimeFormatter.ofPattern( "HH:mm" ); + private final DateTimeFormatter dateTimeFormatter_dd_MM_yyyy_11900521056 = DateTimeFormatter.ofPattern( "dd.MM.yyyy" ); + private final DateTimeFormatter dateTimeFormatter_dd_MM_yyyy_HH_mm_z_01894582668 = DateTimeFormatter.ofPattern( "dd.MM.yyyy HH:mm z" ); + + @Override + public Target sourceToTarget(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + if ( source.getZonedDateTime() != null ) { + target.setZonedDateTime( dateTimeFormatter_dd_MM_yyyy_HH_mm_z_01894582668.format( source.getZonedDateTime() ) ); + } + if ( source.getLocalDateTime() != null ) { + target.setLocalDateTime( dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242.format( source.getLocalDateTime() ) ); + } + if ( source.getLocalDate() != null ) { + target.setLocalDate( dateTimeFormatter_dd_MM_yyyy_11900521056.format( source.getLocalDate() ) ); + } + if ( source.getLocalTime() != null ) { + target.setLocalTime( dateTimeFormatter_HH_mm_168697690.format( source.getLocalTime() ) ); + } + target.setForCalendarConversion( zonedDateTimeToCalendar( source.getForCalendarConversion() ) ); + if ( source.getForDateConversionWithZonedDateTime() != null ) { + target.setForDateConversionWithZonedDateTime( Date.from( source.getForDateConversionWithZonedDateTime().toInstant() ) ); + } + if ( source.getForDateConversionWithLocalDateTime() != null ) { + target.setForDateConversionWithLocalDateTime( Date.from( source.getForDateConversionWithLocalDateTime().toInstant( ZoneOffset.UTC ) ) ); + } + if ( source.getForDateConversionWithLocalDate() != null ) { + target.setForDateConversionWithLocalDate( Date.from( source.getForDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant() ) ); + } + if ( source.getForSqlDateConversionWithLocalDate() != null ) { + target.setForSqlDateConversionWithLocalDate( new java.sql.Date( source.getForSqlDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant().toEpochMilli() ) ); + } + if ( source.getForDateConversionWithInstant() != null ) { + target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); + } + if ( source.getForInstantConversionWithString() != null ) { + target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); + } + if ( source.getForPeriodConversionWithString() != null ) { + target.setForPeriodConversionWithString( source.getForPeriodConversionWithString().toString() ); + } + if ( source.getForDurationConversionWithString() != null ) { + target.setForDurationConversionWithString( source.getForDurationConversionWithString().toString() ); + } + + return target; + } + + @Override + public Target sourceToTargetDefaultMapping(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + if ( source.getZonedDateTime() != null ) { + target.setZonedDateTime( dateTimeFormatter_dd_MM_yyyy_HH_mm_z_01894582668.format( source.getZonedDateTime() ) ); + } + if ( source.getLocalDateTime() != null ) { + target.setLocalDateTime( dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242.format( source.getLocalDateTime() ) ); + } + if ( source.getLocalDate() != null ) { + target.setLocalDate( dateTimeFormatter_dd_MM_yyyy_11900521056.format( source.getLocalDate() ) ); + } + if ( source.getLocalTime() != null ) { + target.setLocalTime( dateTimeFormatter_HH_mm_168697690.format( source.getLocalTime() ) ); + } + target.setForCalendarConversion( zonedDateTimeToCalendar( source.getForCalendarConversion() ) ); + if ( source.getForDateConversionWithZonedDateTime() != null ) { + target.setForDateConversionWithZonedDateTime( Date.from( source.getForDateConversionWithZonedDateTime().toInstant() ) ); + } + if ( source.getForDateConversionWithLocalDateTime() != null ) { + target.setForDateConversionWithLocalDateTime( Date.from( source.getForDateConversionWithLocalDateTime().toInstant( ZoneOffset.UTC ) ) ); + } + if ( source.getForDateConversionWithLocalDate() != null ) { + target.setForDateConversionWithLocalDate( Date.from( source.getForDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant() ) ); + } + if ( source.getForSqlDateConversionWithLocalDate() != null ) { + target.setForSqlDateConversionWithLocalDate( new java.sql.Date( source.getForSqlDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant().toEpochMilli() ) ); + } + if ( source.getForDateConversionWithInstant() != null ) { + target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); + } + if ( source.getForInstantConversionWithString() != null ) { + target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); + } + if ( source.getForPeriodConversionWithString() != null ) { + target.setForPeriodConversionWithString( source.getForPeriodConversionWithString().toString() ); + } + if ( source.getForDurationConversionWithString() != null ) { + target.setForDurationConversionWithString( source.getForDurationConversionWithString().toString() ); + } + + return target; + } + + @Override + public Target sourceToTargetDateTimeMapped(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + if ( source.getZonedDateTime() != null ) { + target.setZonedDateTime( dateTimeFormatter_dd_MM_yyyy_HH_mm_z_01894582668.format( source.getZonedDateTime() ) ); + } + if ( source.getLocalDateTime() != null ) { + target.setLocalDateTime( DateTimeFormatter.ISO_LOCAL_DATE_TIME.format( source.getLocalDateTime() ) ); + } + if ( source.getLocalDate() != null ) { + target.setLocalDate( DateTimeFormatter.ISO_LOCAL_DATE.format( source.getLocalDate() ) ); + } + if ( source.getLocalTime() != null ) { + target.setLocalTime( DateTimeFormatter.ISO_LOCAL_TIME.format( source.getLocalTime() ) ); + } + target.setForCalendarConversion( zonedDateTimeToCalendar( source.getForCalendarConversion() ) ); + if ( source.getForDateConversionWithZonedDateTime() != null ) { + target.setForDateConversionWithZonedDateTime( Date.from( source.getForDateConversionWithZonedDateTime().toInstant() ) ); + } + if ( source.getForDateConversionWithLocalDateTime() != null ) { + target.setForDateConversionWithLocalDateTime( Date.from( source.getForDateConversionWithLocalDateTime().toInstant( ZoneOffset.UTC ) ) ); + } + if ( source.getForDateConversionWithLocalDate() != null ) { + target.setForDateConversionWithLocalDate( Date.from( source.getForDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant() ) ); + } + if ( source.getForSqlDateConversionWithLocalDate() != null ) { + target.setForSqlDateConversionWithLocalDate( new java.sql.Date( source.getForSqlDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant().toEpochMilli() ) ); + } + if ( source.getForDateConversionWithInstant() != null ) { + target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); + } + if ( source.getForInstantConversionWithString() != null ) { + target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); + } + if ( source.getForPeriodConversionWithString() != null ) { + target.setForPeriodConversionWithString( source.getForPeriodConversionWithString().toString() ); + } + if ( source.getForDurationConversionWithString() != null ) { + target.setForDurationConversionWithString( source.getForDurationConversionWithString().toString() ); + } + + return target; + } + + @Override + public Target sourceToTargetLocalDateTimeMapped(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + if ( source.getLocalDateTime() != null ) { + target.setLocalDateTime( dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242.format( source.getLocalDateTime() ) ); + } + if ( source.getZonedDateTime() != null ) { + target.setZonedDateTime( DateTimeFormatter.ISO_DATE_TIME.format( source.getZonedDateTime() ) ); + } + if ( source.getLocalDate() != null ) { + target.setLocalDate( DateTimeFormatter.ISO_LOCAL_DATE.format( source.getLocalDate() ) ); + } + if ( source.getLocalTime() != null ) { + target.setLocalTime( DateTimeFormatter.ISO_LOCAL_TIME.format( source.getLocalTime() ) ); + } + target.setForCalendarConversion( zonedDateTimeToCalendar( source.getForCalendarConversion() ) ); + if ( source.getForDateConversionWithZonedDateTime() != null ) { + target.setForDateConversionWithZonedDateTime( Date.from( source.getForDateConversionWithZonedDateTime().toInstant() ) ); + } + if ( source.getForDateConversionWithLocalDateTime() != null ) { + target.setForDateConversionWithLocalDateTime( Date.from( source.getForDateConversionWithLocalDateTime().toInstant( ZoneOffset.UTC ) ) ); + } + if ( source.getForDateConversionWithLocalDate() != null ) { + target.setForDateConversionWithLocalDate( Date.from( source.getForDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant() ) ); + } + if ( source.getForSqlDateConversionWithLocalDate() != null ) { + target.setForSqlDateConversionWithLocalDate( new java.sql.Date( source.getForSqlDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant().toEpochMilli() ) ); + } + if ( source.getForDateConversionWithInstant() != null ) { + target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); + } + if ( source.getForInstantConversionWithString() != null ) { + target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); + } + if ( source.getForPeriodConversionWithString() != null ) { + target.setForPeriodConversionWithString( source.getForPeriodConversionWithString().toString() ); + } + if ( source.getForDurationConversionWithString() != null ) { + target.setForDurationConversionWithString( source.getForDurationConversionWithString().toString() ); + } + + return target; + } + + @Override + public Target sourceToTargetLocalDateMapped(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + if ( source.getLocalDate() != null ) { + target.setLocalDate( dateTimeFormatter_dd_MM_yyyy_11900521056.format( source.getLocalDate() ) ); + } + if ( source.getZonedDateTime() != null ) { + target.setZonedDateTime( DateTimeFormatter.ISO_DATE_TIME.format( source.getZonedDateTime() ) ); + } + if ( source.getLocalDateTime() != null ) { + target.setLocalDateTime( DateTimeFormatter.ISO_LOCAL_DATE_TIME.format( source.getLocalDateTime() ) ); + } + if ( source.getLocalTime() != null ) { + target.setLocalTime( DateTimeFormatter.ISO_LOCAL_TIME.format( source.getLocalTime() ) ); + } + target.setForCalendarConversion( zonedDateTimeToCalendar( source.getForCalendarConversion() ) ); + if ( source.getForDateConversionWithZonedDateTime() != null ) { + target.setForDateConversionWithZonedDateTime( Date.from( source.getForDateConversionWithZonedDateTime().toInstant() ) ); + } + if ( source.getForDateConversionWithLocalDateTime() != null ) { + target.setForDateConversionWithLocalDateTime( Date.from( source.getForDateConversionWithLocalDateTime().toInstant( ZoneOffset.UTC ) ) ); + } + if ( source.getForDateConversionWithLocalDate() != null ) { + target.setForDateConversionWithLocalDate( Date.from( source.getForDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant() ) ); + } + if ( source.getForSqlDateConversionWithLocalDate() != null ) { + target.setForSqlDateConversionWithLocalDate( new java.sql.Date( source.getForSqlDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant().toEpochMilli() ) ); + } + if ( source.getForDateConversionWithInstant() != null ) { + target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); + } + if ( source.getForInstantConversionWithString() != null ) { + target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); + } + if ( source.getForPeriodConversionWithString() != null ) { + target.setForPeriodConversionWithString( source.getForPeriodConversionWithString().toString() ); + } + if ( source.getForDurationConversionWithString() != null ) { + target.setForDurationConversionWithString( source.getForDurationConversionWithString().toString() ); + } + + return target; + } + + @Override + public Target sourceToTargetLocalTimeMapped(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + if ( source.getLocalTime() != null ) { + target.setLocalTime( dateTimeFormatter_HH_mm_168697690.format( source.getLocalTime() ) ); + } + if ( source.getZonedDateTime() != null ) { + target.setZonedDateTime( DateTimeFormatter.ISO_DATE_TIME.format( source.getZonedDateTime() ) ); + } + if ( source.getLocalDateTime() != null ) { + target.setLocalDateTime( DateTimeFormatter.ISO_LOCAL_DATE_TIME.format( source.getLocalDateTime() ) ); + } + if ( source.getLocalDate() != null ) { + target.setLocalDate( DateTimeFormatter.ISO_LOCAL_DATE.format( source.getLocalDate() ) ); + } + target.setForCalendarConversion( zonedDateTimeToCalendar( source.getForCalendarConversion() ) ); + if ( source.getForDateConversionWithZonedDateTime() != null ) { + target.setForDateConversionWithZonedDateTime( Date.from( source.getForDateConversionWithZonedDateTime().toInstant() ) ); + } + if ( source.getForDateConversionWithLocalDateTime() != null ) { + target.setForDateConversionWithLocalDateTime( Date.from( source.getForDateConversionWithLocalDateTime().toInstant( ZoneOffset.UTC ) ) ); + } + if ( source.getForDateConversionWithLocalDate() != null ) { + target.setForDateConversionWithLocalDate( Date.from( source.getForDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant() ) ); + } + if ( source.getForSqlDateConversionWithLocalDate() != null ) { + target.setForSqlDateConversionWithLocalDate( new java.sql.Date( source.getForSqlDateConversionWithLocalDate().atStartOfDay( ZoneOffset.UTC ).toInstant().toEpochMilli() ) ); + } + if ( source.getForDateConversionWithInstant() != null ) { + target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); + } + if ( source.getForInstantConversionWithString() != null ) { + target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); + } + if ( source.getForPeriodConversionWithString() != null ) { + target.setForPeriodConversionWithString( source.getForPeriodConversionWithString().toString() ); + } + if ( source.getForDurationConversionWithString() != null ) { + target.setForDurationConversionWithString( source.getForDurationConversionWithString().toString() ); + } + + return target; + } + + @Override + public Source targetToSource(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + if ( target.getZonedDateTime() != null ) { + source.setZonedDateTime( ZonedDateTime.parse( target.getZonedDateTime(), dateTimeFormatter_dd_MM_yyyy_HH_mm_z_01894582668 ) ); + } + if ( target.getLocalDateTime() != null ) { + source.setLocalDateTime( LocalDateTime.parse( target.getLocalDateTime(), dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242 ) ); + } + if ( target.getLocalDate() != null ) { + source.setLocalDate( LocalDate.parse( target.getLocalDate(), dateTimeFormatter_dd_MM_yyyy_11900521056 ) ); + } + if ( target.getLocalTime() != null ) { + source.setLocalTime( LocalTime.parse( target.getLocalTime(), dateTimeFormatter_HH_mm_168697690 ) ); + } + source.setForCalendarConversion( calendarToZonedDateTime( target.getForCalendarConversion() ) ); + if ( target.getForDateConversionWithZonedDateTime() != null ) { + source.setForDateConversionWithZonedDateTime( ZonedDateTime.ofInstant( target.getForDateConversionWithZonedDateTime().toInstant(), ZoneId.systemDefault() ) ); + } + if ( target.getForDateConversionWithLocalDateTime() != null ) { + source.setForDateConversionWithLocalDateTime( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDateTime().toInstant(), ZoneId.of( "UTC" ) ) ); + } + if ( target.getForDateConversionWithLocalDate() != null ) { + source.setForDateConversionWithLocalDate( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDate().toInstant(), ZoneOffset.UTC ).toLocalDate() ); + } + if ( target.getForSqlDateConversionWithLocalDate() != null ) { + source.setForSqlDateConversionWithLocalDate( target.getForSqlDateConversionWithLocalDate().toLocalDate() ); + } + if ( target.getForDateConversionWithInstant() != null ) { + source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); + } + if ( target.getForInstantConversionWithString() != null ) { + source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); + } + if ( target.getForPeriodConversionWithString() != null ) { + source.setForPeriodConversionWithString( Period.parse( target.getForPeriodConversionWithString() ) ); + } + if ( target.getForDurationConversionWithString() != null ) { + source.setForDurationConversionWithString( Duration.parse( target.getForDurationConversionWithString() ) ); + } + + return source; + } + + @Override + public Source targetToSourceDateTimeMapped(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + if ( target.getZonedDateTime() != null ) { + source.setZonedDateTime( ZonedDateTime.parse( target.getZonedDateTime(), dateTimeFormatter_dd_MM_yyyy_HH_mm_z_01894582668 ) ); + } + if ( target.getLocalDateTime() != null ) { + source.setLocalDateTime( LocalDateTime.parse( target.getLocalDateTime() ) ); + } + if ( target.getLocalDate() != null ) { + source.setLocalDate( LocalDate.parse( target.getLocalDate() ) ); + } + if ( target.getLocalTime() != null ) { + source.setLocalTime( LocalTime.parse( target.getLocalTime() ) ); + } + source.setForCalendarConversion( calendarToZonedDateTime( target.getForCalendarConversion() ) ); + if ( target.getForDateConversionWithZonedDateTime() != null ) { + source.setForDateConversionWithZonedDateTime( ZonedDateTime.ofInstant( target.getForDateConversionWithZonedDateTime().toInstant(), ZoneId.systemDefault() ) ); + } + if ( target.getForDateConversionWithLocalDateTime() != null ) { + source.setForDateConversionWithLocalDateTime( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDateTime().toInstant(), ZoneId.of( "UTC" ) ) ); + } + if ( target.getForDateConversionWithLocalDate() != null ) { + source.setForDateConversionWithLocalDate( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDate().toInstant(), ZoneOffset.UTC ).toLocalDate() ); + } + if ( target.getForSqlDateConversionWithLocalDate() != null ) { + source.setForSqlDateConversionWithLocalDate( target.getForSqlDateConversionWithLocalDate().toLocalDate() ); + } + if ( target.getForDateConversionWithInstant() != null ) { + source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); + } + if ( target.getForInstantConversionWithString() != null ) { + source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); + } + if ( target.getForPeriodConversionWithString() != null ) { + source.setForPeriodConversionWithString( Period.parse( target.getForPeriodConversionWithString() ) ); + } + if ( target.getForDurationConversionWithString() != null ) { + source.setForDurationConversionWithString( Duration.parse( target.getForDurationConversionWithString() ) ); + } + + return source; + } + + @Override + public Source targetToSourceLocalDateTimeMapped(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + if ( target.getLocalDateTime() != null ) { + source.setLocalDateTime( LocalDateTime.parse( target.getLocalDateTime(), dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242 ) ); + } + if ( target.getZonedDateTime() != null ) { + source.setZonedDateTime( ZonedDateTime.parse( target.getZonedDateTime() ) ); + } + if ( target.getLocalDate() != null ) { + source.setLocalDate( LocalDate.parse( target.getLocalDate() ) ); + } + if ( target.getLocalTime() != null ) { + source.setLocalTime( LocalTime.parse( target.getLocalTime() ) ); + } + source.setForCalendarConversion( calendarToZonedDateTime( target.getForCalendarConversion() ) ); + if ( target.getForDateConversionWithZonedDateTime() != null ) { + source.setForDateConversionWithZonedDateTime( ZonedDateTime.ofInstant( target.getForDateConversionWithZonedDateTime().toInstant(), ZoneId.systemDefault() ) ); + } + if ( target.getForDateConversionWithLocalDateTime() != null ) { + source.setForDateConversionWithLocalDateTime( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDateTime().toInstant(), ZoneId.of( "UTC" ) ) ); + } + if ( target.getForDateConversionWithLocalDate() != null ) { + source.setForDateConversionWithLocalDate( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDate().toInstant(), ZoneOffset.UTC ).toLocalDate() ); + } + if ( target.getForSqlDateConversionWithLocalDate() != null ) { + source.setForSqlDateConversionWithLocalDate( target.getForSqlDateConversionWithLocalDate().toLocalDate() ); + } + if ( target.getForDateConversionWithInstant() != null ) { + source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); + } + if ( target.getForInstantConversionWithString() != null ) { + source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); + } + if ( target.getForPeriodConversionWithString() != null ) { + source.setForPeriodConversionWithString( Period.parse( target.getForPeriodConversionWithString() ) ); + } + if ( target.getForDurationConversionWithString() != null ) { + source.setForDurationConversionWithString( Duration.parse( target.getForDurationConversionWithString() ) ); + } + + return source; + } + + @Override + public Source targetToSourceLocalDateMapped(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + if ( target.getLocalDate() != null ) { + source.setLocalDate( LocalDate.parse( target.getLocalDate(), dateTimeFormatter_dd_MM_yyyy_11900521056 ) ); + } + if ( target.getZonedDateTime() != null ) { + source.setZonedDateTime( ZonedDateTime.parse( target.getZonedDateTime() ) ); + } + if ( target.getLocalDateTime() != null ) { + source.setLocalDateTime( LocalDateTime.parse( target.getLocalDateTime() ) ); + } + if ( target.getLocalTime() != null ) { + source.setLocalTime( LocalTime.parse( target.getLocalTime() ) ); + } + source.setForCalendarConversion( calendarToZonedDateTime( target.getForCalendarConversion() ) ); + if ( target.getForDateConversionWithZonedDateTime() != null ) { + source.setForDateConversionWithZonedDateTime( ZonedDateTime.ofInstant( target.getForDateConversionWithZonedDateTime().toInstant(), ZoneId.systemDefault() ) ); + } + if ( target.getForDateConversionWithLocalDateTime() != null ) { + source.setForDateConversionWithLocalDateTime( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDateTime().toInstant(), ZoneId.of( "UTC" ) ) ); + } + if ( target.getForDateConversionWithLocalDate() != null ) { + source.setForDateConversionWithLocalDate( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDate().toInstant(), ZoneOffset.UTC ).toLocalDate() ); + } + if ( target.getForSqlDateConversionWithLocalDate() != null ) { + source.setForSqlDateConversionWithLocalDate( target.getForSqlDateConversionWithLocalDate().toLocalDate() ); + } + if ( target.getForDateConversionWithInstant() != null ) { + source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); + } + if ( target.getForInstantConversionWithString() != null ) { + source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); + } + if ( target.getForPeriodConversionWithString() != null ) { + source.setForPeriodConversionWithString( Period.parse( target.getForPeriodConversionWithString() ) ); + } + if ( target.getForDurationConversionWithString() != null ) { + source.setForDurationConversionWithString( Duration.parse( target.getForDurationConversionWithString() ) ); + } + + return source; + } + + @Override + public Source targetToSourceLocalTimeMapped(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + if ( target.getLocalTime() != null ) { + source.setLocalTime( LocalTime.parse( target.getLocalTime(), dateTimeFormatter_HH_mm_168697690 ) ); + } + if ( target.getZonedDateTime() != null ) { + source.setZonedDateTime( ZonedDateTime.parse( target.getZonedDateTime() ) ); + } + if ( target.getLocalDateTime() != null ) { + source.setLocalDateTime( LocalDateTime.parse( target.getLocalDateTime() ) ); + } + if ( target.getLocalDate() != null ) { + source.setLocalDate( LocalDate.parse( target.getLocalDate() ) ); + } + source.setForCalendarConversion( calendarToZonedDateTime( target.getForCalendarConversion() ) ); + if ( target.getForDateConversionWithZonedDateTime() != null ) { + source.setForDateConversionWithZonedDateTime( ZonedDateTime.ofInstant( target.getForDateConversionWithZonedDateTime().toInstant(), ZoneId.systemDefault() ) ); + } + if ( target.getForDateConversionWithLocalDateTime() != null ) { + source.setForDateConversionWithLocalDateTime( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDateTime().toInstant(), ZoneId.of( "UTC" ) ) ); + } + if ( target.getForDateConversionWithLocalDate() != null ) { + source.setForDateConversionWithLocalDate( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDate().toInstant(), ZoneOffset.UTC ).toLocalDate() ); + } + if ( target.getForSqlDateConversionWithLocalDate() != null ) { + source.setForSqlDateConversionWithLocalDate( target.getForSqlDateConversionWithLocalDate().toLocalDate() ); + } + if ( target.getForDateConversionWithInstant() != null ) { + source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); + } + if ( target.getForInstantConversionWithString() != null ) { + source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); + } + if ( target.getForPeriodConversionWithString() != null ) { + source.setForPeriodConversionWithString( Period.parse( target.getForPeriodConversionWithString() ) ); + } + if ( target.getForDurationConversionWithString() != null ) { + source.setForDurationConversionWithString( Duration.parse( target.getForDurationConversionWithString() ) ); + } + + return source; + } + + @Override + public Source targetToSourceDefaultMapping(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + if ( target.getZonedDateTime() != null ) { + source.setZonedDateTime( ZonedDateTime.parse( target.getZonedDateTime(), dateTimeFormatter_dd_MM_yyyy_HH_mm_z_01894582668 ) ); + } + if ( target.getLocalDateTime() != null ) { + source.setLocalDateTime( LocalDateTime.parse( target.getLocalDateTime(), dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242 ) ); + } + if ( target.getLocalDate() != null ) { + source.setLocalDate( LocalDate.parse( target.getLocalDate(), dateTimeFormatter_dd_MM_yyyy_11900521056 ) ); + } + if ( target.getLocalTime() != null ) { + source.setLocalTime( LocalTime.parse( target.getLocalTime(), dateTimeFormatter_HH_mm_168697690 ) ); + } + source.setForCalendarConversion( calendarToZonedDateTime( target.getForCalendarConversion() ) ); + if ( target.getForDateConversionWithZonedDateTime() != null ) { + source.setForDateConversionWithZonedDateTime( ZonedDateTime.ofInstant( target.getForDateConversionWithZonedDateTime().toInstant(), ZoneId.systemDefault() ) ); + } + if ( target.getForDateConversionWithLocalDateTime() != null ) { + source.setForDateConversionWithLocalDateTime( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDateTime().toInstant(), ZoneId.of( "UTC" ) ) ); + } + if ( target.getForDateConversionWithLocalDate() != null ) { + source.setForDateConversionWithLocalDate( LocalDateTime.ofInstant( target.getForDateConversionWithLocalDate().toInstant(), ZoneOffset.UTC ).toLocalDate() ); + } + if ( target.getForSqlDateConversionWithLocalDate() != null ) { + source.setForSqlDateConversionWithLocalDate( target.getForSqlDateConversionWithLocalDate().toLocalDate() ); + } + if ( target.getForDateConversionWithInstant() != null ) { + source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); + } + if ( target.getForInstantConversionWithString() != null ) { + source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); + } + if ( target.getForPeriodConversionWithString() != null ) { + source.setForPeriodConversionWithString( Period.parse( target.getForPeriodConversionWithString() ) ); + } + if ( target.getForDurationConversionWithString() != null ) { + source.setForDurationConversionWithString( Duration.parse( target.getForDurationConversionWithString() ) ); + } + + return source; + } + + private ZonedDateTime calendarToZonedDateTime(Calendar cal) { + if ( cal == null ) { + return null; + } + + return ZonedDateTime.ofInstant( cal.toInstant(), cal.getTimeZone().toZoneId() ); + } + + private Calendar zonedDateTimeToCalendar(ZonedDateTime dateTime) { + if ( dateTime == null ) { + return null; + } + + Calendar instance = Calendar.getInstance( TimeZone.getTimeZone( dateTime.getZone() ) ); + instance.setTimeInMillis( dateTime.toInstant().toEpochMilli() ); + return instance; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/SourceTargetMapperImpl.java new file mode 100644 index 0000000000..98c2c19bb6 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/custompatterndatetimeformattergenerated/SourceTargetMapperImpl.java @@ -0,0 +1,63 @@ +/* + * 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.conversion.java8time.custompatterndatetimeformattergenerated; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-05-16T13:11:04+0200", + comments = "version: , compiler: javac, environment: Java 1.8.0_275 (AdoptOpenJDK)" +) +public class SourceTargetMapperImpl implements SourceTargetMapper { + + private final DateTimeFormatter dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242 = DateTimeFormatter.ofPattern( "dd.MM.yyyy HH:mm" ); + private final DateTimeFormatter dateTimeFormatter_dd_MM_yyyy_HH_mm_12071757710 = DateTimeFormatter.ofPattern( "dd.MM.yyyy HH.mm" ); + + @Override + public Target map(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + if ( source.getLocalDateTime1() != null ) { + target.setLocalDateTime1( dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242.format( source.getLocalDateTime1() ) ); + } + if ( source.getLocalDateTime2() != null ) { + target.setLocalDateTime2( dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242.format( source.getLocalDateTime2() ) ); + } + if ( source.getLocalDateTime3() != null ) { + target.setLocalDateTime3( dateTimeFormatter_dd_MM_yyyy_HH_mm_12071757710.format( source.getLocalDateTime3() ) ); + } + + return target; + } + + @Override + public Source map(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + if ( target.getLocalDateTime1() != null ) { + source.setLocalDateTime1( LocalDateTime.parse( target.getLocalDateTime1(), dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242 ) ); + } + if ( target.getLocalDateTime2() != null ) { + source.setLocalDateTime2( LocalDateTime.parse( target.getLocalDateTime2(), dateTimeFormatter_dd_MM_yyyy_HH_mm_12071769242 ) ); + } + if ( target.getLocalDateTime3() != null ) { + source.setLocalDateTime3( LocalDateTime.parse( target.getLocalDateTime3(), dateTimeFormatter_dd_MM_yyyy_HH_mm_12071757710 ) ); + } + + return source; + } +} From 70ea65f7aa5518cac236c277bf040b058907f713 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 16 May 2021 17:39:26 +0200 Subject: [PATCH 167/597] #2437 Do not visit the same type element twice when retrieving methods When using the diamond inheritance a TypeElement might be visited twice. This is however, incorrect. We need to visit a TypeElement exactly once. --- .../util/AbstractElementUtilsDecorator.java | 15 ++++++++-- .../ap/test/bugs/_2437/Issue2437Test.java | 30 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2437/Phone.java | 22 ++++++++++++++ .../ap/test/bugs/_2437/PhoneDto.java | 22 ++++++++++++++ .../ap/test/bugs/_2437/PhoneMapper.java | 15 ++++++++++ .../test/bugs/_2437/PhoneParent1Mapper.java | 12 ++++++++ .../test/bugs/_2437/PhoneParent2Mapper.java | 12 ++++++++ .../ap/test/bugs/_2437/PhoneSuperMapper.java | 14 +++++++++ 8 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/Issue2437Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/Phone.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneParent1Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneParent2Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneSuperMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java index 8239dcb41b..14734b9c8e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java @@ -7,6 +7,8 @@ import java.io.Writer; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -118,7 +120,7 @@ public boolean isFunctionalInterface(TypeElement type) { public List getAllEnclosedExecutableElements(TypeElement element) { List enclosedElements = new ArrayList<>(); element = replaceTypeElementIfNecessary( element ); - addEnclosedMethodsInHierarchy( enclosedElements, element, element ); + addEnclosedMethodsInHierarchy( enclosedElements, new HashSet<>(), element, element ); return enclosedElements; } @@ -132,7 +134,9 @@ public List getAllEnclosedFields( TypeElement element) { return enclosedElements; } - private void addEnclosedMethodsInHierarchy(List alreadyAdded, TypeElement element, + private void addEnclosedMethodsInHierarchy(List alreadyAdded, + Collection alreadyVisitedElements, + TypeElement element, TypeElement parentType) { if ( element != parentType ) { // otherwise the element was already checked for replacement element = replaceTypeElementIfNecessary( element ); @@ -142,11 +146,17 @@ private void addEnclosedMethodsInHierarchy(List alreadyAdded, throw new TypeHierarchyErroneousException( element ); } + if ( !alreadyVisitedElements.add( element.getQualifiedName().toString() ) ) { + // If we already visited the element we should not go into it again. + // This can happen when diamond inheritance is used with interfaces + return; + } addMethodNotYetOverridden( alreadyAdded, methodsIn( element.getEnclosedElements() ), parentType ); if ( hasNonObjectSuperclass( element ) ) { addEnclosedMethodsInHierarchy( alreadyAdded, + alreadyVisitedElements, asTypeElement( element.getSuperclass() ), parentType ); @@ -155,6 +165,7 @@ private void addEnclosedMethodsInHierarchy(List alreadyAdded, for ( TypeMirror interfaceType : element.getInterfaces() ) { addEnclosedMethodsInHierarchy( alreadyAdded, + alreadyVisitedElements, asTypeElement( interfaceType ), parentType ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/Issue2437Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/Issue2437Test.java new file mode 100644 index 0000000000..343b8fbf1f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/Issue2437Test.java @@ -0,0 +1,30 @@ +/* + * 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._2437; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2437") +@WithClasses({ + Phone.class, + PhoneDto.class, + PhoneMapper.class, + PhoneParent1Mapper.class, + PhoneParent2Mapper.class, + PhoneSuperMapper.class, +}) +class Issue2437Test { + + @ProcessorTest + void shouldGenerateValidCode() { + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/Phone.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/Phone.java new file mode 100644 index 0000000000..98027861fe --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/Phone.java @@ -0,0 +1,22 @@ +/* + * 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._2437; + +/** + * @author Filip Hrisafov + */ +public class Phone { + + private final String number; + + public Phone(String number) { + this.number = number; + } + + public String getNumber() { + return number; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneDto.java new file mode 100644 index 0000000000..883e086e89 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneDto.java @@ -0,0 +1,22 @@ +/* + * 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._2437; + +/** + * @author Filip Hrisafov + */ +public class PhoneDto { + + private final String number; + + public PhoneDto(String number) { + this.number = number; + } + + public String getNumber() { + return number; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneMapper.java new file mode 100644 index 0000000000..16b01658be --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneMapper.java @@ -0,0 +1,15 @@ +/* + * 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._2437; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface PhoneMapper extends PhoneParent1Mapper, PhoneParent2Mapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneParent1Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneParent1Mapper.java new file mode 100644 index 0000000000..0b4da27c15 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneParent1Mapper.java @@ -0,0 +1,12 @@ +/* + * 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._2437; + +/** + * @author Filip Hrisafov + */ +public interface PhoneParent1Mapper extends PhoneSuperMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneParent2Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneParent2Mapper.java new file mode 100644 index 0000000000..77e9db6235 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneParent2Mapper.java @@ -0,0 +1,12 @@ +/* + * 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._2437; + +/** + * @author Filip Hrisafov + */ +public interface PhoneParent2Mapper extends PhoneSuperMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneSuperMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneSuperMapper.java new file mode 100644 index 0000000000..ba35b21d9b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2437/PhoneSuperMapper.java @@ -0,0 +1,14 @@ +/* + * 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._2437; + +/** + * @author Filip Hrisafov + */ +public interface PhoneSuperMapper { + + Phone map(PhoneDto dto); +} From 857f87276f686f0659b0fa604aa363e25701916b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 5 Jun 2021 09:22:37 +0200 Subject: [PATCH 168/597] #2466 Update dependencies so tests run on Java 16+ Update GitHub Actions for tests to run on Java 11, 13, 16 and 17-ea. Update Lombok so tests can run on 16+ Add jaxb-runtime dependency to the maven-jaxb2-plugin see https://github.com/highsource/maven-jaxb2-plugin/issues/148 Disable cdi integration test on Java 16+ until we find a solution for them --- .github/workflows/java-ea.yml | 2 +- .github/workflows/main.yml | 2 +- .../org/mapstruct/itest/tests/MavenIntegrationTest.java | 2 ++ integrationtest/src/test/resources/jaxbTest/pom.xml | 7 +++++++ parent/pom.xml | 2 +- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index 3e901b6119..598a8979a6 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - java: [16-ea] + java: [17-ea] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f63a183d4b..741f8bce61 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - java: [11, 13, 15] + java: [11, 13, 16] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 76a72b4b7f..e82f13a018 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -5,6 +5,7 @@ */ package org.mapstruct.itest.tests; +import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.parallel.Execution; @@ -25,6 +26,7 @@ void autoValueBuilderTest() { } @ProcessorTest(baseDir = "cdiTest") + @DisabledForJreRange(min = JRE.JAVA_16) void cdiTest() { } diff --git a/integrationtest/src/test/resources/jaxbTest/pom.xml b/integrationtest/src/test/resources/jaxbTest/pom.xml index 8b3e4c7921..000e7cf598 100644 --- a/integrationtest/src/test/resources/jaxbTest/pom.xml +++ b/integrationtest/src/test/resources/jaxbTest/pom.xml @@ -47,6 +47,13 @@ true 2.1 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.2 + + diff --git a/parent/pom.xml b/parent/pom.xml index c1172cbfe0..a924b1cd68 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -202,7 +202,7 @@ org.projectlombok lombok - 1.18.16 + 1.18.20 org.immutables From 9ce9d4fb3adad0c64a2b636ebea5f71ef2602f09 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 5 Jun 2021 08:56:11 +0200 Subject: [PATCH 169/597] #2375 Add records cross module integration test --- .../itest/tests/MavenIntegrationTest.java | 7 ++++ .../recordsCrossModuleTest/api/pom.xml | 22 +++++++++++ .../itest/records/api/CustomerDto.java | 13 +++++++ .../recordsCrossModuleTest/mapper/pom.xml | 30 ++++++++++++++ .../itest/records/mapper/CustomerEntity.java | 31 +++++++++++++++ .../itest/records/mapper/CustomerMapper.java | 28 +++++++++++++ .../itest/records/mapper/RecordsTest.java | 39 +++++++++++++++++++ .../resources/recordsCrossModuleTest/pom.xml | 26 +++++++++++++ 8 files changed, 196 insertions(+) create mode 100644 integrationtest/src/test/resources/recordsCrossModuleTest/api/pom.xml create mode 100644 integrationtest/src/test/resources/recordsCrossModuleTest/api/src/main/java/org/mapstruct/itest/records/api/CustomerDto.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleTest/mapper/pom.xml create mode 100644 integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/main/java/org/mapstruct/itest/records/mapper/CustomerEntity.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/main/java/org/mapstruct/itest/records/mapper/CustomerMapper.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleTest/mapper/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleTest/pom.xml diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index e82f13a018..2ec0ba9aa9 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -109,6 +109,13 @@ void protobufBuilderTest() { void recordsTest() { } + @ProcessorTest(baseDir = "recordsCrossModuleTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC + }) + @EnabledForJreRange(min = JRE.JAVA_17) + void recordsCrossModuleTest() { + } + @ProcessorTest(baseDir = "kotlinDataTest", processorTypes = { ProcessorTest.ProcessorType.JAVAC }, forkJvm = true) diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/api/pom.xml b/integrationtest/src/test/resources/recordsCrossModuleTest/api/pom.xml new file mode 100644 index 0000000000..362f2d3840 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleTest/api/pom.xml @@ -0,0 +1,22 @@ + + + + 4.0.0 + + + recordsCrossModuleTest + org.mapstruct + 1.0.0 + + + records-cross-module-api + + diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/api/src/main/java/org/mapstruct/itest/records/api/CustomerDto.java b/integrationtest/src/test/resources/recordsCrossModuleTest/api/src/main/java/org/mapstruct/itest/records/api/CustomerDto.java new file mode 100644 index 0000000000..344aa79d96 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleTest/api/src/main/java/org/mapstruct/itest/records/api/CustomerDto.java @@ -0,0 +1,13 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.api; + +/** + * @author Filip Hrisafov + */ +public record CustomerDto(String name, String email) { + +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/pom.xml b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/pom.xml new file mode 100644 index 0000000000..6fc250ed45 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/pom.xml @@ -0,0 +1,30 @@ + + + + 4.0.0 + + + recordsCrossModuleTest + org.mapstruct + 1.0.0 + + + records-cross-module-mapper + + + + + org.mapstruct + records-cross-module-api + 1.0.0 + + + diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/main/java/org/mapstruct/itest/records/mapper/CustomerEntity.java b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/main/java/org/mapstruct/itest/records/mapper/CustomerEntity.java new file mode 100644 index 0000000000..51bcdaa8e1 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/main/java/org/mapstruct/itest/records/mapper/CustomerEntity.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.mapper; + +/** + * @author Filip Hrisafov + */ +public class CustomerEntity { + + private String name; + private String mail; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/main/java/org/mapstruct/itest/records/mapper/CustomerMapper.java b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/main/java/org/mapstruct/itest/records/mapper/CustomerMapper.java new file mode 100644 index 0000000000..7b679a4245 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/main/java/org/mapstruct/itest/records/mapper/CustomerMapper.java @@ -0,0 +1,28 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.mapper; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; +import org.mapstruct.itest.records.api.CustomerDto; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CustomerMapper { + + CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class ); + + @Mapping(target = "mail", source = "email") + CustomerEntity fromRecord(CustomerDto record); + + @InheritInverseConfiguration + CustomerDto toRecord(CustomerEntity entity); + +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java new file mode 100644 index 0000000000..2f274792b8 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java @@ -0,0 +1,39 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.mapper; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.mapstruct.itest.records.api.CustomerDto; +import org.mapstruct.itest.records.mapper.CustomerEntity; +import org.mapstruct.itest.records.mapper.CustomerMapper; + +public class RecordsTest { + + @Test + public void shouldMapRecord() { + CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) ); + + assertThat( customer ).isNotNull(); + assertThat( customer.getName() ).isEqualTo( "Kermit" ); + assertThat( customer.getMail() ).isEqualTo( "kermit@test.com" ); + } + + @Test + public void shouldMapIntoRecord() { + CustomerEntity entity = new CustomerEntity(); + entity.setName( "Kermit" ); + entity.setMail( "kermit@test.com" ); + + CustomerDto customer = CustomerMapper.INSTANCE.toRecord( entity ); + + assertThat( customer ).isNotNull(); + assertThat( customer.name() ).isEqualTo( "Kermit" ); + assertThat( customer.email() ).isEqualTo( "kermit@test.com" ); + } + +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/pom.xml b/integrationtest/src/test/resources/recordsCrossModuleTest/pom.xml new file mode 100644 index 0000000000..47c55ea410 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleTest/pom.xml @@ -0,0 +1,26 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + recordsCrossModuleTest + pom + + + api + mapper + + From 08016d9ef24a4fb59347953f1f0cba9bcecca810 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 12 Jun 2021 10:16:40 +0200 Subject: [PATCH 170/597] #2446 Use DefaultLocale and DefaultTimeZone from JUnit Pioneer Configure the JUnit Platform to run the processor tests in parallel by running different test classes in concurrent threads --- parent/pom.xml | 6 ++++ processor/pom.xml | 6 ++++ .../ap/internal/util/StringsTest.java | 34 +++---------------- .../test/bugs/_1523/java8/Issue1523Test.java | 23 +++---------- .../ap/test/builtin/BuiltInTest.java | 18 ++-------- .../ap/test/builtin/DatatypeFactoryTest.java | 18 ++-------- .../conversion/date/DateConversionTest.java | 18 ++-------- .../java8time/Java8TimeConversionTest.java | 23 +++---------- .../jodatime/JodaConversionTest.java | 18 ++-------- .../numbers/NumberFormatConversionTest.java | 18 ++-------- ...ingCompileOptionConstructorMapperTest.java | 18 ++-------- .../SpringConstructorMapperTest.java | 15 ++------ .../NestedMappingMethodInvocationTest.java | 18 ++-------- .../test/resources/junit-platform.properties | 3 ++ 14 files changed, 46 insertions(+), 190 deletions(-) create mode 100644 processor/src/test/resources/junit-platform.properties diff --git a/parent/pom.xml b/parent/pom.xml index a924b1cd68..bb1229fcb8 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -29,6 +29,7 @@ 1.6.0 8.36.1 5.8.0-M1 + 1.4.2 1 3.17.2 @@ -141,6 +142,11 @@ pom import + + org.junit-pioneer + junit-pioneer + ${junit-pioneer.version} + diff --git a/processor/pom.xml b/processor/pom.xml index 5aa6470ff9..fcc6dbbab8 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -115,6 +115,12 @@ test + + org.junit-pioneer + junit-pioneer + test + + joda-time diff --git a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java index 44fbb565ba..687b63f793 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java @@ -9,30 +9,15 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Locale; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.DefaultLocale; /** * @author Filip Hrisafov */ public class StringsTest { - private static final Locale TURKEY_LOCALE = getTurkeyLocale(); - private Locale defaultLocale; - - @BeforeEach - public void before() { - defaultLocale = Locale.getDefault(); - } - - @AfterEach - public void after() { - Locale.setDefault( defaultLocale ); - } - @Test public void testCapitalize() { assertThat( Strings.capitalize( null ) ).isNull(); @@ -137,40 +122,31 @@ public void findMostSimilarWord() { } @Test + @DefaultLocale("en") public void capitalizeEnglish() { - Locale.setDefault( Locale.ENGLISH ); String international = Strings.capitalize( "international" ); assertThat( international ).isEqualTo( "International" ); } @Test + @DefaultLocale("en") public void decapitalizeEnglish() { - Locale.setDefault( Locale.ENGLISH ); String international = Strings.decapitalize( "International" ); assertThat( international ).isEqualTo( "international" ); } @Test + @DefaultLocale("tr") public void capitalizeTurkish() { - Locale.setDefault( TURKEY_LOCALE ); String international = Strings.capitalize( "international" ); assertThat( international ).isEqualTo( "International" ); } @Test + @DefaultLocale("tr") public void decapitalizeTurkish() { - Locale.setDefault( TURKEY_LOCALE ); String international = Strings.decapitalize( "International" ); assertThat( international ).isEqualTo( "international" ); } - private static Locale getTurkeyLocale() { - Locale turkeyLocale = Locale.forLanguageTag( "tr" ); - - if ( turkeyLocale == null ) { - throw new IllegalStateException( "Can't find Turkey locale." ); - } - - return turkeyLocale; - } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java index f528432c18..825e318356 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1523/java8/Issue1523Test.java @@ -9,8 +9,7 @@ import java.util.Calendar; import java.util.TimeZone; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.junitpioneer.jupiter.DefaultTimeZone; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -31,24 +30,12 @@ Target.class }) @IssueKey("1523") +// we want to test that the timezone will correctly be used in mapped XMLGregorianCalendar and not the +// default one, so we must ensure that we use a different timezone than the default one -> set the default +// one explicitly to UTC +@DefaultTimeZone("UTC") public class Issue1523Test { - private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault(); - - @BeforeAll - public static void before() { - // we want to test that the timezone will correctly be used in mapped XMLGregorianCalendar and not the - // default one, so we must ensure that we use a different timezone than the default one -> set the default - // one explicitly to UTC - TimeZone.setDefault( TimeZone.getTimeZone( "UTC" ) ); - } - - @AfterAll - public static void after() { - // revert the changed default TZ - TimeZone.setDefault( DEFAULT_TIMEZONE ); - } - @ProcessorTest public void testThatCorrectTimeZoneWillBeUsedInTarget() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java index e6cd7a19b4..0f1983a851 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java @@ -17,15 +17,13 @@ import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; -import java.util.TimeZone; import javax.xml.bind.JAXBElement; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.junitpioneer.jupiter.DefaultTimeZone; import org.mapstruct.ap.test.builtin._target.IterableTarget; import org.mapstruct.ap.test.builtin._target.MapTarget; import org.mapstruct.ap.test.builtin.bean.BigDecimalProperty; @@ -85,21 +83,9 @@ IterableSource.class, MapSource.class }) +@DefaultTimeZone("Europe/Berlin") public class BuiltInTest { - private static TimeZone originalTimeZone; - - @BeforeAll - public static void setDefaultTimeZoneToCet() { - originalTimeZone = TimeZone.getDefault(); - TimeZone.setDefault( TimeZone.getTimeZone( "Europe/Berlin" ) ); - } - - @AfterAll - public static void restoreOriginalTimeZone() { - TimeZone.setDefault( originalTimeZone ); - } - @ProcessorTest @WithClasses( JaxbMapper.class ) public void shouldApplyBuiltInOnJAXBElement() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java index 85c1bed399..1db8d3abcf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/DatatypeFactoryTest.java @@ -10,10 +10,8 @@ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; -import java.util.TimeZone; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.junitpioneer.jupiter.DefaultTimeZone; import org.mapstruct.ap.test.builtin.bean.CalendarProperty; import org.mapstruct.ap.test.builtin.bean.DatatypeFactory; import org.mapstruct.ap.test.builtin.bean.DateProperty; @@ -32,21 +30,9 @@ DateProperty.class } ) +@DefaultTimeZone("Europe/Berlin") public class DatatypeFactoryTest { - private TimeZone originalTimeZone; - - @BeforeEach - public void setUp() { - originalTimeZone = TimeZone.getDefault(); - TimeZone.setDefault( TimeZone.getTimeZone( "Europe/Berlin" ) ); - } - - @AfterEach - public void tearDown() { - TimeZone.setDefault( originalTimeZone ); - } - @ProcessorTest public void testNoConflictsWithOwnDatatypeFactory() throws ParseException { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java index 363b287b71..83464f81a0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java @@ -12,13 +12,11 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.List; -import java.util.Locale; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.EnabledOnJre; import org.junit.jupiter.api.condition.JRE; +import org.junitpioneer.jupiter.DefaultLocale; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -36,21 +34,9 @@ SourceTargetMapper.class }) @IssueKey("43") +@DefaultLocale("de") public class DateConversionTest { - private Locale originalLocale; - - @BeforeEach - public void setDefaultLocale() { - originalLocale = Locale.getDefault(); - Locale.setDefault( Locale.GERMAN ); - } - - @AfterEach - public void tearDown() { - Locale.setDefault( originalLocale ); - } - @ProcessorTest @EnabledOnJre( JRE.JAVA_8 ) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java index b95b5d1acd..aa30865425 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java @@ -17,8 +17,7 @@ import java.util.Date; import java.util.TimeZone; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.junitpioneer.jupiter.DefaultTimeZone; import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; @@ -37,18 +36,6 @@ public class Java8TimeConversionTest { @RegisterExtension GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor( SourceTargetMapper.class ); - private TimeZone originalTimeZone; - - @BeforeEach - public void setUp() { - originalTimeZone = TimeZone.getDefault(); - } - - @AfterEach - public void tearDown() { - TimeZone.setDefault( originalTimeZone ); - } - @ProcessorTest public void testDateTimeToString() { Source src = new Source(); @@ -226,8 +213,8 @@ public void testCalendarMapping() { } @ProcessorTest + @DefaultTimeZone("UTC") public void testZonedDateTimeToDateMapping() { - TimeZone.setDefault( TimeZone.getTimeZone( "UTC" ) ); Source source = new Source(); ZonedDateTime dateTime = ZonedDateTime.of( LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) ); source.setForDateConversionWithZonedDateTime( @@ -266,8 +253,8 @@ public void testInstantToDateMapping() { } @ProcessorTest + @DefaultTimeZone("Australia/Melbourne") public void testLocalDateTimeToDateMapping() { - TimeZone.setDefault( TimeZone.getTimeZone( "Australia/Melbourne" ) ); Source source = new Source(); LocalDateTime dateTime = LocalDateTime.of( 2014, 1, 1, 0, 0 ); @@ -292,8 +279,8 @@ public void testLocalDateTimeToDateMapping() { } @ProcessorTest + @DefaultTimeZone("Australia/Melbourne") public void testLocalDateToDateMapping() { - TimeZone.setDefault( TimeZone.getTimeZone( "Australia/Melbourne" ) ); Source source = new Source(); LocalDate localDate = LocalDate.of( 2016, 3, 1 ); @@ -316,8 +303,8 @@ public void testLocalDateToDateMapping() { } @ProcessorTest + @DefaultTimeZone("Australia/Melbourne") public void testLocalDateToSqlDateMapping() { - TimeZone.setDefault( TimeZone.getTimeZone( "Australia/Melbourne" ) ); Source source = new Source(); LocalDate localDate = LocalDate.of( 2016, 3, 1 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java index e861497169..001b6390e2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java @@ -6,7 +6,6 @@ package org.mapstruct.ap.test.conversion.jodatime; import java.util.Calendar; -import java.util.Locale; import java.util.TimeZone; import org.joda.time.DateTime; @@ -14,11 +13,10 @@ import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.EnabledOnJre; import org.junit.jupiter.api.condition.JRE; +import org.junitpioneer.jupiter.DefaultLocale; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -32,21 +30,9 @@ */ @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) @IssueKey("75") +@DefaultLocale("de") public class JodaConversionTest { - private Locale originalLocale; - - @BeforeEach - public void setDefaultLocale() { - originalLocale = Locale.getDefault(); - Locale.setDefault( Locale.GERMAN ); - } - - @AfterEach - public void tearDown() { - Locale.setDefault( originalLocale ); - } - @ProcessorTest public void testDateTimeToString() { Source src = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java index ea1dd0da60..6dfc1b3ca1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java @@ -10,11 +10,9 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.junitpioneer.jupiter.DefaultLocale; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -26,21 +24,9 @@ Target.class, SourceTargetMapper.class }) +@DefaultLocale("en") public class NumberFormatConversionTest { - private Locale originalLocale; - - @BeforeEach - public void setDefaultLocale() { - originalLocale = Locale.getDefault(); - Locale.setDefault( Locale.ENGLISH ); - } - - @AfterEach - public void tearDown() { - Locale.setDefault( originalLocale ); - } - @ProcessorTest public void shouldApplyStringConversions() { Source source = new Source(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java index 8043c24305..b106a5d788 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java @@ -8,13 +8,11 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.TimeZone; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.DefaultTimeZone; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordDto; @@ -54,10 +52,9 @@ @ProcessorOption( name = "mapstruct.defaultInjectionStrategy", value = "constructor") @ComponentScan(basePackageClasses = CustomerSpringCompileOptionConstructorMapper.class) @Configuration +@DefaultTimeZone("Europe/Berlin") public class SpringCompileOptionConstructorMapperTest { - private static TimeZone originalTimeZone; - @RegisterExtension final GeneratedSource generatedSource = new GeneratedSource(); @@ -65,17 +62,6 @@ public class SpringCompileOptionConstructorMapperTest { private CustomerRecordSpringCompileOptionConstructorMapper customerRecordMapper; private ConfigurableApplicationContext context; - @BeforeAll - public static void setDefaultTimeZoneToCet() { - originalTimeZone = TimeZone.getDefault(); - TimeZone.setDefault( TimeZone.getTimeZone( "Europe/Berlin" ) ); - } - - @AfterAll - public static void restoreOriginalTimeZone() { - TimeZone.setDefault( originalTimeZone ); - } - @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java index 1346ac1ee8..1ff3fc8428 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java @@ -10,11 +10,10 @@ import java.util.Date; import java.util.TimeZone; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.DefaultTimeZone; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordDto; @@ -54,6 +53,7 @@ @IssueKey( "571" ) @ComponentScan(basePackageClasses = CustomerSpringConstructorMapper.class) @Configuration +@DefaultTimeZone("Europe/Berlin") public class SpringConstructorMapperTest { private static TimeZone originalTimeZone; @@ -65,17 +65,6 @@ public class SpringConstructorMapperTest { private CustomerRecordSpringConstructorMapper customerRecordMapper; private ConfigurableApplicationContext context; - @BeforeAll - public static void setDefaultTimeZoneToCet() { - originalTimeZone = TimeZone.getDefault(); - TimeZone.setDefault( TimeZone.getTimeZone( "Europe/Berlin" ) ); - } - - @AfterAll - public static void restoreOriginalTimeZone() { - TimeZone.setDefault( originalTimeZone ); - } - @BeforeEach public void springUp() { context = new AnnotationConfigApplicationContext( getClass() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java index 375f524d68..1e9b52cf62 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java @@ -9,7 +9,6 @@ import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; -import java.util.Locale; import javax.xml.bind.JAXBElement; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConstants; @@ -17,8 +16,7 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.junitpioneer.jupiter.DefaultLocale; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -31,23 +29,11 @@ * @author Sjaak Derksen */ @IssueKey("134") +@DefaultLocale("de") public class NestedMappingMethodInvocationTest { public static final QName QNAME = new QName( "dont-care" ); - private Locale originalLocale; - - @BeforeEach - public void setDefaultLocale() { - originalLocale = Locale.getDefault(); - Locale.setDefault( Locale.GERMAN ); - } - - @AfterEach - public void tearDown() { - Locale.setDefault( originalLocale ); - } - @ProcessorTest @WithClasses( { OrderTypeToOrderDtoMapper.class, diff --git a/processor/src/test/resources/junit-platform.properties b/processor/src/test/resources/junit-platform.properties new file mode 100644 index 0000000000..97a956feff --- /dev/null +++ b/processor/src/test/resources/junit-platform.properties @@ -0,0 +1,3 @@ +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.mode.default = same_thread +junit.jupiter.execution.parallel.mode.classes.default = concurrent From 046077f7011fd63e78205d5a8a3d1d0af6e98956 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 13 Jun 2021 10:02:11 +0200 Subject: [PATCH 171/597] #2483: Update Asciidoctor to latest versions --- documentation/pom.xml | 9 ++++----- .../src/main/asciidoc/mapstruct-reference-guide.asciidoc | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/documentation/pom.xml b/documentation/pom.xml index 0426805375..8850da641c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -21,9 +21,9 @@ MapStruct Documentation - 1.5.3 - 1.6.2 - 9.2.6.0 + 1.6.0 + 2.5.1 + 9.2.17.0 @@ -33,7 +33,7 @@ org.asciidoctor asciidoctor-maven-plugin - 1.6.0 + 2.1.0 org.asciidoctor @@ -52,7 +52,6 @@ - coderay mapstruct-reference-guide.asciidoc ${project.version} diff --git a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc index b83c874c5e..7bffbca5c7 100644 --- a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc +++ b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc @@ -2,6 +2,7 @@ :revdate: {docdate} :toc: right :sectanchors: +:source-highlighter: coderay :Author: Gunnar Morling, Andreas Gudian, Sjaak Derksen, Filip Hrisafov and the MapStruct community :processor-dir: ../../../../processor :processor-ap-test: {processor-dir}/src/test/java/org/mapstruct/ap/test From 55c62ab43f150710e1939ed7b05b295a0d5dd8aa Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 13 Jun 2021 12:36:42 +0200 Subject: [PATCH 172/597] #2108 Make sure Javadoc can be generated with Java 11 Remove the org.jboss.apiviz.APIviz doclet since it is no longer compatible with Java 11. Add new group in the Javadoc for the MapStruct Processor SPI Fix Javadoc warnings --- .../java/org/mapstruct/DecoratedWith.java | 2 +- .../InheritInverseConfiguration.java | 2 +- core/src/main/java/org/mapstruct/Mapping.java | 1 - .../main/java/org/mapstruct/ValueMapping.java | 24 +++------ .../org/mapstruct/control/MappingControl.java | 2 +- distribution/pom.xml | 50 ++++++++++++++++--- .../mapstruct/ap/internal/model/Mapper.java | 4 ++ .../model/ObjectFactoryMethodResolver.java | 5 +- .../model/beanmapping/AbstractReference.java | 12 ++--- .../model/beanmapping/TargetReference.java | 2 +- .../model/source/BeanMappingOptions.java | 2 + .../internal/model/source/MappingOptions.java | 3 ++ .../model/source/SelectionParameters.java | 5 +- .../ap/internal/util/accessor/Accessor.java | 4 +- .../org/mapstruct/ap/spi/BuilderInfo.java | 11 ++++ 15 files changed, 85 insertions(+), 44 deletions(-) diff --git a/core/src/main/java/org/mapstruct/DecoratedWith.java b/core/src/main/java/org/mapstruct/DecoratedWith.java index 7f740a8983..78b2c580ff 100644 --- a/core/src/main/java/org/mapstruct/DecoratedWith.java +++ b/core/src/main/java/org/mapstruct/DecoratedWith.java @@ -22,7 +22,7 @@ *

      * NOTE: This annotation is not supported for the component model {@code cdi}. Use CDI's own * {@code @Decorator} feature instead. - *

      + *

      *

      Examples

      *

      * For the examples below, consider the following mapper declaration: diff --git a/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java b/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java index 3063ca07c9..ac582a5dfa 100644 --- a/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java +++ b/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java @@ -59,7 +59,7 @@ * } * } * - *

      + * *

      
        * @Mapper
        * public interface CarMapper {
      diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java
      index b4bfa30d88..0af73f3687 100644
      --- a/core/src/main/java/org/mapstruct/Mapping.java
      +++ b/core/src/main/java/org/mapstruct/Mapping.java
      @@ -372,7 +372,6 @@
            *     }
            * 
      *

      - *

      * Any types referenced in expressions must be given via their fully-qualified name. Alternatively, types can be * imported via {@link Mapper#imports()}. *

      diff --git a/core/src/main/java/org/mapstruct/ValueMapping.java b/core/src/main/java/org/mapstruct/ValueMapping.java index 2d4a586366..21ea5f4a83 100644 --- a/core/src/main/java/org/mapstruct/ValueMapping.java +++ b/core/src/main/java/org/mapstruct/ValueMapping.java @@ -18,12 +18,9 @@ *

        *
      1. Enumeration to Enumeration
      2. *
      - *

      - * Example 1: - * - *

      - * 
      + * Example 1:
        *
      + * 
      
        * public enum OrderType { RETAIL, B2B, C2C, EXTRA, STANDARD, NORMAL }
        *
        * public enum ExternalOrderType { RETAIL, B2B, SPECIAL, DEFAULT }
      @@ -46,12 +43,9 @@
        * +---------------------+----------------------------+
        * 
      * - *

      - * Example 2: - * - *

      - * 
      + * Example 2:
        *
      + * 
      
        * @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ),
        * @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ),
        * @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" )
      @@ -70,17 +64,13 @@
        * +---------------------+----------------------------+
        * 
      * - *

      - * Example 3: - *

      + * Example 3: * - *

      MapStruct will WARN on incomplete mappings. However, if for some reason no match is found, an + * MapStruct will WARN on incomplete mappings. However, if for some reason no match is found, an * {@link java.lang.IllegalStateException} will be thrown. This compile-time error can be avoided by * using {@link MappingConstants#THROW_EXCEPTION} for {@link ValueMapping#target()}. It will result an * {@link java.lang.IllegalArgumentException} at runtime. - *
      - * 
      - *
      + * 
      
        * @ValueMapping( source = "STANDARD", target = "DEFAULT" ),
        * @ValueMapping( source = "C2C", target = MappingConstants.THROW_EXCEPTION )
        * ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
      diff --git a/core/src/main/java/org/mapstruct/control/MappingControl.java b/core/src/main/java/org/mapstruct/control/MappingControl.java
      index 1d416dafd7..c44797602a 100644
      --- a/core/src/main/java/org/mapstruct/control/MappingControl.java
      +++ b/core/src/main/java/org/mapstruct/control/MappingControl.java
      @@ -132,7 +132,7 @@ enum Use {
                * This means if source type and target type are of the same type, MapStruct will not perform
                * any mappings anymore and assign the target to the source direct.
                * 

      - * An exception are types from the package {@link java}, which will be mapped always directly. + * An exception are types from the package {@code java}, which will be mapped always directly. * * @since 1.4 */ diff --git a/distribution/pom.xml b/distribution/pom.xml index 30beb1d319..337ad35e51 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -34,6 +34,11 @@ org.freemarker freemarker + + + org.mapstruct.tools.gem + gem-api + @@ -96,18 +101,16 @@ MapStruct API org.mapstruct* + + MapStruct Processor SPI + org.mapstruct.ap.spi* + MapStruct Processor org.mapstruct.ap* - org.jboss.apiviz.APIviz - - org.jboss.apiviz - apiviz - 1.3.2.GA - true UTF-8 UTF-8 @@ -116,6 +119,23 @@ true true true + + + + + if (typeof useModuleDirectories !== 'undefined') { + useModuleDirectories = false; + } + + ]]> + + --allow-script-in-comments + @@ -156,4 +176,22 @@ + + + + jdk-11-or-newer + + [11 + + + + javax.xml.bind + jaxb-api + 2.3.1 + provided + true + + + + diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java index db63a8431d..a066b123c9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java @@ -162,6 +162,10 @@ protected String getTemplateName() { /** * Returns the same as {@link Class#getName()} but without the package declaration. + * + * @param element the element that should be flattened + * + * @return the flat name for the type element */ public static String getFlatName(TypeElement element) { if (!(element.getEnclosingElement() instanceof TypeElement)) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index f0bd9c4dff..397a001a4f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -40,10 +40,9 @@ private ObjectFactoryMethodResolver() { * * @param method target mapping method * @param selectionParameters parameters used in the selection process - * @param ctx + * @param ctx the mapping builder context * * @return a method reference to the factory method, or null if no suitable, or ambiguous method found - * */ public static MethodReference getFactoryMethod( Method method, SelectionParameters selectionParameters, @@ -57,7 +56,7 @@ public static MethodReference getFactoryMethod( Method method, * @param method target mapping method * @param alternativeTarget alternative to {@link Method#getResultType()} e.g. when target is abstract * @param selectionParameters parameters used in the selection process - * @param ctx + * @param ctx the mapping builder context * * @return a method reference to the factory method, or null if no suitable, or ambiguous method found * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/AbstractReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/AbstractReference.java index 4db93505e3..72f2b8c8fe 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/AbstractReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/AbstractReference.java @@ -56,8 +56,7 @@ public List getElementNames() { } /** - * returns the property name on the shallowest nesting level - * @return + * @return the property name on the shallowest nesting level */ public PropertyEntry getShallowestProperty() { if ( propertyEntries.isEmpty() ) { @@ -67,8 +66,7 @@ public PropertyEntry getShallowestProperty() { } /** - * returns the property name on the shallowest nesting level - * @return + * @return the property name on the shallowest nesting level */ public String getShallowestPropertyName() { if ( propertyEntries.isEmpty() ) { @@ -78,8 +76,7 @@ public String getShallowestPropertyName() { } /** - * returns the property name on the deepest nesting level - * @return + * @return the property name on the deepest nesting level */ public PropertyEntry getDeepestProperty() { if ( propertyEntries.isEmpty() ) { @@ -89,8 +86,7 @@ public PropertyEntry getDeepestProperty() { } /** - * returns the property name on the deepest nesting level - * @return + * @return the property name on the deepest nesting level */ public String getDeepestPropertyName() { if ( propertyEntries.isEmpty() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java index 22bfd6343b..961e519561 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/TargetReference.java @@ -80,7 +80,7 @@ public List getElementNames() { } /** - * returns the property name on the shallowest nesting level + * @return the property name on the shallowest nesting level */ public String getShallowestPropertyName() { if ( propertyEntries.isEmpty() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index 78800e6053..9567dd80bf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -39,6 +39,8 @@ public class BeanMappingOptions extends DelegatingOptions { /** * creates a mapping for inheritance. Will set * + * @param beanMapping the bean mapping options that should be used + * * @return new mapping */ public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index d7d6ad6291..cb496a3a31 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -502,6 +502,9 @@ public MappingOptions copyForInverseInheritance(SourceMethod templateMethod, /** * Creates a copy of this mapping * + * @param templateMethod the template method for the inheritance + * @param beanMappingOptions the bean mapping options + * * @return the copy */ public MappingOptions copyForForwardInheritance(SourceMethod templateMethod, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java index 697f0d9e4a..fb76c13629 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java @@ -34,8 +34,9 @@ public class SelectionParameters { * * ResultType is not inherited. * - * @param selectionParameters - * @return + * @param selectionParameters the selection parameters that need to be copied + * + * @return the selection parameters based on the given ones */ public static SelectionParameters forInheritance(SelectionParameters selectionParameters) { return new SelectionParameters( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java index 314302a14e..d624b7788a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/Accessor.java @@ -45,9 +45,7 @@ public interface Accessor { Element getElement(); /** - * The accessor type - * - * @return + * @return type of the accessor */ AccessorType getAccessorType(); } diff --git a/processor/src/main/java/org/mapstruct/ap/spi/BuilderInfo.java b/processor/src/main/java/org/mapstruct/ap/spi/BuilderInfo.java index 0f95567481..5e37987f75 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/BuilderInfo.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/BuilderInfo.java @@ -54,6 +54,10 @@ public static class Builder { private Collection buildMethods; /** + * @param method The creation method for the builder + * + * @return the builder for chaining + * * @see BuilderInfo#getBuilderCreationMethod() */ public Builder builderCreationMethod(ExecutableElement method) { @@ -62,6 +66,10 @@ public Builder builderCreationMethod(ExecutableElement method) { } /** + * @param methods the build methods for the type + * + * @return the builder for chaining + * * @see BuilderInfo#getBuildMethods() */ public Builder buildMethod(Collection methods) { @@ -71,6 +79,9 @@ public Builder buildMethod(Collection methods) { /** * Create the {@link BuilderInfo}. + * + * @return the created {@link BuilderInfo} + * * @throws IllegalArgumentException if the builder creation or build methods are {@code null} */ public BuilderInfo build() { From 7f38efad4d21cf88a2b68ab1c164846e83978b47 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 19 Jun 2021 13:56:26 +0200 Subject: [PATCH 173/597] #2463 Selection stops when method type-args are flipped (#2487) --- .../internal/model/source/MethodMatcher.java | 15 ++-- .../multiple/MultipleTypeVarTest.java | 16 ++++ ...eVarBothArgumentsFlippedGenericMapper.java | 76 +++++++++++++++++++ .../objectfactory/ObjectFactoryMapper.java | 61 +++++++++++++++ .../objectfactory/ObjectFactoryTest.java | 28 +++++++ 5 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index 136828adfc..f08e4a9152 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -248,10 +248,12 @@ boolean getCandidates(Type aCandidateMethodType, Type matchingType, Map String method( List in ) Type.ResolvedPair resolved = mthdParType.resolveParameterToType( matchingType, aCandidateMethodType ); - if ( resolved == null ) { - // cannot find a candidate type, but should have since the typeFromCandidateMethod had parameters - // to be resolved - return !hasGenericTypeParameters( aCandidateMethodType ); + if ( resolved.getMatch() == null ) { + // we should be dealing with something containing a type parameter at this point. This is + // covered with the checks above. Therefore resolved itself cannot be null. + // If there is no match here, continue with the next candidate, perhaps there will a match with + // the next method type parameter + continue; } // resolved something at this point, a candidate can be fetched or created @@ -270,13 +272,10 @@ boolean getCandidates(Type aCandidateMethodType, Type matchingType, Map HashMap toMap( Pair entry) { + HashMap result = new HashMap<>( ); + result.put( entry.first, entry.second ); + return result; + } + + class Source { + + private Pair prop; + + public Source(Pair prop) { + this.prop = prop; + } + + public Pair getProp() { + return prop; + } + } + + class Target { + + private Map prop; + + public Map getProp() { + return prop; + } + + public Target setProp(Map prop) { + this.prop = prop; + return this; + } + } + + class Pair { + private final T first; + private final U second; + + public Pair(T first, U second) { + this.first = first; + this.second = second; + } + + public T getFirst() { + return first; + } + + public U getSecond() { + return second; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java new file mode 100644 index 0000000000..2ea183bfc9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java @@ -0,0 +1,61 @@ +/* + * 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.selection.methodgenerics.objectfactory; + +import org.mapstruct.Mapper; +import org.mapstruct.ObjectFactory; +import org.mapstruct.TargetType; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ObjectFactoryMapper { + + ObjectFactoryMapper INSTANCE = Mappers.getMapper( ObjectFactoryMapper.class ); + + TargetA toTarget(SourceA source); + + @ObjectFactory + default T createTarget(S source, @TargetType Class targetType) { + if ( source.isA() ) { + return (T) new TargetA(); + } + return (T) new TargetB(); + } + + abstract class Source { + public abstract boolean isA(); + } + + class SourceA extends Source { + @Override + public boolean isA() { + return true; + } + } + + class SourceB extends Source { + @Override + public boolean isA() { + return false; + } + } + + abstract class Target { + } + + class TargetA extends Target { + + private TargetA() { + } + } + + class TargetB extends Target { + + private TargetB() { + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryTest.java new file mode 100644 index 0000000000..21392710ba --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryTest.java @@ -0,0 +1,28 @@ +/* + * 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.selection.methodgenerics.objectfactory; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +public class ObjectFactoryTest { + + @IssueKey( "2463" ) + @ProcessorTest + @WithClasses( ObjectFactoryMapper.class ) + public void testSelectionOfFactoryMethod() { + + ObjectFactoryMapper.SourceA source = new ObjectFactoryMapper.SourceA(); + + ObjectFactoryMapper.Target target = ObjectFactoryMapper.INSTANCE.toTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target ).isInstanceOf( ObjectFactoryMapper.TargetA.class ); + } +} From 934a47323a4a183054fc445fdb441bd42d34e0e8 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 12 Jun 2021 10:41:39 +0200 Subject: [PATCH 174/597] #2478 Make sure that forged methods do not generate duplicate method parameters --- .../ap/internal/model/ForgedMethod.java | 12 ++- .../ap/test/bugs/_2478/Issue2478Mapper.java | 86 +++++++++++++++++++ .../ap/test/bugs/_2478/Issue2478Test.java | 33 +++++++ 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2478/Issue2478Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2478/Issue2478Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java index 820a09c22a..b97d8be111 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java @@ -10,6 +10,7 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import javax.lang.model.element.ExecutableElement; import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; @@ -125,7 +126,16 @@ private ForgedMethod(String name, Type sourceType, Type returnType, List( 1 + additionalParameters.size() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2478/Issue2478Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2478/Issue2478Mapper.java new file mode 100644 index 0000000000..3cec3e0b3c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2478/Issue2478Mapper.java @@ -0,0 +1,86 @@ +/* + * 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._2478; + +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2478Mapper { + + Issue2478Mapper INSTANCE = Mappers.getMapper( Issue2478Mapper.class ); + + ProductEntity productFromDto(Product dto, @Context Shop shop); + + class Product { + protected final String name; + protected final Shop shop; + + public Product(String name, Shop shop) { + this.name = name; + this.shop = shop; + } + + public String getName() { + return name; + } + + public Shop getShop() { + return shop; + } + } + + class Shop { + protected final String name; + + public Shop(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + class ProductEntity { + + protected String name; + protected ShopEntity shop; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ShopEntity getShop() { + return shop; + } + + public void setShop(ShopEntity shop) { + this.shop = shop; + } + } + + class ShopEntity { + protected String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2478/Issue2478Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2478/Issue2478Test.java new file mode 100644 index 0000000000..05e21f4c40 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2478/Issue2478Test.java @@ -0,0 +1,33 @@ +/* + * 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._2478; + +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 Filip Hrisafov + */ +@IssueKey("2478") +@WithClasses({ + Issue2478Mapper.class +}) +class Issue2478Test { + + @ProcessorTest + void shouldGenerateCodeThatCompiles() { + Issue2478Mapper.Product dto = new Issue2478Mapper.Product( "Test", new Issue2478Mapper.Shop( "Shopify" ) ); + Issue2478Mapper.ProductEntity entity = Issue2478Mapper.INSTANCE.productFromDto( dto, dto.getShop() ); + + assertThat( entity ).isNotNull(); + assertThat( entity.getName() ).isEqualTo( "Test" ); + assertThat( entity.getShop() ).isNotNull(); + assertThat( entity.getShop().getName() ).isEqualTo( "Shopify" ); + } +} From 845d83e9d510aa2405cbe82167ff35bb983fb5ac Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 5 Jun 2021 11:39:52 +0200 Subject: [PATCH 175/597] #2439 Do not throw NPE getting accessors for null typeElement Provide better error message if the source type has no read properties --- .../model/beanmapping/SourceReference.java | 36 +++++++++----- .../ap/internal/model/common/Type.java | 15 ++++-- .../mapstruct/ap/internal/util/Message.java | 1 + .../bugs/_2439/ErroneousIssue2439Mapper.java | 48 +++++++++++++++++++ .../ap/test/bugs/_2439/Issuer2439Test.java | 38 +++++++++++++++ 5 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2439/ErroneousIssue2439Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2439/Issuer2439Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index c87c6fb46d..4f2d0957e7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.type.DeclaredType; @@ -277,17 +278,30 @@ private void reportErrorOnNoMatch( Parameter parameter, String[] propertyNames, notFoundPropertyIndex = entries.size(); sourceType = last( entries ).getType(); } - String mostSimilarWord = Strings.getMostSimilarWord( - propertyNames[notFoundPropertyIndex], - sourceType.getPropertyReadAccessors().keySet() - ); - List elements = new ArrayList<>( - Arrays.asList( propertyNames ).subList( 0, notFoundPropertyIndex ) - ); - elements.add( mostSimilarWord ); - reportMappingError( - Message.PROPERTYMAPPING_INVALID_PROPERTY_NAME, sourceName, Strings.join( elements, "." ) - ); + + Set readProperties = sourceType.getPropertyReadAccessors().keySet(); + + if ( !readProperties.isEmpty() ) { + String mostSimilarWord = Strings.getMostSimilarWord( + propertyNames[notFoundPropertyIndex], + readProperties + ); + + List elements = new ArrayList<>( + Arrays.asList( propertyNames ).subList( 0, notFoundPropertyIndex ) + ); + elements.add( mostSimilarWord ); + reportMappingError( + Message.PROPERTYMAPPING_INVALID_PROPERTY_NAME, sourceName, Strings.join( elements, "." ) + ); + } + else { + reportMappingError( + Message.PROPERTYMAPPING_INVALID_PROPERTY_NAME_SOURCE_HAS_NO_PROPERTIES, + sourceName, + sourceType.describe() + ); + } } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 15f787829b..4da9128f98 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -693,7 +694,7 @@ else if ( candidate.getAccessorType() == AccessorType.FIELD && ( Executables.is public List getRecordComponents() { if ( recordComponents == null ) { - recordComponents = filters.recordComponentsIn( typeElement ); + recordComponents = nullSafeTypeElementListConversion( filters::recordComponentsIn ); } return recordComponents; @@ -720,7 +721,7 @@ else if ( candidate.getAccessorType() == AccessorType.GETTER private List getAllMethods() { if ( allMethods == null ) { - allMethods = elementUtils.getAllEnclosedExecutableElements( typeElement ); + allMethods = nullSafeTypeElementListConversion( elementUtils::getAllEnclosedExecutableElements ); } return allMethods; @@ -728,12 +729,20 @@ private List getAllMethods() { private List getAllFields() { if ( allFields == null ) { - allFields = elementUtils.getAllEnclosedFields( typeElement ); + allFields = nullSafeTypeElementListConversion( elementUtils::getAllEnclosedFields ); } return allFields; } + private List nullSafeTypeElementListConversion(Function> conversionFunction) { + if ( typeElement != null ) { + return conversionFunction.apply( typeElement ); + } + + return Collections.emptyList(); + } + private String getPropertyName(Accessor accessor ) { Element accessorElement = accessor.getElement(); if ( accessorElement instanceof ExecutableElement ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index cb182af6b9..4c474186d3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -70,6 +70,7 @@ public enum Message { PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no source parameter named \"%s\". Method source parameters are: \"%s\"." ), PROPERTYMAPPING_NO_PROPERTY_IN_PARAMETER( "The type of parameter \"%s\" has no property named \"%s\"." ), PROPERTYMAPPING_INVALID_PROPERTY_NAME( "No property named \"%s\" exists in source parameter(s). Did you mean \"%s\"?" ), + PROPERTYMAPPING_INVALID_PROPERTY_NAME_SOURCE_HAS_NO_PROPERTIES( "No property named \"%s\" exists in source parameter(s). Type \"%s\" has no properties." ), PROPERTYMAPPING_NO_PRESENCE_CHECKER_FOR_SOURCE_TYPE( "Using custom source value presence checking strategy, but no presence checker found for %s in source type." ), PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE( "No read accessor found for property \"%s\" in target type." ), PROPERTYMAPPING_NO_WRITE_ACCESSOR_FOR_TARGET_TYPE( "No write accessor found for property \"%s\" in target type." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2439/ErroneousIssue2439Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2439/ErroneousIssue2439Mapper.java new file mode 100644 index 0000000000..a154d55fff --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2439/ErroneousIssue2439Mapper.java @@ -0,0 +1,48 @@ +/* + * 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._2439; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousIssue2439Mapper { + + @Mapping(target = "modeName", source = "mode.desc") + LiveDto map(LiveEntity entity); + + class LiveEntity { + private final LiveMode[] mode; + + public LiveEntity(LiveMode... mode) { + this.mode = mode; + } + + public LiveMode[] getMode() { + return mode; + } + } + + class LiveDto { + private String modeName; + + public String getModeName() { + return modeName; + } + + public void setModeName(String modeName) { + this.modeName = modeName; + } + } + + enum LiveMode { + TEST, + PROD, + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2439/Issuer2439Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2439/Issuer2439Test.java new file mode 100644 index 0000000000..c3c4702927 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2439/Issuer2439Test.java @@ -0,0 +1,38 @@ +/* + * 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._2439; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2439") +@WithClasses({ + ErroneousIssue2439Mapper.class +}) +class Issuer2439Test { + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousIssue2439Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + message = "No property named \"mode.desc\" exists in source parameter(s)." + + " Type \"ErroneousIssue2439Mapper.LiveMode[]\" has no properties.") + } + ) + void shouldProvideGoodErrorMessage() { + + } +} From 5d8fcfa033f8f51614a7b07d791d1ba94b5df7f2 Mon Sep 17 00:00:00 2001 From: Tobias Meggendorfer Date: Sun, 13 Jun 2021 11:28:04 +0200 Subject: [PATCH 176/597] #2481 Report ignored source properties which are missing --- .../ap/internal/model/BeanMappingMethod.java | 25 ++++++++++++++- .../internal/model/source/MapperOptions.java | 1 - .../mapstruct/ap/internal/util/Message.java | 1 + .../ErroneousSourceTargetMapper.java | 21 +++++++++++++ .../MissingIgnoredSourceTest.java | 31 +++++++++++++++++++ 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/ErroneousSourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/MissingIgnoredSourceTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 324df63cc8..2dba4637fd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -98,6 +98,7 @@ public static class Builder { private Map unprocessedConstructorProperties; private Map unprocessedTargetProperties; private Map unprocessedSourceProperties; + private Set missingIgnoredSourceProperties; private Set targetProperties; private final List propertyMappings = new ArrayList<>(); private final Set unprocessedSourceParameters = new HashSet<>(); @@ -248,9 +249,12 @@ else if ( !method.isUpdateMethod() ) { } // get bean mapping (when specified as annotation ) + this.missingIgnoredSourceProperties = new HashSet<>(); if ( beanMapping != null ) { for ( String ignoreUnmapped : beanMapping.getIgnoreUnmappedSourceProperties() ) { - unprocessedSourceProperties.remove( ignoreUnmapped ); + if ( unprocessedSourceProperties.remove( ignoreUnmapped ) == null ) { + missingIgnoredSourceProperties.add( ignoreUnmapped ); + } } } @@ -283,6 +287,7 @@ else if ( !method.isUpdateMethod() ) { // report errors on unmapped properties reportErrorForUnmappedTargetPropertiesIfRequired(); reportErrorForUnmappedSourcePropertiesIfRequired(); + reportErrorForMissingIgnoredSourceProperties(); // mapNullToDefault boolean mapNullToDefault = method.getOptions() @@ -1511,6 +1516,24 @@ private void reportErrorForUnmappedSourcePropertiesIfRequired() { ); } } + + private void reportErrorForMissingIgnoredSourceProperties() { + if ( !missingIgnoredSourceProperties.isEmpty() ) { + Object[] args = new Object[] { + MessageFormat.format( + "{0,choice,1#property|1 imports() { public ReportingPolicyGem unmappedTargetPolicy() { return mapper.unmappedTargetPolicy().hasValue() ? ReportingPolicyGem.valueOf( mapper.unmappedTargetPolicy().get() ) : next().unmappedTargetPolicy(); - } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 4c474186d3..4bacef6a07 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -37,6 +37,7 @@ public enum Message { BEANMAPPING_UNMAPPED_FORGED_TARGETS_ERROR( "Unmapped target %s. Mapping from %s to %s." ), BEANMAPPING_UNMAPPED_SOURCES_WARNING( "Unmapped source %s.", Diagnostic.Kind.WARNING ), BEANMAPPING_UNMAPPED_SOURCES_ERROR( "Unmapped source %s." ), + BEANMAPPING_MISSING_IGNORED_SOURCES_ERROR( "Ignored unknown source %s." ), BEANMAPPING_CYCLE_BETWEEN_PROPERTIES( "Cycle(s) between properties given via dependsOn(): %s." ), BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON( "\"%s\" is no property of the method return type." ), BEANMAPPING_IGNORE_BY_DEFAULT_WITH_MAPPING_TARGET_THIS( "Using @BeanMapping( ignoreByDefault = true ) with @Mapping( target = \".\", ... ) is not allowed. You'll need to explicitly ignore the target properties that should be ignored instead." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/ErroneousSourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/ErroneousSourceTargetMapper.java new file mode 100644 index 0000000000..659867ca40 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/ErroneousSourceTargetMapper.java @@ -0,0 +1,21 @@ +/* + * 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.missingignoredsource; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper( + unmappedTargetPolicy = ReportingPolicy.IGNORE, + unmappedSourcePolicy = ReportingPolicy.IGNORE) +public interface ErroneousSourceTargetMapper { + ErroneousSourceTargetMapper INSTANCE = Mappers.getMapper( ErroneousSourceTargetMapper.class ); + + @BeanMapping(ignoreUnmappedSourceProperties = "bar") + Object sourceToTarget(Object source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/MissingIgnoredSourceTest.java b/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/MissingIgnoredSourceTest.java new file mode 100644 index 0000000000..77f80c5537 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/MissingIgnoredSourceTest.java @@ -0,0 +1,31 @@ +/* + * 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.missingignoredsource; + +import javax.tools.Diagnostic.Kind; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +public class MissingIgnoredSourceTest { + + @ProcessorTest + @WithClasses({ ErroneousSourceTargetMapper.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousSourceTargetMapper.class, + kind = Kind.ERROR, + line = 20, + message = "Ignored unknown source property: \"bar\".") + } + ) + public void shouldRaiseErrorDueToMissingIgnoredSourceProperty() { + } +} From 4c338fa1db0c6af9b73b5a3376f611efe22e507d Mon Sep 17 00:00:00 2001 From: chaos Date: Wed, 23 Jun 2021 10:31:30 +0800 Subject: [PATCH 177/597] fix(pom): fix gradle repo url 404 original url is 404 replace with new url --- integrationtest/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 17a6a4a9a9..4b48c19496 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -28,7 +28,7 @@ gradle - https://repo.gradle.org/gradle/libs-releases + https://repo.gradle.org/artifactory/libs-releases/ true From 1bf698785c795bf7d3832a129ff5ca3cb170ff3e Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Jun 2021 19:19:02 +0200 Subject: [PATCH 178/597] Fix typo in Named.java (#2500) --- core/src/main/java/org/mapstruct/Named.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/mapstruct/Named.java b/core/src/main/java/org/mapstruct/Named.java index 0b6e8b53a9..773886a7b1 100644 --- a/core/src/main/java/org/mapstruct/Named.java +++ b/core/src/main/java/org/mapstruct/Named.java @@ -14,7 +14,7 @@ * Marks mapping methods with the given qualifier name. Can be used to qualify a single method or all methods of a given * type by specifying this annotation on the type level. *

      - * Will be used to to select the correct mapping methods when mapping a bean property type, element of an iterable type + * Will be used to select the correct mapping methods when mapping a bean property type, element of an iterable type * or the key/value of a map type. *

      * Example (both methods of {@code Titles} are capable to convert a string, but the ambiguity is resolved by applying From c5c292f602b7a60e762d1cbac04283e3330c6d87 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 26 Jun 2021 11:28:37 +0200 Subject: [PATCH 179/597] #2501 Add test case --- .../ap/test/bugs/_2501/Issue2501Mapper.java | 62 +++++++++++++++++++ .../ap/test/bugs/_2501/Issue2501Test.java | 30 +++++++++ 2 files changed, 92 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2501/Issue2501Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2501/Issue2501Test.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2501/Issue2501Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2501/Issue2501Mapper.java new file mode 100644 index 0000000000..08fa637791 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2501/Issue2501Mapper.java @@ -0,0 +1,62 @@ +/* + * 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._2501; + +import java.util.Optional; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2501Mapper { + + Issue2501Mapper INSTANCE = Mappers.getMapper( Issue2501Mapper.class ); + + Customer map(CustomerDTO value); + + CustomerStatus map(DtoStatus status); + + default T unwrap(Optional optional) { + return optional.orElse( null ); + } + + enum CustomerStatus { + ENABLED, DISABLED, + } + + class Customer { + + private CustomerStatus status; + + public CustomerStatus getStatus() { + return status; + } + + public void setStatus(CustomerStatus stat) { + this.status = stat; + } + } + + enum DtoStatus { + ENABLED, DISABLED + } + + class CustomerDTO { + + private DtoStatus status; + + public Optional getStatus() { + return Optional.ofNullable( status ); + } + + public void setStatus(DtoStatus stat) { + this.status = stat; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2501/Issue2501Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2501/Issue2501Test.java new file mode 100644 index 0000000000..c5eb1e0aed --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2501/Issue2501Test.java @@ -0,0 +1,30 @@ +/* + * 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._2501; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Issue2501Mapper.class +}) +class Issue2501Test { + + @ProcessorTest + void shouldUnwrapEnumOptional() { + Issue2501Mapper.CustomerDTO source = new Issue2501Mapper.CustomerDTO(); + source.setStatus( Issue2501Mapper.DtoStatus.DISABLED ); + + Issue2501Mapper.Customer target = Issue2501Mapper.INSTANCE.map( source ); + + assertThat( target.getStatus() ).isEqualTo( Issue2501Mapper.CustomerStatus.DISABLED ); + } +} From fb9c7a3dedcc751917109cae05d33026c12f92c6 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 19 Jun 2021 17:11:04 +0200 Subject: [PATCH 180/597] #2491 Do not use types not part of java.base in MapStruct processor MapStruct should not use types that are outside of java.base. This makes sure that no additional dependencies (such as jaxb-api) are needed on the annotation processor path --- .../ProcessorInvocationInterceptor.java | 6 +- .../testutil/extension/ProcessorTest.java | 12 +++ integrationtest/src/test/resources/pom.xml | 32 ++++++++ .../common/DateFormatValidatorFactory.java | 2 +- .../AbstractToXmlGregorianCalendar.java | 14 ++-- .../source/builtin/BuiltInMappingMethods.java | 5 +- .../model/source/builtin/JaxbElemToValue.java | 5 +- ...daLocalDateTimeToXmlGregorianCalendar.java | 4 +- .../JodaLocalDateToXmlGregorianCalendar.java | 4 +- .../JodaLocalTimeToXmlGregorianCalendar.java | 4 +- .../LocalDateTimeToXmlGregorianCalendar.java | 5 +- .../LocalDateToXmlGregorianCalendar.java | 4 +- .../XmlGregorianCalendarToCalendar.java | 4 +- .../builtin/XmlGregorianCalendarToDate.java | 4 +- .../XmlGregorianCalendarToJodaDateTime.java | 7 +- .../XmlGregorianCalendarToJodaLocalDate.java | 7 +- ...lGregorianCalendarToJodaLocalDateTime.java | 7 +- .../XmlGregorianCalendarToJodaLocalTime.java | 7 +- .../XmlGregorianCalendarToLocalDate.java | 4 +- .../XmlGregorianCalendarToLocalDateTime.java | 8 +- .../builtin/XmlGregorianCalendarToString.java | 4 +- .../ap/internal/util/ClassUtils.java | 76 ------------------- .../ap/internal/util/JaxbConstants.java | 10 --- .../ap/internal/util/XmlConstants.java | 17 ++--- 24 files changed, 98 insertions(+), 154 deletions(-) delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java index 839bf86f18..89009aaffe 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java @@ -136,7 +136,11 @@ private void addAdditionalCliArguments(Verifier verifier) private void configureProcessor(Verifier verifier) { String compilerId = processorTestContext.getProcessor().getCompilerId(); if ( compilerId != null ) { - verifier.addCliOption( "-Pgenerate-via-compiler-plugin" ); + String profile = processorTestContext.getProcessor().getProfile(); + if ( profile == null ) { + profile = "generate-via-compiler-plugin"; + } + verifier.addCliOption( "-P" + profile ); verifier.addCliOption( "-Dcompiler-id=" + compilerId ); } else { diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java index eb8553960c..95480eee9d 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java @@ -44,6 +44,7 @@ enum ProcessorType { JAVAC( "javac" ), + JAVAC_WITH_PATHS( "javac", JRE.OTHER, "generate-via-compiler-plugin-with-annotation-processor-paths" ), ECLIPSE_JDT( "jdt", JRE.JAVA_8 ), @@ -51,14 +52,20 @@ enum ProcessorType { private final String compilerId; private final JRE max; + private final String profile; ProcessorType(String compilerId) { this( compilerId, JRE.OTHER ); } ProcessorType(String compilerId, JRE max) { + this( compilerId, max, null ); + } + + ProcessorType(String compilerId, JRE max, String profile) { this.compilerId = compilerId; this.max = max; + this.profile = profile; } public String getCompilerId() { @@ -68,6 +75,10 @@ public String getCompilerId() { public JRE maxJre() { return max; } + + public String getProfile() { + return profile; + } } /** @@ -98,6 +109,7 @@ Collection getAdditionalCommandLineArguments(ProcessorType processorType */ ProcessorType[] processorTypes() default { ProcessorType.JAVAC, + ProcessorType.JAVAC_WITH_PATHS, ProcessorType.ECLIPSE_JDT, ProcessorType.PROCESSOR_PLUGIN }; diff --git a/integrationtest/src/test/resources/pom.xml b/integrationtest/src/test/resources/pom.xml index e209daf234..c8a19228f0 100644 --- a/integrationtest/src/test/resources/pom.xml +++ b/integrationtest/src/test/resources/pom.xml @@ -65,6 +65,38 @@ + + generate-via-compiler-plugin-with-annotation-processor-paths + + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + \${compiler-id} + + + ${project.groupId} + mapstruct-processor + ${mapstruct.version} + + + + + + org.eclipse.tycho + tycho-compiler-jdt + ${org.eclipse.tycho.compiler-jdt.version} + + + + + + generate-via-processor-plugin diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java index 8e8c328f20..09e4957aad 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java @@ -75,7 +75,7 @@ public DateFormatValidationResult validate(String dateFormat) { private static boolean isXmlGregorianCalendarSupposedToBeMapped(Type sourceType, Type targetType) { return typesEqualsOneOf( - sourceType, targetType, XmlConstants.JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR ); + sourceType, targetType, XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ); } private static boolean isJodaDateTimeSupposed(Type sourceType, Type targetType) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java index 7a2ac5588f..e2ccc3f476 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java @@ -9,16 +9,12 @@ import java.util.Set; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeFactory; -import javax.xml.datatype.XMLGregorianCalendar; - import org.mapstruct.ap.internal.model.common.ConstructorFragment; import org.mapstruct.ap.internal.model.common.FieldReference; import org.mapstruct.ap.internal.model.common.FinalField; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.internal.util.XmlConstants; /** * @author Sjaak Derksen @@ -30,12 +26,12 @@ public abstract class AbstractToXmlGregorianCalendar extends BuiltInMethod { private final Type dataTypeFactoryType; public AbstractToXmlGregorianCalendar(TypeFactory typeFactory) { - this.returnType = typeFactory.getType( XMLGregorianCalendar.class ); - this.dataTypeFactoryType = typeFactory.getType( DatatypeFactory.class ); + this.returnType = typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ); + this.dataTypeFactoryType = typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_FACTORY ); this.importTypes = asSet( returnType, dataTypeFactoryType, - typeFactory.getType( DatatypeConfigurationException.class ) + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONFIGURATION_EXCEPTION ) ); } @@ -51,7 +47,7 @@ public Type getReturnType() { @Override public FieldReference getFieldReference() { - return new FinalField( dataTypeFactoryType, Strings.decapitalize( DatatypeFactory.class.getSimpleName() ) ); + return new FinalField( dataTypeFactoryType, "datatypeFactory" ); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java index 4b096f8035..0edae7f10f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java @@ -59,12 +59,11 @@ public BuiltInMappingMethods(TypeFactory typeFactory) { } private static boolean isJaxbAvailable(TypeFactory typeFactory) { - return JaxbConstants.isJaxbElementPresent() && typeFactory.isTypeAvailable( JaxbConstants.JAXB_ELEMENT_FQN ); + return typeFactory.isTypeAvailable( JaxbConstants.JAXB_ELEMENT_FQN ); } private static boolean isXmlGregorianCalendarAvailable(TypeFactory typeFactory) { - return XmlConstants.isXmlGregorianCalendarPresent() && - typeFactory.isTypeAvailable( XmlConstants.JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR ); + return typeFactory.isTypeAvailable( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ); } private static boolean isJodaTimeAvailable(TypeFactory typeFactory) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java index 4d116266fd..0d3d4c30af 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java @@ -9,11 +9,10 @@ import java.util.Set; -import javax.xml.bind.JAXBElement; - import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.JaxbConstants; /** * @author Sjaak Derksen @@ -25,7 +24,7 @@ public class JaxbElemToValue extends BuiltInMethod { private final Set importTypes; public JaxbElemToValue(TypeFactory typeFactory) { - Type type = typeFactory.getType( JAXBElement.class ); + Type type = typeFactory.getType( JaxbConstants.JAXB_ELEMENT_FQN ); this.parameter = new Parameter( "element", type ); this.returnType = type.getTypeParameters().get( 0 ); this.importTypes = asSet( parameter.getType() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateTimeToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateTimeToXmlGregorianCalendar.java index fa87bff2db..80fa9ba326 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateTimeToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateTimeToXmlGregorianCalendar.java @@ -6,12 +6,12 @@ package org.mapstruct.ap.internal.model.source.builtin; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.JodaTimeConstants; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -28,7 +28,7 @@ public JodaLocalDateTimeToXmlGregorianCalendar(TypeFactory typeFactory) { this.parameter = new Parameter( "dt", typeFactory.getType( JodaTimeConstants.LOCAL_DATE_TIME_FQN ) ); this.importTypes = asSet( parameter.getType(), - typeFactory.getType( DatatypeConstants.class ) + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateToXmlGregorianCalendar.java index e958ca952e..37c89ef360 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateToXmlGregorianCalendar.java @@ -6,12 +6,12 @@ package org.mapstruct.ap.internal.model.source.builtin; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.JodaTimeConstants; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -28,7 +28,7 @@ public JodaLocalDateToXmlGregorianCalendar(TypeFactory typeFactory) { this.parameter = new Parameter( "dt", typeFactory.getType( JodaTimeConstants.LOCAL_DATE_FQN ) ); this.importTypes = asSet( parameter.getType(), - typeFactory.getType( DatatypeConstants.class ) + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalTimeToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalTimeToXmlGregorianCalendar.java index e9daac39c8..b29c7aec54 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalTimeToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalTimeToXmlGregorianCalendar.java @@ -6,12 +6,12 @@ package org.mapstruct.ap.internal.model.source.builtin; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.JodaTimeConstants; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -28,7 +28,7 @@ public JodaLocalTimeToXmlGregorianCalendar(TypeFactory typeFactory) { this.parameter = new Parameter( "dt", typeFactory.getType( JodaTimeConstants.LOCAL_TIME_FQN ) ); this.importTypes = asSet( parameter.getType(), - typeFactory.getType( DatatypeConstants.class ) + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateTimeToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateTimeToXmlGregorianCalendar.java index 8853c1aae7..178a77b5fa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateTimeToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateTimeToXmlGregorianCalendar.java @@ -9,11 +9,10 @@ import java.time.temporal.ChronoField; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; - import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -30,7 +29,7 @@ public LocalDateTimeToXmlGregorianCalendar(TypeFactory typeFactory) { this.parameter = new Parameter( "localDateTime", typeFactory.getType( LocalDateTime.class ) ); this.importTypes = asSet( parameter.getType(), - typeFactory.getType( DatatypeConstants.class ), + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ), typeFactory.getType( ChronoField.class ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateToXmlGregorianCalendar.java index 96c8a8a31b..2e2544ade0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateToXmlGregorianCalendar.java @@ -7,11 +7,11 @@ import java.time.LocalDate; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -28,7 +28,7 @@ public LocalDateToXmlGregorianCalendar(TypeFactory typeFactory) { this.parameter = new Parameter( "localDate", typeFactory.getType( LocalDate.class ) ); this.importTypes = asSet( parameter.getType(), - typeFactory.getType( DatatypeConstants.class ) + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToCalendar.java index f4d8ceee9b..03927fd869 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToCalendar.java @@ -7,11 +7,11 @@ import java.util.Calendar; import java.util.Set; -import javax.xml.datatype.XMLGregorianCalendar; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -25,7 +25,7 @@ public class XmlGregorianCalendarToCalendar extends BuiltInMethod { private final Set importTypes; public XmlGregorianCalendarToCalendar(TypeFactory typeFactory) { - this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) ); + this.parameter = new Parameter( "xcal", typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ) ); this.returnType = typeFactory.getType( Calendar.class ); this.importTypes = asSet( returnType, parameter.getType() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToDate.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToDate.java index 7f270be5f6..38f76a66ee 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToDate.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToDate.java @@ -7,11 +7,11 @@ import java.util.Date; import java.util.Set; -import javax.xml.datatype.XMLGregorianCalendar; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -25,7 +25,7 @@ public class XmlGregorianCalendarToDate extends BuiltInMethod { private final Set importTypes; public XmlGregorianCalendarToDate(TypeFactory typeFactory) { - this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) ); + this.parameter = new Parameter( "xcal", typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ) ); this.returnType = typeFactory.getType( Date.class ); this.importTypes = asSet( returnType, parameter.getType() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaDateTime.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaDateTime.java index ab2db49e8a..ab6658bfc5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaDateTime.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaDateTime.java @@ -6,13 +6,12 @@ package org.mapstruct.ap.internal.model.source.builtin; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; -import javax.xml.datatype.XMLGregorianCalendar; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.JodaTimeConstants; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -26,10 +25,10 @@ public class XmlGregorianCalendarToJodaDateTime extends BuiltInMethod { private final Set importTypes; public XmlGregorianCalendarToJodaDateTime(TypeFactory typeFactory) { - this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) ); + this.parameter = new Parameter( "xcal", typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ) ); this.returnType = typeFactory.getType( JodaTimeConstants.DATE_TIME_FQN ); this.importTypes = asSet( - typeFactory.getType( DatatypeConstants.class ), + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ), typeFactory.getType( JodaTimeConstants.DATE_TIME_ZONE_FQN ), returnType, parameter.getType() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDate.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDate.java index d26cde0215..f45488755c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDate.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDate.java @@ -6,13 +6,12 @@ package org.mapstruct.ap.internal.model.source.builtin; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; -import javax.xml.datatype.XMLGregorianCalendar; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.JodaTimeConstants; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -26,10 +25,10 @@ public class XmlGregorianCalendarToJodaLocalDate extends BuiltInMethod { private final Set importTypes; public XmlGregorianCalendarToJodaLocalDate(TypeFactory typeFactory) { - this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) ); + this.parameter = new Parameter( "xcal", typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ) ); this.returnType = typeFactory.getType( JodaTimeConstants.LOCAL_DATE_FQN ); this.importTypes = asSet( - typeFactory.getType( DatatypeConstants.class ), + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ), returnType, parameter.getType() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDateTime.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDateTime.java index 700c19c211..af7fd9e9fd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDateTime.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDateTime.java @@ -6,13 +6,12 @@ package org.mapstruct.ap.internal.model.source.builtin; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; -import javax.xml.datatype.XMLGregorianCalendar; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.JodaTimeConstants; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -26,10 +25,10 @@ public class XmlGregorianCalendarToJodaLocalDateTime extends BuiltInMethod { private final Set importTypes; public XmlGregorianCalendarToJodaLocalDateTime(TypeFactory typeFactory) { - this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) ); + this.parameter = new Parameter( "xcal", typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ) ); this.returnType = typeFactory.getType( JodaTimeConstants.LOCAL_DATE_TIME_FQN ); this.importTypes = asSet( - typeFactory.getType( DatatypeConstants.class ), + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ), returnType, parameter.getType() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalTime.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalTime.java index b3eae20314..3725691134 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalTime.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalTime.java @@ -6,13 +6,12 @@ package org.mapstruct.ap.internal.model.source.builtin; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; -import javax.xml.datatype.XMLGregorianCalendar; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.JodaTimeConstants; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -26,10 +25,10 @@ public class XmlGregorianCalendarToJodaLocalTime extends BuiltInMethod { private final Set importTypes; public XmlGregorianCalendarToJodaLocalTime(TypeFactory typeFactory) { - this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) ); + this.parameter = new Parameter( "xcal", typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ) ); this.returnType = typeFactory.getType( JodaTimeConstants.LOCAL_TIME_FQN ); this.importTypes = asSet( - typeFactory.getType( DatatypeConstants.class ), + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ), returnType, parameter.getType() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDate.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDate.java index 1da6951b9a..2c9ced3413 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDate.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDate.java @@ -7,11 +7,11 @@ import java.time.LocalDate; import java.util.Set; -import javax.xml.datatype.XMLGregorianCalendar; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -25,7 +25,7 @@ public class XmlGregorianCalendarToLocalDate extends BuiltInMethod { private final Set importTypes; public XmlGregorianCalendarToLocalDate(TypeFactory typeFactory) { - this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) ); + this.parameter = new Parameter( "xcal", typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ) ); this.returnType = typeFactory.getType( LocalDate.class ); this.importTypes = asSet( returnType, parameter.getType() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDateTime.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDateTime.java index 5d29892444..b676bd03a4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDateTime.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDateTime.java @@ -9,12 +9,10 @@ import java.time.LocalDateTime; import java.util.Set; -import javax.xml.datatype.DatatypeConstants; -import javax.xml.datatype.XMLGregorianCalendar; - import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -28,12 +26,12 @@ public class XmlGregorianCalendarToLocalDateTime extends BuiltInMethod { private final Set importTypes; public XmlGregorianCalendarToLocalDateTime(TypeFactory typeFactory) { - this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) ); + this.parameter = new Parameter( "xcal", typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ) ); this.returnType = typeFactory.getType( LocalDateTime.class ); this.importTypes = asSet( returnType, parameter.getType(), - typeFactory.getType( DatatypeConstants.class ), + typeFactory.getType( XmlConstants.JAVAX_XML_DATATYPE_CONSTANTS ), typeFactory.getType( Duration.class ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToString.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToString.java index 73ac51fa2a..c932da6675 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToString.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToString.java @@ -8,12 +8,12 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Set; -import javax.xml.datatype.XMLGregorianCalendar; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.XmlConstants; import static org.mapstruct.ap.internal.util.Collections.asSet; @@ -27,7 +27,7 @@ public class XmlGregorianCalendarToString extends BuiltInMethod { private final Set importTypes; public XmlGregorianCalendarToString(TypeFactory typeFactory) { - this.parameter = new Parameter( "xcal", typeFactory.getType( XMLGregorianCalendar.class ) ); + this.parameter = new Parameter( "xcal", typeFactory.getType( XmlConstants.JAVAX_XML_XML_GREGORIAN_CALENDAR ) ); this.returnType = typeFactory.getType( String.class ); this.importTypes = asSet( parameter.getType(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java deleted file mode 100644 index d66d81f9b0..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.internal.util; - -/** - * Utilities for working with classes. It is mainly needed because using the {@link ElementUtils} - * is not always correct. For example when compiling with JDK 9 and source version 8 classes from different modules - * are available by {@link ElementUtils#getTypeElement(CharSequence)} but they are actually not - * if those modules are not added during compilation. - * - * @author Filip Hrisafov - */ -class ClassUtils { - - private ClassUtils() { - } - - /** - * Determine whether the {@link Class} identified by the supplied name is present - * and can be loaded. Will return {@code false} if either the class or - * one of its dependencies is not present or cannot be loaded. - * - * @param className the name of the class to check - * @param classLoader the class loader to use - * (may be {@code null}, which indicates the default class loader) - * - * @return whether the specified class is present - */ - static boolean isPresent(String className, ClassLoader classLoader) { - try { - ClassLoader classLoaderToUse = classLoader; - if ( classLoaderToUse == null ) { - classLoaderToUse = getDefaultClassLoader(); - } - classLoaderToUse.loadClass( className ); - return true; - } - catch ( ClassNotFoundException ex ) { - // Class or one of its dependencies is not present... - return false; - } - } - - /** - * Return the default ClassLoader to use: typically the thread context - * ClassLoader, if available; the ClassLoader that loaded the ClassUtils - * class will be used as fallback. - *

      Call this method if you intend to use the thread context ClassLoader - * in a scenario where you absolutely need a non-null ClassLoader reference: - * for example, for class path resource loading (but not necessarily for - * {@code Class.forName}, which accepts a {@code null} ClassLoader - * reference as well). - * - * @return the default ClassLoader (never {@code null}) - * - * @see Thread#getContextClassLoader() - */ - private static ClassLoader getDefaultClassLoader() { - ClassLoader cl = null; - try { - cl = Thread.currentThread().getContextClassLoader(); - } - catch ( Throwable ex ) { - // Cannot access thread context ClassLoader - falling back to system class loader... - } - if ( cl == null ) { - // No thread context class loader -> use class loader of this class. - cl = ClassUtils.class.getClassLoader(); - } - return cl; - } - -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java index f8f01cd18c..db18673ba2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java @@ -11,18 +11,8 @@ public final class JaxbConstants { public static final String JAXB_ELEMENT_FQN = "javax.xml.bind.JAXBElement"; - private static final boolean IS_JAXB_ELEMENT_PRESENT = ClassUtils.isPresent( - JAXB_ELEMENT_FQN, - JaxbConstants.class.getClassLoader() - ); private JaxbConstants() { } - /** - * @return {@code true} if {@link javax.xml.bind.JAXBElement} is present, {@code false} otherwise - */ - public static boolean isJaxbElementPresent() { - return IS_JAXB_ELEMENT_PRESENT; - } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/XmlConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/XmlConstants.java index 8216e83121..532b9f0bce 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/XmlConstants.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/XmlConstants.java @@ -12,19 +12,14 @@ */ public final class XmlConstants { - public static final String JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR = "javax.xml.datatype.XMLGregorianCalendar"; - private static final boolean IS_XML_GREGORIAN_CALENDAR_PRESENT = ClassUtils.isPresent( - JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR, - XmlConstants.class.getClassLoader() - ); + // CHECKSTYLE:OFF + public static final String JAVAX_XML_XML_GREGORIAN_CALENDAR = "javax.xml.datatype.XMLGregorianCalendar"; + public static final String JAVAX_XML_DATATYPE_CONFIGURATION_EXCEPTION = "javax.xml.datatype.DatatypeConfigurationException"; + public static final String JAVAX_XML_DATATYPE_FACTORY = "javax.xml.datatype.DatatypeFactory"; + public static final String JAVAX_XML_DATATYPE_CONSTANTS = "javax.xml.datatype.DatatypeConstants"; + // CHECKSTYLE:ON private XmlConstants() { } - /** - * @return {@code true} if the {@link javax.xml.datatype.XMLGregorianCalendar} is present, {@code false} otherwise - */ - public static boolean isXmlGregorianCalendarPresent() { - return IS_XML_GREGORIAN_CALENDAR_PRESENT; - } } From 985ca2fe64745607b003c71a87559dfbe8ffe482 Mon Sep 17 00:00:00 2001 From: Christian Kosmowski Date: Sat, 26 Jun 2021 11:18:02 +0200 Subject: [PATCH 181/597] #1075 Support for Mapping from Map to Bean Co-authored-by: Filip Hrisafov --- .../chapter-3-defining-a-mapper.asciidoc | 65 +++ .../ap/internal/model/BeanMappingMethod.java | 43 +- .../ap/internal/model/PropertyMapping.java | 24 ++ .../model/beanmapping/SourceReference.java | 46 +++ ...urceReferenceContainsKeyPresenceCheck.java | 59 +++ .../mapstruct/ap/internal/util/Message.java | 6 +- .../ap/internal/util/ValueProvider.java | 5 + .../internal/util/accessor/AccessorType.java | 2 + .../util/accessor/MapValueAccessor.java | 55 +++ .../accessor/MapValuePresenceChecker.java | 55 +++ ...ourceReferenceContainsKeyPresenceCheck.ftl | 9 + .../ap/test/frommap/FromMapMappingTest.java | 371 ++++++++++++++++++ .../test/frommap/MapToBeanDefinedMapper.java | 38 ++ .../frommap/MapToBeanFromMapAndNestedMap.java | 67 ++++ .../MapToBeanFromMapAndNestedSource.java | 79 ++++ .../frommap/MapToBeanFromMultipleSources.java | 70 ++++ .../test/frommap/MapToBeanImplicitMapper.java | 36 ++ ...eanImplicitUnmappedSourcePolicyMapper.java | 38 ++ ...ToBeanNonStringMapAsMultiSourceMapper.java | 55 +++ .../test/frommap/MapToBeanRawMapMapper.java | 36 ++ .../frommap/MapToBeanTypeCheckMapper.java | 36 ++ .../MapToBeanUpdateImplicitMapper.java | 47 +++ .../MapToBeanUsingMappingMethodMapper.java | 42 ++ .../frommap/MapToBeanWithDefaultMapper.java | 38 ++ .../ObjectMapToBeanWithQualifierMapper.java | 74 ++++ .../test/frommap/StringMapToBeanMapper.java | 67 ++++ ...apToBeanWithCustomPresenceCheckMapper.java | 74 ++++ 27 files changed, 1535 insertions(+), 2 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValueAccessor.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValuePresenceChecker.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.ftl create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/FromMapMappingTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanDefinedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedMap.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMultipleSources.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanImplicitMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanImplicitUnmappedSourcePolicyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanNonStringMapAsMultiSourceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanRawMapMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanTypeCheckMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUpdateImplicitMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUsingMappingMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanWithDefaultMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/ObjectMapToBeanWithQualifierMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/StringMapToBeanMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/StringMapToBeanWithCustomPresenceCheckMapper.java diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index f679d4494a..2d2b930114 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -650,3 +650,68 @@ public class PersonMapperImpl implements PersonMapper { } ---- ==== + +[[mapping-map-to-bean]] +=== Mapping Map to Bean + +There are situations when a mapping from a `Map` into a specific bean is needed. +MapStruct offers a transparent way of doing such a mapping by using the target bean properties (or defined through `Mapping#source`) to extract the values from the map. +Such a mapping looks like: + +.Example classes for mapping map to bean +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public class Customer { + + private Long id; + private String name; + + //getters and setter omitted for brevity +} + +@Mapper +public interface CustomerMapper { + + @Mapping(target = "name", source = "customerName") + Customer toCustomer(Map map); + +} +---- +==== + +.Generated mapper for mapping map to bean +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class CustomerMapperImpl implements CustomerMapper { + + @Override + public Customer toCustomer(Map map) { + // ... + if ( map.containsKey( "id" ) ) { + customer.setId( Integer.parseInt( map.get( "id" ) ) ); + } + if ( map.containsKey( "customerName" ) ) { + customer.setName( source.get( "customerName" ) ); + } + // ... + } +} +---- +==== + +[NOTE] +==== +All existing rules about mapping between different types and using other mappers defined with `Mapper#uses` or custom methods in the mappers are applied. +i.e. You can map from `Map` where for each property a conversion from `Integer` into the respective property will be needed. +==== + +[WARNING] +==== +When a raw map or a map that does not have a String as a key is used, then a warning will be generated. +The warning is not generated if the map itself is mapped into some other target property directly as is. +==== diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 2dba4637fd..09cc20a6ed 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -238,9 +238,11 @@ else if ( !method.isUpdateMethod() ) { for ( Parameter sourceParameter : method.getSourceParameters() ) { unprocessedSourceParameters.add( sourceParameter ); - if ( sourceParameter.getType().isPrimitive() || sourceParameter.getType().isArrayType() ) { + if ( sourceParameter.getType().isPrimitive() || sourceParameter.getType().isArrayType() || + sourceParameter.getType().isMapType() ) { continue; } + Map readAccessors = sourceParameter.getType().getPropertyReadAccessors(); for ( Entry entry : readAccessors.entrySet() ) { @@ -276,6 +278,7 @@ else if ( !method.isUpdateMethod() ) { // map parameters without a mapping applyParameterNameBasedMapping(); + } // Process the unprocessed defined targets @@ -288,6 +291,7 @@ else if ( !method.isUpdateMethod() ) { reportErrorForUnmappedTargetPropertiesIfRequired(); reportErrorForUnmappedSourcePropertiesIfRequired(); reportErrorForMissingIgnoredSourceProperties(); + reportErrorForUnusedSourceParameters(); // mapNullToDefault boolean mapNullToDefault = method.getOptions() @@ -1364,6 +1368,16 @@ private SourceReference getSourceRefByTargetName(Parameter sourceParameter, Stri return sourceRef; } + if ( sourceParameter.getType().isMapType() ) { + List typeParameters = sourceParameter.getType().getTypeParameters(); + if ( typeParameters.size() == 2 && typeParameters.get( 0 ).isString() ) { + return SourceReference.fromMapSource( + new String[] { targetPropertyName }, + sourceParameter + ); + } + } + Accessor sourceReadAccessor = sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName ); if ( sourceReadAccessor != null ) { @@ -1534,6 +1548,33 @@ private void reportErrorForMissingIgnoredSourceProperties() { ); } } + + private void reportErrorForUnusedSourceParameters() { + for ( Parameter sourceParameter : unprocessedSourceParameters ) { + Type parameterType = sourceParameter.getType(); + if ( parameterType.isMapType() ) { + // We are only going to output a warning for the source parameter if it was unused + // i.e. the intention of the user was most likely to use it as a mapping from Bean to Map + List typeParameters = parameterType.getTypeParameters(); + if ( typeParameters.size() != 2 || !typeParameters.get( 0 ).isString() ) { + Message message = typeParameters.isEmpty() ? + Message.MAPTOBEANMAPPING_RAW_MAP : + Message.MAPTOBEANMAPPING_WRONG_KEY_TYPE; + ctx.getMessager() + .printMessage( + method.getExecutable(), + message, + sourceParameter.getName(), + String.format( + "Map<%s,%s>", + !typeParameters.isEmpty() ? typeParameters.get( 0 ).describe() : "", + typeParameters.size() > 1 ? typeParameters.get( 1 ).describe() : "" + ) + ); + } + } + } + } } private static class ConstructorAccessor { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index b53b8f90ec..82d2dd53fa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -37,6 +37,7 @@ import org.mapstruct.ap.internal.model.presence.AllPresenceChecksPresenceCheck; import org.mapstruct.ap.internal.model.presence.JavaExpressionPresenceCheck; import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; +import org.mapstruct.ap.internal.model.presence.SourceReferenceContainsKeyPresenceCheck; import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck; import org.mapstruct.ap.internal.model.source.DelegatingOptions; import org.mapstruct.ap.internal.model.source.MappingControl; @@ -307,6 +308,9 @@ private Assignment forge( ) { else if ( sourceType.isMapType() && targetType.isMapType() ) { assignment = forgeMapMapping( sourceType, targetType, rightHandSide ); } + else if ( sourceType.isMapType() && !targetType.isMapType()) { + assignment = forgeMapToBeanMapping( sourceType, targetType, rightHandSide ); + } else if ( ( sourceType.isIterableType() && targetType.isStreamType() ) || ( sourceType.isStreamType() && targetType.isStreamType() ) || ( sourceType.isStreamType() && targetType.isIterableType() ) ) { @@ -656,6 +660,13 @@ private PresenceCheck getSourcePresenceCheckerRef(SourceReference sourceReferenc // in the forged method? PropertyEntry propertyEntry = sourceReference.getShallowestProperty(); if ( propertyEntry.getPresenceChecker() != null ) { + if (propertyEntry.getPresenceChecker().getAccessorType() == AccessorType.MAP_CONTAINS ) { + return new SourceReferenceContainsKeyPresenceCheck( + sourceParam.getName(), + propertyEntry.getPresenceChecker().getSimpleName() + ); + } + List presenceChecks = new ArrayList<>(); presenceChecks.add( new SourceReferenceMethodPresenceCheck( sourceParam.getName(), @@ -742,6 +753,19 @@ private Assignment forgeMapMapping(Type sourceType, Type targetType, SourceRHS s return createForgedAssignment( source, methodRef, mapMappingMethod ); } + private Assignment forgeMapToBeanMapping(Type sourceType, Type targetType, SourceRHS source) { + + targetType = targetType.withoutBounds(); + ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, "{}" ); + + BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder(); + final BeanMappingMethod mapToBeanMappingMethod = builder.mappingContext( ctx ) + .forgedMethod( methodRef ) + .build(); + + return createForgedAssignment( source, methodRef, mapToBeanMappingMethod ); + } + private Assignment forgeMapping(SourceRHS sourceRHS) { Type sourceType; if ( targetWriteAccessorType == AccessorType.ADDER ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index 4f2d0957e7..15b10b5782 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -7,13 +7,16 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; @@ -24,6 +27,8 @@ import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.MapValueAccessor; +import org.mapstruct.ap.internal.util.accessor.MapValuePresenceChecker; import static org.mapstruct.ap.internal.model.beanmapping.PropertyEntry.forSourceReference; import static org.mapstruct.ap.internal.util.Collections.last; @@ -52,6 +57,26 @@ */ public class SourceReference extends AbstractReference { + public static SourceReference fromMapSource(String[] segments, Parameter parameter) { + Type parameterType = parameter.getType(); + Type valueType = parameterType.getTypeParameters().get( 1 ); + + TypeElement typeElement = parameterType.getTypeElement(); + TypeMirror typeMirror = valueType.getTypeMirror(); + String simpleName = String.join( ".", segments ); + + MapValueAccessor mapValueAccessor = new MapValueAccessor( typeElement, typeMirror, simpleName ); + MapValuePresenceChecker mapValuePresenceChecker = new MapValuePresenceChecker( + typeElement, + typeMirror, + simpleName + ); + List entries = Collections.singletonList( + PropertyEntry.forSourceReference( segments, mapValueAccessor, mapValuePresenceChecker, valueType ) + ); + return new SourceReference( parameter, entries, true ); + } + /** * Builds a {@link SourceReference} from an {@code @Mappping}. */ @@ -149,6 +174,10 @@ public SourceReference build() { */ private SourceReference buildFromSingleSourceParameters(String[] segments, Parameter parameter) { + if ( canBeTreatedAsMapSourceType( parameter.getType() ) ) { + return fromMapSource( segments, parameter ); + } + boolean foundEntryMatch; String[] propertyNames = segments; @@ -185,6 +214,14 @@ private SourceReference buildFromSingleSourceParameters(String[] segments, Param */ private SourceReference buildFromMultipleSourceParameters(String[] segments, Parameter parameter) { + if (parameter != null && canBeTreatedAsMapSourceType( parameter.getType() )) { + String[] propertyNames = new String[0]; + if ( segments.length > 1 ) { + propertyNames = Arrays.copyOfRange( segments, 1, segments.length ); + } + return fromMapSource( propertyNames, parameter ); + } + boolean foundEntryMatch; String[] propertyNames = new String[0]; @@ -207,6 +244,15 @@ private SourceReference buildFromMultipleSourceParameters(String[] segments, Par return new SourceReference( parameter, entries, foundEntryMatch ); } + private boolean canBeTreatedAsMapSourceType(Type type) { + if ( !type.isMapType() ) { + return false; + } + + List typeParameters = type.getTypeParameters(); + return typeParameters.size() == 2 && typeParameters.get( 0 ).isString(); + } + /** * When there are more than one source parameters, the first segment name of the propery * needs to match the parameter name to avoid ambiguity diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.java new file mode 100644 index 0000000000..d4c011a051 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.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.internal.model.presence; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Filip Hrisafov + */ +public class SourceReferenceContainsKeyPresenceCheck extends ModelElement implements PresenceCheck { + + private final String sourceReference; + private final String propertyName; + + public SourceReferenceContainsKeyPresenceCheck(String sourceReference, String propertyName) { + this.sourceReference = sourceReference; + this.propertyName = propertyName; + } + + public String getSourceReference() { + return sourceReference; + } + + public String getPropertyName() { + return propertyName; + } + + @Override + public Set getImportTypes() { + return Collections.emptySet(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + SourceReferenceContainsKeyPresenceCheck that = (SourceReferenceContainsKeyPresenceCheck) o; + return Objects.equals( sourceReference, that.sourceReference ) && + Objects.equals( propertyName, that.propertyName ); + } + + @Override + public int hashCode() { + return Objects.hash( sourceReference, propertyName ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 4bacef6a07..f2226ab224 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -181,7 +181,11 @@ public enum Message { VALUEMAPPING_ANY_REMAINING_OR_UNMAPPED_MISSING( "Source = \"\" or \"\" is advisable for mapping of type String to an enum type.", Diagnostic.Kind.WARNING ), VALUEMAPPING_NON_EXISTING_CONSTANT_FROM_SPI( "Constant %s doesn't exist in enum type %s. Constant was returned from EnumMappingStrategy: %s"), VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." ), - VALUEMAPPING_THROW_EXCEPTION_SOURCE( "Source = \"\" is not allowed. Target = \"\" can only be used." ); + VALUEMAPPING_THROW_EXCEPTION_SOURCE( "Source = \"\" is not allowed. Target = \"\" can only be used." ), + + MAPTOBEANMAPPING_WRONG_KEY_TYPE( "The Map parameter \"%s\" cannot be used for property mapping. It must be typed with Map but it was typed with %s.", Diagnostic.Kind.WARNING ), + MAPTOBEANMAPPING_RAW_MAP( "The Map parameter \"%s\" cannot be used for property mapping. It must be typed with Map but it was raw.", Diagnostic.Kind.WARNING ), + ; // CHECKSTYLE:ON diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java index bbd73943db..16abd074dd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.util; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; /** * This a wrapper class which provides the value that needs to be used in the models. @@ -45,6 +46,10 @@ public static ValueProvider of(Accessor accessor) { return null; } String value = accessor.getSimpleName(); + if (accessor.getAccessorType() == AccessorType.MAP_GET ) { + value = "get( \"" + value + "\" )"; + return new ValueProvider( value ); + } if ( !accessor.getAccessorType().isFieldAssignment() ) { value += "()"; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java index 23448a9e98..4e3b7a4776 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java @@ -12,6 +12,8 @@ public enum AccessorType { GETTER, SETTER, ADDER, + MAP_GET, + MAP_CONTAINS, PRESENCE_CHECKER; public boolean isFieldAssignment() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValueAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValueAccessor.java new file mode 100644 index 0000000000..ab086d9b0e --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValueAccessor.java @@ -0,0 +1,55 @@ +/* + * 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.internal.util.accessor; + +import java.util.Collections; +import java.util.Set; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import javax.lang.model.type.TypeMirror; + +/** + * An {@link Accessor} that wraps a Map value. + * + * @author Christian Kosmowski + */ +public class MapValueAccessor implements Accessor { + + private final TypeMirror valueTypeMirror; + private final String simpleName; + private final Element element; + + public MapValueAccessor(Element element, TypeMirror valueTypeMirror, String simpleName) { + this.element = element; + this.valueTypeMirror = valueTypeMirror; + this.simpleName = simpleName; + } + + @Override + public TypeMirror getAccessedType() { + return valueTypeMirror; + } + + @Override + public String getSimpleName() { + return this.simpleName; + } + + @Override + public Set getModifiers() { + return Collections.emptySet(); + } + + @Override + public Element getElement() { + return this.element; + } + + @Override + public AccessorType getAccessorType() { + return AccessorType.MAP_GET; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValuePresenceChecker.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValuePresenceChecker.java new file mode 100644 index 0000000000..3f72ed86c1 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValuePresenceChecker.java @@ -0,0 +1,55 @@ +/* + * 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.internal.util.accessor; + +import java.util.Collections; +import java.util.Set; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import javax.lang.model.type.TypeMirror; + +/** + * An {@link Accessor} that wraps a Map value. + * + * @author Christian Kosmowski + */ +public class MapValuePresenceChecker implements Accessor { + + private final Element element; + private final TypeMirror valueTypeMirror; + private final String simpleName; + + public MapValuePresenceChecker(Element element, TypeMirror valueTypeMirror, String simpleName) { + this.element = element; + this.valueTypeMirror = valueTypeMirror; + this.simpleName = simpleName; + } + + @Override + public TypeMirror getAccessedType() { + return valueTypeMirror; + } + + @Override + public String getSimpleName() { + return this.simpleName; + } + + @Override + public Set getModifiers() { + return Collections.emptySet(); + } + + @Override + public Element getElement() { + return this.element; + } + + @Override + public AccessorType getAccessorType() { + return AccessorType.MAP_CONTAINS; + } +} diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.ftl new file mode 100644 index 0000000000..5cffc481a1 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.ftl @@ -0,0 +1,9 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.SourceReferenceContainsKeyPresenceCheck" --> +${sourceReference}.containsKey( "${propertyName}" ) \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/FromMapMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/FromMapMappingTest.java new file mode 100644 index 0000000000..548aecc1ec --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/FromMapMappingTest.java @@ -0,0 +1,371 @@ +/* + * 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.frommap; + +import java.text.ParseException; +import java.time.LocalDate; +import java.time.Month; +import java.time.format.DateTimeParseException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Nested; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.entry; + +/** + * @author Christian Kosmowski + */ +@IssueKey("1075") +class FromMapMappingTest { + + @Nested + @WithClasses({ + StringMapToBeanMapper.class + }) + class StringMapToBeanTests { + + @ProcessorTest + void fromNullMap() { + assertThat( StringMapToBeanMapper.INSTANCE.fromMap( null ) ).isNull(); + } + + @ProcessorTest + void fromEmptyMap() { + StringMapToBeanMapper.Order order = StringMapToBeanMapper.INSTANCE.fromMap( Collections.emptyMap() ); + + assertThat( order ).isNotNull(); + assertThat( order.getName() ).isNull(); + assertThat( order.getPrice() ).isEqualTo( 0.0 ); + assertThat( order.getOrderDate() ).isNull(); + assertThat( order.getShipmentDate() ).isNull(); + } + + @ProcessorTest + void fromFullMap() { + Map map = new HashMap<>(); + map.put( "name", "Jacket" ); + map.put( "price", "25.5" ); + map.put( "shipmentDate", "2021-06-15" ); + StringMapToBeanMapper.Order order = StringMapToBeanMapper.INSTANCE.fromMap( map ); + + assertThat( order ).isNotNull(); + assertThat( order.getName() ).isEqualTo( "Jacket" ); + assertThat( order.getPrice() ).isEqualTo( 25.5 ); + assertThat( order.getOrderDate() ).isNull(); + assertThat( order.getShipmentDate() ).isEqualTo( LocalDate.of( 2021, Month.JUNE, 15 ) ); + } + + @ProcessorTest + void fromMapWithEmptyValuesForString() { + Map map = Collections.singletonMap( "name", "" ); + StringMapToBeanMapper.Order order = StringMapToBeanMapper.INSTANCE.fromMap( map ); + + assertThat( order ).isNotNull(); + assertThat( order.getName() ).isEqualTo( "" ); + assertThat( order.getPrice() ).isEqualTo( 0 ); + assertThat( order.getOrderDate() ).isNull(); + assertThat( order.getShipmentDate() ).isNull(); + } + + @ProcessorTest + void fromMapWithEmptyValuesForDouble() { + Map map = Collections.singletonMap( "price", "" ); + assertThatThrownBy( () -> StringMapToBeanMapper.INSTANCE.fromMap( map ) ) + .isInstanceOf( NumberFormatException.class ); + } + + @ProcessorTest + void fromMapWithEmptyValuesForDate() { + Map map = Collections.singletonMap( "orderDate", "" ); + assertThatThrownBy( () -> StringMapToBeanMapper.INSTANCE.fromMap( map ) ) + .isInstanceOf( RuntimeException.class ) + .getCause() + .isInstanceOf( ParseException.class ); + } + + @ProcessorTest + void fromMapWithEmptyValuesForLocalDate() { + Map map = Collections.singletonMap( "shipmentDate", "" ); + assertThatThrownBy( () -> StringMapToBeanMapper.INSTANCE.fromMap( map ) ) + .isInstanceOf( DateTimeParseException.class ); + } + } + + @Nested + @WithClasses({ + StringMapToBeanWithCustomPresenceCheckMapper.class + }) + class StringMapToBeanWithCustomPresenceCheckTests { + + @ProcessorTest + void fromFullMap() { + Map map = new HashMap<>(); + map.put( "name", "Jacket" ); + map.put( "price", "25.5" ); + map.put( "shipmentDate", "2021-06-15" ); + StringMapToBeanWithCustomPresenceCheckMapper.Order order = + StringMapToBeanWithCustomPresenceCheckMapper.INSTANCE.fromMap( map ); + + assertThat( order ).isNotNull(); + assertThat( order.getName() ).isEqualTo( "Jacket" ); + assertThat( order.getPrice() ).isEqualTo( 25.5 ); + assertThat( order.getOrderDate() ).isNull(); + assertThat( order.getShipmentDate() ).isEqualTo( LocalDate.of( 2021, Month.JUNE, 15 ) ); + } + + @ProcessorTest + void fromMapWithEmptyValuesForString() { + Map map = new HashMap<>(); + map.put( "name", "" ); + map.put( "price", "" ); + map.put( "orderDate", "" ); + map.put( "shipmentDate", "" ); + StringMapToBeanWithCustomPresenceCheckMapper.Order order = + StringMapToBeanWithCustomPresenceCheckMapper.INSTANCE.fromMap( map ); + + assertThat( order ).isNotNull(); + assertThat( order.getName() ).isNull(); + assertThat( order.getPrice() ).isEqualTo( 0 ); + assertThat( order.getOrderDate() ).isNull(); + assertThat( order.getShipmentDate() ).isNull(); + } + + } + + @ProcessorTest + @WithClasses(MapToBeanDefinedMapper.class) + void shouldMapWithDefinedMapping() { + Map sourceMap = new HashMap<>(); + sourceMap.put( "number", 44 ); + + MapToBeanDefinedMapper.Target target = MapToBeanDefinedMapper.INSTANCE.toTarget( sourceMap ); + + assertThat( target ).isNotNull(); + assertThat( target.getNormalInt() ).isEqualTo( "44" ); + } + + @ProcessorTest + @WithClasses(MapToBeanImplicitMapper.class) + void shouldMapWithImpicitMapping() { + Map sourceMap = new HashMap<>(); + sourceMap.put( "name", "mapstruct" ); + + MapToBeanImplicitMapper.Target target = MapToBeanImplicitMapper.INSTANCE.toTarget( sourceMap ); + + assertThat( target ).isNotNull(); + assertThat( target.getName() ).isEqualTo( "mapstruct" ); + } + + @ProcessorTest + @WithClasses(MapToBeanUpdateImplicitMapper.class) + void shouldMapToExistingTargetWithImplicitMapping() { + Map sourceMap = new HashMap<>(); + sourceMap.put( "rating", 5 ); + + MapToBeanUpdateImplicitMapper.Target existingTarget = new MapToBeanUpdateImplicitMapper.Target(); + existingTarget.setRating( 4 ); + existingTarget.setName( "mapstruct" ); + + MapToBeanUpdateImplicitMapper.Target target = MapToBeanUpdateImplicitMapper.INSTANCE + .toTarget( existingTarget, sourceMap ); + + assertThat( target ).isNotNull(); + assertThat( target.getName() ).isEqualTo( "mapstruct" ); + assertThat( target.getRating() ).isEqualTo( 5 ); + } + + @ProcessorTest + @WithClasses(MapToBeanWithDefaultMapper.class) + void shouldMapWithDefaultValue() { + Map sourceMap = new HashMap<>(); + + MapToBeanWithDefaultMapper.Target target = MapToBeanWithDefaultMapper.INSTANCE + .toTarget( sourceMap ); + + assertThat( target ).isNotNull(); + assertThat( target.getNormalInt() ).isEqualTo( "4711" ); + } + + @ProcessorTest + @WithClasses(MapToBeanUsingMappingMethodMapper.class) + void shouldMapUsingMappingMethod() { + Map sourceMap = new HashMap<>(); + sourceMap.put( "number", 23 ); + + MapToBeanUsingMappingMethodMapper.Target target = MapToBeanUsingMappingMethodMapper.INSTANCE + .toTarget( sourceMap ); + + assertThat( target ).isNotNull(); + assertThat( target.getNormalInt() ).isEqualTo( "converted_23" ); + } + + @ProcessorTest + @WithClasses(MapToBeanFromMultipleSources.class) + void shouldMapFromMultipleSources() { + Map integers = new HashMap<>(); + integers.put( "number", 23 ); + + Map strings = new HashMap<>(); + strings.put( "string", "stringFromMap" ); + + MapToBeanFromMultipleSources.Source source = new MapToBeanFromMultipleSources.Source(); + + MapToBeanFromMultipleSources.Target target = MapToBeanFromMultipleSources.INSTANCE + .toTarget( integers, strings, source ); + + assertThat( target ).isNotNull(); + assertThat( target.getInteger() ).isEqualTo( 23 ); + assertThat( target.getString() ).isEqualTo( "stringFromMap" ); + assertThat( target.getStringFromBean() ).isEqualTo( "stringFromBean" ); + } + + @ProcessorTest + @WithClasses(MapToBeanFromMapAndNestedSource.class) + void shouldMapFromNestedSource() { + Map integers = new HashMap<>(); + integers.put( "number", 23 ); + + MapToBeanFromMapAndNestedSource.Source source = new MapToBeanFromMapAndNestedSource.Source(); + + MapToBeanFromMapAndNestedSource.Target target = MapToBeanFromMapAndNestedSource.INSTANCE + .toTarget( integers, source ); + + assertThat( target ).isNotNull(); + assertThat( target.getInteger() ).isEqualTo( 23 ); + assertThat( target.getStringFromNestedSource() ).isEqualTo( "nestedString" ); + } + + @ProcessorTest + @WithClasses(MapToBeanFromMapAndNestedMap.class) + void shouldMapFromNestedMap() { + + MapToBeanFromMapAndNestedMap.Source source = new MapToBeanFromMapAndNestedMap.Source(); + MapToBeanFromMapAndNestedMap.Target target = MapToBeanFromMapAndNestedMap.INSTANCE + .toTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getNestedTarget() ).isNotNull(); + assertThat( target.getNestedTarget().getStringFromNestedMap() ).isEqualTo( "valueFromNestedMap" ); + } + + @ProcessorTest + @WithClasses(ObjectMapToBeanWithQualifierMapper.class) + void shouldUseObjectQualifiedMethod() { + Map vehicles = new HashMap<>(); + vehicles.put( "car", new ObjectMapToBeanWithQualifierMapper.Car( "Tesla" ) ); + + ObjectMapToBeanWithQualifierMapper.VehiclesDto dto = + ObjectMapToBeanWithQualifierMapper.INSTANCE.map( vehicles ); + + assertThat( dto.getCar() ).isNotNull(); + assertThat( dto.getCar() ).isInstanceOf( ObjectMapToBeanWithQualifierMapper.CarDto.class ); + assertThat( dto.getCar().getBrand() ).isEqualTo( "Tesla" ); + } + + @ProcessorTest + @WithClasses(MapToBeanTypeCheckMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic( + type = MapToBeanTypeCheckMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 21, + message = "Unmapped target property: \"value\"." + ), + @Diagnostic( + type = MapToBeanTypeCheckMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 21, + message = "The Map parameter \"source\" cannot be used for property mapping. " + + "It must be typed with Map but it was typed with Map." + ), + } + ) + void shouldWarnAboutWrongMapTypes() { + + Map upsideDownMap = new HashMap<>(); + upsideDownMap.put( 23, "number" ); + + MapToBeanTypeCheckMapper.Target target = MapToBeanTypeCheckMapper.INSTANCE + .toTarget( upsideDownMap ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isNull(); + } + + @ProcessorTest + @WithClasses(MapToBeanRawMapMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic( + type = MapToBeanRawMapMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 21, + message = "Unmapped target property: \"value\"." + ), + @Diagnostic( + type = MapToBeanRawMapMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 21, + message = "The Map parameter \"source\" cannot be used for property mapping. " + + "It must be typed with Map but it was raw." + ), + } + ) + void shouldWarnAboutRawMapTypes() { + + Map rawMap = new HashMap<>(); + rawMap.put( "value", "number" ); + + MapToBeanRawMapMapper.Target target = MapToBeanRawMapMapper.INSTANCE + .toTarget( rawMap ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isNull(); + } + + @ProcessorTest + @WithClasses({ + MapToBeanNonStringMapAsMultiSourceMapper.class + }) + void shouldNotWarnIfMappedIsUsedAsSourceParameter() { + MapToBeanNonStringMapAsMultiSourceMapper.Target target = MapToBeanNonStringMapAsMultiSourceMapper.INSTANCE + .toTarget( + new MapToBeanNonStringMapAsMultiSourceMapper.Source( "test" ), + Collections.singletonMap( 10, "value" ) + ); + + assertThat( target.getValue() ).isEqualTo( "test" ); + assertThat( target.getMap() ) + .containsOnly( entry( "10", "value" ) ); + } + + @ProcessorTest + @WithClasses(MapToBeanImplicitUnmappedSourcePolicyMapper.class) + void shouldNotReportUnmappedSourcePropertiesWithMap() { + Map sourceMap = new HashMap<>(); + sourceMap.put( "name", "mapstruct" ); + + MapToBeanImplicitUnmappedSourcePolicyMapper.Target target = + MapToBeanImplicitUnmappedSourcePolicyMapper.INSTANCE.toTarget( sourceMap ); + + assertThat( target ).isNotNull(); + assertThat( target.getName() ).isEqualTo( "mapstruct" ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanDefinedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanDefinedMapper.java new file mode 100644 index 0000000000..eef8c285b3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanDefinedMapper.java @@ -0,0 +1,38 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanDefinedMapper { + + MapToBeanDefinedMapper INSTANCE = Mappers.getMapper( MapToBeanDefinedMapper.class ); + + @Mapping(target = "normalInt", source = "number") + Target toTarget(Map source); + + class Target { + + private String normalInt; + + public String getNormalInt() { + return normalInt; + } + + public void setNormalInt(String normalInt) { + this.normalInt = normalInt; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedMap.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedMap.java new file mode 100644 index 0000000000..f1b21263a4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedMap.java @@ -0,0 +1,67 @@ +/* + * 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.frommap; + +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanFromMapAndNestedMap { + + MapToBeanFromMapAndNestedMap INSTANCE = Mappers.getMapper( MapToBeanFromMapAndNestedMap.class ); + + Target toTarget(Source source); + + class Source { + + private Map nestedTarget = new HashMap<>( ); + + public Map getNestedTarget() { + return nestedTarget; + } + + public void setNestedTarget(Map nestedTarget) { + this.nestedTarget = nestedTarget; + } + + public Source() { + nestedTarget.put( "stringFromNestedMap", "valueFromNestedMap" ); + } + } + + class Target { + + private NestedTarget nestedTarget; + + public NestedTarget getNestedTarget() { + return nestedTarget; + } + + public void setNestedTarget(NestedTarget nestedTarget) { + this.nestedTarget = nestedTarget; + } + + } + + class NestedTarget { + private String stringFromNestedMap; + + public String getStringFromNestedMap() { + return stringFromNestedMap; + } + + public void setStringFromNestedMap(String stringFromNestedMap) { + this.stringFromNestedMap = stringFromNestedMap; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedSource.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedSource.java new file mode 100644 index 0000000000..c66080332e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedSource.java @@ -0,0 +1,79 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanFromMapAndNestedSource { + + MapToBeanFromMapAndNestedSource INSTANCE = Mappers.getMapper( MapToBeanFromMapAndNestedSource.class ); + + @Mapping(target = "integer", source = "integers.number") + @Mapping(target = "stringFromNestedSource", source = "source.nestedSource.nestedString") + Target toTarget(Map integers, Source source); + + class Source { + + private String stringFromBean = "stringFromBean"; + private NestedSource nestedSource = new NestedSource(); + + public String getStringFromBean() { + return stringFromBean; + } + + public void setStringFromBean(String stringFromBean) { + this.stringFromBean = stringFromBean; + } + + public NestedSource getNestedSource() { + return nestedSource; + } + + public void setNestedSource(NestedSource nestedSource) { + this.nestedSource = nestedSource; + } + + class NestedSource { + + private String nestedString = "nestedString"; + + public String getNestedString() { + return nestedString; + } + } + } + + class Target { + + private int integer; + private String stringFromNestedSource; + + public int getInteger() { + return integer; + } + + public void setInteger(int integer) { + this.integer = integer; + } + + public String getStringFromNestedSource() { + return stringFromNestedSource; + } + + public void setStringFromNestedSource(String stringFromNestedSource) { + this.stringFromNestedSource = stringFromNestedSource; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMultipleSources.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMultipleSources.java new file mode 100644 index 0000000000..c5c9b2e665 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMultipleSources.java @@ -0,0 +1,70 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanFromMultipleSources { + + MapToBeanFromMultipleSources INSTANCE = Mappers.getMapper( MapToBeanFromMultipleSources.class ); + + @Mapping(target = "integer", source = "integers.number") + @Mapping(target = "string", source = "strings.string") + @Mapping(target = "stringFromBean", source = "bean.stringFromBean") + Target toTarget(Map integers, Map strings, Source bean); + + class Source { + private String stringFromBean = "stringFromBean"; + + public String getStringFromBean() { + return stringFromBean; + } + + public void setStringFromBean(String stringFromBean) { + this.stringFromBean = stringFromBean; + } + } + + class Target { + + private int integer; + private String string; + private String stringFromBean; + + public int getInteger() { + return integer; + } + + public void setInteger(int integer) { + this.integer = integer; + } + + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + + public String getStringFromBean() { + return stringFromBean; + } + + public void setStringFromBean(String stringFromBean) { + this.stringFromBean = stringFromBean; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanImplicitMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanImplicitMapper.java new file mode 100644 index 0000000000..ca4b7a3d21 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanImplicitMapper.java @@ -0,0 +1,36 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanImplicitMapper { + + MapToBeanImplicitMapper INSTANCE = Mappers.getMapper( MapToBeanImplicitMapper.class ); + + Target toTarget(Map source); + + class Target { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanImplicitUnmappedSourcePolicyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanImplicitUnmappedSourcePolicyMapper.java new file mode 100644 index 0000000000..2c72dc7b70 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanImplicitUnmappedSourcePolicyMapper.java @@ -0,0 +1,38 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface MapToBeanImplicitUnmappedSourcePolicyMapper { + + MapToBeanImplicitUnmappedSourcePolicyMapper INSTANCE = + Mappers.getMapper( MapToBeanImplicitUnmappedSourcePolicyMapper.class ); + + Target toTarget(Map source); + + class Target { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanNonStringMapAsMultiSourceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanNonStringMapAsMultiSourceMapper.java new file mode 100644 index 0000000000..2606a9a037 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanNonStringMapAsMultiSourceMapper.java @@ -0,0 +1,55 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanNonStringMapAsMultiSourceMapper { + + MapToBeanNonStringMapAsMultiSourceMapper INSTANCE = + Mappers.getMapper( MapToBeanNonStringMapAsMultiSourceMapper.class ); + + Target toTarget(Source source, Map map); + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Target { + + private final String value; + private final Map map; + + public Target(String value, Map map) { + this.value = value; + this.map = map; + } + + public String getValue() { + return value; + } + + public Map getMap() { + return map; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanRawMapMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanRawMapMapper.java new file mode 100644 index 0000000000..40215ab183 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanRawMapMapper.java @@ -0,0 +1,36 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanRawMapMapper { + + MapToBeanRawMapMapper INSTANCE = Mappers.getMapper( MapToBeanRawMapMapper.class ); + + Target toTarget(Map source); + + class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanTypeCheckMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanTypeCheckMapper.java new file mode 100644 index 0000000000..346c153d71 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanTypeCheckMapper.java @@ -0,0 +1,36 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanTypeCheckMapper { + + MapToBeanTypeCheckMapper INSTANCE = Mappers.getMapper( MapToBeanTypeCheckMapper.class ); + + Target toTarget(Map source); + + class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUpdateImplicitMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUpdateImplicitMapper.java new file mode 100644 index 0000000000..82fe906bfe --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUpdateImplicitMapper.java @@ -0,0 +1,47 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) +public interface MapToBeanUpdateImplicitMapper { + + MapToBeanUpdateImplicitMapper INSTANCE = Mappers.getMapper( MapToBeanUpdateImplicitMapper.class ); + + Target toTarget(@MappingTarget Target target, Map source); + + class Target { + + private String name; + private Integer rating; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getRating() { + return rating; + } + + public void setRating(Integer rating) { + this.rating = rating; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUsingMappingMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUsingMappingMethodMapper.java new file mode 100644 index 0000000000..9a6344ff15 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUsingMappingMethodMapper.java @@ -0,0 +1,42 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanUsingMappingMethodMapper { + + MapToBeanUsingMappingMethodMapper INSTANCE = Mappers.getMapper( MapToBeanUsingMappingMethodMapper.class ); + + @Mapping(target = "normalInt", source = "number") + Target toTarget(Map source); + + default String mapIntegerToString( Integer input ) { + return "converted_" + input; + } + + class Target { + + private String normalInt; + + public String getNormalInt() { + return normalInt; + } + + public void setNormalInt(String normalInt) { + this.normalInt = normalInt; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanWithDefaultMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanWithDefaultMapper.java new file mode 100644 index 0000000000..104d34bd0a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanWithDefaultMapper.java @@ -0,0 +1,38 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Christian Kosmowski + */ +@Mapper +public interface MapToBeanWithDefaultMapper { + + MapToBeanWithDefaultMapper INSTANCE = Mappers.getMapper( MapToBeanWithDefaultMapper.class ); + + @Mapping(target = "normalInt", source = "number", defaultValue = "4711") + Target toTarget(Map source); + + class Target { + + private String normalInt; + + public String getNormalInt() { + return normalInt; + } + + public void setNormalInt(String normalInt) { + this.normalInt = normalInt; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/ObjectMapToBeanWithQualifierMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/ObjectMapToBeanWithQualifierMapper.java new file mode 100644 index 0000000000..54ddb0e42a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/ObjectMapToBeanWithQualifierMapper.java @@ -0,0 +1,74 @@ +/* + * 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.frommap; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ObjectMapToBeanWithQualifierMapper { + + ObjectMapToBeanWithQualifierMapper INSTANCE = Mappers.getMapper( ObjectMapToBeanWithQualifierMapper.class ); + + @Mapping( target = "car", qualifiedByName = "objectToCar") + VehiclesDto map(Map vehicles); + + @Named("objectToCar") + default CarDto objectToCar(Object object) { + CarDto car = new CarDto(); + + if ( object instanceof Car ) { + car.setBrand( ( (Car) object ).brand ); + } + + return car; + } + + class VehiclesDto { + + private VehicleDto car; + + public VehicleDto getCar() { + return car; + } + + public void setCar(VehicleDto car) { + this.car = car; + } + } + + class VehicleDto { + private String brand; + + public String getBrand() { + return brand; + } + + public void setBrand(String brand) { + this.brand = brand; + } + } + + class CarDto extends VehicleDto { + + } + + class Car { + + private final String brand; + + public Car(String brand) { + this.brand = brand; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/StringMapToBeanMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/StringMapToBeanMapper.java new file mode 100644 index 0000000000..95b0f72597 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/StringMapToBeanMapper.java @@ -0,0 +1,67 @@ +/* + * 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.frommap; + +import java.time.LocalDate; +import java.util.Date; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface StringMapToBeanMapper { + StringMapToBeanMapper INSTANCE = Mappers.getMapper( StringMapToBeanMapper.class ); + + Order fromMap(Map map); + + class Order { + private String name; + private double price; + private Date orderDate; + private LocalDate shipmentDate; + + public Order() { + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return this.price; + } + + public void setPrice(double price) { + this.price = price; + } + + public Date getOrderDate() { + return this.orderDate; + } + + public void setOrderDate(Date orderDate) { + this.orderDate = orderDate; + } + + public LocalDate getShipmentDate() { + return this.shipmentDate; + } + + public void setShipmentDate(LocalDate shipmentDate) { + this.shipmentDate = shipmentDate; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/StringMapToBeanWithCustomPresenceCheckMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/StringMapToBeanWithCustomPresenceCheckMapper.java new file mode 100644 index 0000000000..62c105599f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/StringMapToBeanWithCustomPresenceCheckMapper.java @@ -0,0 +1,74 @@ +/* + * 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.frommap; + +import java.time.LocalDate; +import java.util.Date; +import java.util.Map; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface StringMapToBeanWithCustomPresenceCheckMapper { + StringMapToBeanWithCustomPresenceCheckMapper INSTANCE = + Mappers.getMapper( StringMapToBeanWithCustomPresenceCheckMapper.class ); + + Order fromMap(Map map); + + @Condition + default boolean isNotEmpty(String value) { + return value != null && !value.isEmpty(); + } + + class Order { + private String name; + private double price; + private Date orderDate; + private LocalDate shipmentDate; + + public Order() { + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return this.price; + } + + public void setPrice(double price) { + this.price = price; + } + + public Date getOrderDate() { + return this.orderDate; + } + + public void setOrderDate(Date orderDate) { + this.orderDate = orderDate; + } + + public LocalDate getShipmentDate() { + return this.shipmentDate; + } + + public void setShipmentDate(LocalDate shipmentDate) { + this.shipmentDate = shipmentDate; + } + } + +} From 38744d9f73d5ff921acd10431e364b31d7b075da Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 27 Jun 2021 18:43:13 +0200 Subject: [PATCH 182/597] Add users that have contributed to 1.5 to the copyright.txt --- copyright.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/copyright.txt b/copyright.txt index 2980f611b4..01b6526c70 100644 --- a/copyright.txt +++ b/copyright.txt @@ -18,17 +18,26 @@ Darren Rambaud - https://github.com/xyzst Dekel Pilli - https://github.com/dekelpilli Dilip Krishnan - https://github.com/dilipkrish Dmytro Polovinkin - https://github.com/navpil +Ewald Volkert - https://github.com/eforest Eric Martineau - https://github.com/ericmartineau Ewald Volkert - https://github.com/eforest Filip Hrisafov - https://github.com/filiphr Florian Tavares - https://github.com/neoXfire Gervais Blaise - https://github.com/gervaisb +Gibou Damien - https://github.com/dmngb Gunnar Morling - https://github.com/gunnarmorling Ivo Smid - https://github.com/bedla +Jason Bodnar - https://github.com/Blackbaud-JasonBodnar +Jeroen van Wilgenburg - https://github.com/jvwilge Jeff Smyth - https://github.com/smythie86 +João Paulo Bassinello - https://github.com/jpbassinello Jonathan Kraska - https://github.com/jakraska Joshua Spoerri - https://github.com/spoerri +Jude Niroshan - https://github.com/JudeNiroshan +Kemal Özcan - https://github.com/yekeoe Kevin Grüneberg - https://github.com/kevcodez +Lukas Lazar - https://github.com/LukeLaz +Nikolas Charalambidis - https://github.com/Nikolas-Charalambidis Michael Pardo - https://github.com/pardom Mustafa Caylak - https://github.com/luxmeter Oliver Ehrenmüller - https://github.com/greuelpirat @@ -48,6 +57,8 @@ Sjaak Derksen - https://github.com/sjaakd Stefan May - https://github.com/osthus-sm Taras Mychaskiw - https://github.com/twentylemon Thibault Duperron - https://github.com/Zomzog +Tomáš Poledný - https://github.com/Saljack +Tobias Meggendorfer - https://github.com/incaseoftrouble Tillmann Gaida - https://github.com/Tillerino Timo Eckhardt - https://github.com/timoe Tomek Gubala - https://github.com/vgtworld From a95d1c59c3f531fc90b62c60092dd9c16bc93b66 Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Sat, 3 Jul 2021 15:28:10 +0200 Subject: [PATCH 183/597] #2505 deepclone generates enum mapping method (#2507) --- .../creation/MappingResolverImpl.java | 4 ++ .../ap/test/bugs/_2505/Issue2505Mapper.java | 51 +++++++++++++++++++ .../ap/test/bugs/_2505/Issue2505Test.java | 36 +++++++++++++ .../test/bugs/_2505/Issue2505MapperImpl.java | 29 +++++++++++ 4 files changed, 120 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2505/Issue2505Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2505/Issue2505Test.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2505/Issue2505MapperImpl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index d9bd4d731b..b971341fc9 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -383,6 +383,10 @@ private boolean allowDirect(Type type) { return true; } + if ( type.isEnumType() ) { + return true; + } + if ( type.isArrayType() ) { return type.isJavaLangType() || type.getComponentType().isPrimitive(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2505/Issue2505Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2505/Issue2505Mapper.java new file mode 100644 index 0000000000..525eb09fd3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2505/Issue2505Mapper.java @@ -0,0 +1,51 @@ +/* + * 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._2505; + +import org.mapstruct.Mapper; +import org.mapstruct.control.DeepClone; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper( mappingControl = DeepClone.class ) +public interface Issue2505Mapper { + + Issue2505Mapper INSTANCE = Mappers.getMapper( Issue2505Mapper.class ); + + Customer map(CustomerDTO value); + + enum Status { + ENABLED, DISABLED, + } + + class Customer { + + private Status status; + + public Status getStatus() { + return status; + } + + public void setStatus(Status stat) { + this.status = stat; + } + } + + class CustomerDTO { + + private Status status; + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2505/Issue2505Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2505/Issue2505Test.java new file mode 100644 index 0000000000..b3fa6ea3c1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2505/Issue2505Test.java @@ -0,0 +1,36 @@ +/* + * 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._2505; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Sjaak derksen + */ +@IssueKey("2505") +@WithClasses( Issue2505Mapper.class ) +class Issue2505Test { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() + .addComparisonToFixtureFor( Issue2505Mapper.class ); + + @ProcessorTest + void shouldNotGenerateEnumMappingMethodForDeepClone() { + Issue2505Mapper.CustomerDTO source = new Issue2505Mapper.CustomerDTO(); + source.setStatus( Issue2505Mapper.Status.DISABLED ); + + Issue2505Mapper.Customer target = Issue2505Mapper.INSTANCE.map( source ); + + assertThat( target.getStatus() ).isEqualTo( Issue2505Mapper.Status.DISABLED ); + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2505/Issue2505MapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2505/Issue2505MapperImpl.java new file mode 100644 index 0000000000..87977b093b --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2505/Issue2505MapperImpl.java @@ -0,0 +1,29 @@ +/* + * 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._2505; + +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-07-03T14:21:53+0200", + comments = "version: , compiler: Eclipse JDT (Batch) 3.20.0.v20191203-2131, environment: Java 1.8.0_181 (Oracle Corporation)" +) +public class Issue2505MapperImpl implements Issue2505Mapper { + + @Override + public Customer map(CustomerDTO value) { + if ( value == null ) { + return null; + } + + Customer customer = new Customer(); + + customer.setStatus( value.getStatus() ); + + return customer; + } +} From a91b93f357a2ab8d481ff3a3484440a507f06166 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 18 Jul 2021 15:15:44 +0200 Subject: [PATCH 184/597] [maven-release-plugin] prepare release 1.5.0.Beta1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index aaae7b5404..bcf279c6c4 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 293043e6f9..3363f4c590 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 10c95cf3b5..9472ca45a1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 337ad35e51..f8fe133b9f 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 8850da641c..8d24c027ed 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 4b48c19496..f9ad6782d4 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index bb1229fcb8..b249ad635d 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta1 pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.0.Beta1 diff --git a/pom.xml b/pom.xml index 28c35aea74..03c1c0fcd9 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta1 parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.0.Beta1 diff --git a/processor/pom.xml b/processor/pom.xml index fcc6dbbab8..b79b61e2bc 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta1 ../parent/pom.xml From 43dfd92e05193111992807076ff0fe7b47da1075 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 18 Jul 2021 15:15:44 +0200 Subject: [PATCH 185/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index bcf279c6c4..aaae7b5404 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 3363f4c590..293043e6f9 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 9472ca45a1..10c95cf3b5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index f8fe133b9f..337ad35e51 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 8d24c027ed..8850da641c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index f9ad6782d4..4b48c19496 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index b249ad635d..bb1229fcb8 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta1 + 1.5.0-SNAPSHOT pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.0.Beta1 + HEAD diff --git a/pom.xml b/pom.xml index 03c1c0fcd9..28c35aea74 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta1 + 1.5.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.0.Beta1 + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index b79b61e2bc..fcc6dbbab8 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta1 + 1.5.0-SNAPSHOT ../parent/pom.xml From 8c554b95564f412466592f438661b64d36bed42e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 18 Jul 2021 15:55:17 +0200 Subject: [PATCH 186/597] Change reference from Google Group to GitHub Discussions in reference guide --- .../src/main/asciidoc/mapstruct-reference-guide.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc index 7bffbca5c7..1705ed4af1 100644 --- a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc +++ b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc @@ -12,7 +12,7 @@ [[Preface]] == Preface This is the reference documentation of MapStruct, an annotation processor for generating type-safe, performant and dependency-free bean mapping code. -This guide covers all the functionality provided by MapStruct. In case this guide doesn't answer all your questions just join the MapStruct https://groups.google.com/forum/?fromgroups#!forum/mapstruct-users[Google group] to get help. +This guide covers all the functionality provided by MapStruct. In case this guide doesn't answer all your questions just join the MapStruct https://github.com/mapstruct/mapstruct/discussions[GitHub Discussions] to get help. You found a typo or other error in this guide? Please let us know by opening an issue in the https://github.com/mapstruct/mapstruct[MapStruct GitHub repository], or, better yet, help the community and send a pull request for fixing it! From e6e9b6ce923a07f75a598fdaf4a87dfc85ea3be8 Mon Sep 17 00:00:00 2001 From: Amogh Date: Thu, 22 Jul 2021 04:08:54 -0400 Subject: [PATCH 187/597] #2525 add available transformations to CaseEnumTransformationStrategy exception --- .../org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java | 4 +++- .../EnumNameTransformationStrategyTest.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java index 2f99f56ce4..3a4ba18ade 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/CaseEnumTransformationStrategy.java @@ -20,6 +20,7 @@ public class CaseEnumTransformationStrategy implements EnumTransformationStrateg private static final String UPPER = "upper"; private static final String LOWER = "lower"; private static final String CAPITAL = "capital"; + private static final String CASE_ENUM_TRANSFORMATION_STRATEGIES = UPPER + ", " + LOWER + ", " + CAPITAL; @Override public String getStrategyName() { @@ -37,7 +38,8 @@ public String transform(String value, String configuration) { return capitalize( value ); default: throw new IllegalArgumentException( - "Unexpected configuration for enum case transformation: " + configuration ); + "Unexpected configuration for enum case transformation: " + configuration + + ". Allowed values: " + CASE_ENUM_TRANSFORMATION_STRATEGIES); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java index 7a29ae40a8..012fc9961a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/value/nametransformation/EnumNameTransformationStrategyTest.java @@ -112,7 +112,8 @@ public void shouldGiveCompileErrorWhenUsingUnknownNameTransformStrategy() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 18, message = "Illegal transformation for 'case' EnumTransformationStrategy." + - " Error: 'Unexpected configuration for enum case transformation: unknown'." + " Error: 'Unexpected configuration for enum case transformation: unknown." + + " Allowed values: upper, lower, capital'." ) } ) From 196528e5783ab39401982fae449ec91de98b06ab Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Wed, 11 Aug 2021 06:49:05 +0200 Subject: [PATCH 188/597] #2530: fix missing supporting fields for ReverseConversion --- .../conversion/ReverseConversion.java | 5 +++ .../ap/test/bugs/_2530/Issue2530Mapper.java | 37 +++++++++++++++++++ .../ap/test/bugs/_2530/Issue2530Test.java | 34 +++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2530/Issue2530Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2530/Issue2530Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java index 7fd7f6bbb8..0e2cd3d832 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java @@ -9,6 +9,7 @@ import java.util.List; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.FieldReference; import org.mapstruct.ap.internal.model.HelperMethod; /** @@ -44,4 +45,8 @@ public List getRequiredHelperMethods(ConversionContext conversionC return Collections.emptyList(); } + @Override + public List getRequiredHelperFields(ConversionContext conversionContext) { + return conversionProvider.getRequiredHelperFields( conversionContext ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2530/Issue2530Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2530/Issue2530Mapper.java new file mode 100644 index 0000000000..268237376c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2530/Issue2530Mapper.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.bugs._2530; + +import java.time.LocalDate; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface Issue2530Mapper { + + Issue2530Mapper INSTANCE = Mappers.getMapper( Issue2530Mapper.class ); + + @Mapping(target = "date", source = ".", dateFormat = "yyyy-MM-dd") + Test map(String s); + + class Test { + + LocalDate date; + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate dateTime) { + this.date = dateTime; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2530/Issue2530Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2530/Issue2530Test.java new file mode 100644 index 0000000000..a1ea466d0b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2530/Issue2530Test.java @@ -0,0 +1,34 @@ +/* + * 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._2530; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Month; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Ben Zegveld + */ +@IssueKey("2530") +@WithClasses({ + Issue2530Mapper.class +}) +public class Issue2530Test { + + @ProcessorTest + public void shouldConvert() { + Issue2530Mapper.Test target = Issue2530Mapper.INSTANCE.map( "2021-07-31" ); + + assertThat( target ).isNotNull(); + assertThat( target.getDate().getYear() ).isEqualTo( 2021 ); + assertThat( target.getDate().getMonth() ).isEqualTo( Month.JULY ); + assertThat( target.getDate().getDayOfMonth() ).isEqualTo( 31 ); + } +} From 8ad55b164fbba3417150daf45cb2d67bf26b13b9 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Wed, 11 Aug 2021 11:35:45 +0200 Subject: [PATCH 189/597] #2544: fix missing helper methods for ReverseConversion --- .../conversion/ReverseConversion.java | 6 +-- .../ap/test/bugs/_2544/Issue2544Mapper.java | 37 +++++++++++++++++++ .../ap/test/bugs/_2544/Issue2544Test.java | 30 +++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java index 0e2cd3d832..cb496b0c54 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java @@ -5,12 +5,12 @@ */ package org.mapstruct.ap.internal.conversion; -import java.util.Collections; import java.util.List; + +import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.FieldReference; -import org.mapstruct.ap.internal.model.HelperMethod; /** * * A {@link ConversionProvider} which creates the inversed conversions for a @@ -42,7 +42,7 @@ public Assignment from(ConversionContext conversionContext) { @Override public List getRequiredHelperMethods(ConversionContext conversionContext) { - return Collections.emptyList(); + return conversionProvider.getRequiredHelperMethods( conversionContext ); } @Override diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Mapper.java new file mode 100644 index 0000000000..6d02d709a6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Mapper.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.bugs._2544; + +import java.math.BigDecimal; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface Issue2544Mapper { + + Issue2544Mapper INSTANCE = Mappers.getMapper( Issue2544Mapper.class ); + + @Mapping( target = "bigNumber", source = ".", numberFormat = "##0.#####E0" ) + Target map(String s); + + class Target { + + private BigDecimal bigNumber; + + public BigDecimal getBigNumber() { + return bigNumber; + } + + public void setBigNumber(BigDecimal bigNumber) { + this.bigNumber = bigNumber; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Test.java new file mode 100644 index 0000000000..5cc252bbf8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Test.java @@ -0,0 +1,30 @@ +/* + * 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._2544; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigDecimal; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Ben Zegveld + */ +@IssueKey( "2544" ) +@WithClasses( { Issue2544Mapper.class } ) +public class Issue2544Test { + + @ProcessorTest + public void shouldConvert() { + Issue2544Mapper.Target target = Issue2544Mapper.INSTANCE.map( "123.45679E6" ); + + assertThat( target ).isNotNull(); + assertThat( target.getBigNumber() ).isEqualTo( new BigDecimal( "1.2345679E+8" ) ); + } +} From c1fa9bd0bd1e29b01c2c7f951543788b60a4f356 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sat, 14 Aug 2021 08:37:20 +0200 Subject: [PATCH 190/597] #2537 Fix incorrect unmapped source property when only defined in Mapping#target --- .../ap/internal/model/BeanMappingMethod.java | 1 + .../test/bugs/_2537/ImplicitSourceTest.java | 24 ++++++++++ ...dSourcePolicyWithImplicitSourceMapper.java | 45 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2537/ImplicitSourceTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2537/UnmappedSourcePolicyWithImplicitSourceMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 09cc20a6ed..4ebcd9f5ec 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1170,6 +1170,7 @@ else if ( mapping.getJavaExpression() != null ) { .build(); handledTargets.add( targetPropertyName ); unprocessedSourceParameters.remove( sourceRef.getParameter() ); + unprocessedSourceProperties.remove( sourceRef.getShallowestPropertyName() ); } else { errorOccured = true; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2537/ImplicitSourceTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2537/ImplicitSourceTest.java new file mode 100644 index 0000000000..edcbbfd4d4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2537/ImplicitSourceTest.java @@ -0,0 +1,24 @@ +/* + * 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._2537; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * implicit source-target mapping should list the source property as being mapped. + * + * @author Ben Zegveld + */ +@WithClasses( { UnmappedSourcePolicyWithImplicitSourceMapper.class } ) +@IssueKey( "2537" ) +public class ImplicitSourceTest { + + @ProcessorTest + public void situationCompilesWithoutErrors() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2537/UnmappedSourcePolicyWithImplicitSourceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2537/UnmappedSourcePolicyWithImplicitSourceMapper.java new file mode 100644 index 0000000000..21d8ca5332 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2537/UnmappedSourcePolicyWithImplicitSourceMapper.java @@ -0,0 +1,45 @@ +/* + * 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._2537; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +/** + * @author Ben Zegveld + */ +@Mapper( unmappedSourcePolicy = ReportingPolicy.ERROR ) +public interface UnmappedSourcePolicyWithImplicitSourceMapper { + @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "property" ) + Target map(Source source); + + class Source { + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + } + + class Target { + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + } +} From 0d8729767be07b1c6496c585691089890e1cedf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20P=C3=B6ttker?= Date: Sat, 14 Aug 2021 08:38:36 +0200 Subject: [PATCH 191/597] Remove remaining references to Hickory (#2511) --- parent/pom.xml | 2 +- .../src/main/java/org/mapstruct/ap/MappingProcessor.java | 6 +++--- .../java/org/mapstruct/ap/internal/gem/GemGenerator.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index bb1229fcb8..8ce94764ea 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -34,7 +34,7 @@ 1 3.17.2 - + jdt_apt diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index e7e52be695..8369a8b270 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -67,9 +67,9 @@ *

    5. if no error occurred, write out the model into Java source files
    6. * *

      - * For reading annotation attributes, gems as generated with help of the Hickory tool are used. These gems allow a comfortable access to - * annotations and their attributes without depending on their class objects. + * For reading annotation attributes, gems as generated with help of Gem Tools. These gems allow comfortable access to annotations and + * their attributes without depending on their class objects. *

      * The creation of Java source files is done using the FreeMarker template engine. * Each node of the mapper model has a corresponding FreeMarker template file which provides the Java representation of diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index f8bdbc539d..df5a90d5c2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -36,7 +36,7 @@ import org.mapstruct.tools.gem.GemDefinition; /** - * Triggers the generation of ge types using Hickory. + * Triggers the generation of gem types using Gem Tools. * * @author Gunnar Morling */ From 06c416043ca361e075e4bb19e09edf539010c499 Mon Sep 17 00:00:00 2001 From: Bas Claessen <35045227+basclaessen@users.noreply.github.com> Date: Sat, 14 Aug 2021 09:06:54 +0200 Subject: [PATCH 192/597] #2515 add ambiguous constructors to ambiguous constructor error message --- .../mapstruct/ap/internal/model/BeanMappingMethod.java | 10 +++++++++- .../java/org/mapstruct/ap/internal/util/Message.java | 2 +- .../erroneous/ErroneousConstructorTest.java | 6 ++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 4ebcd9f5ec..36dae0ba51 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -26,6 +26,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; @@ -699,7 +700,14 @@ private ConstructorAccessor getConstructorAccessor(Type type) { method.getExecutable(), GENERAL_AMBIGUOUS_CONSTRUCTORS, type, - Strings.join( constructors, ", " ) + constructors.stream() + .map( ExecutableElement::getParameters ) + .map( ps -> ps.stream() + .map( VariableElement::asType ) + .map( String::valueOf ) + .collect( Collectors.joining( ", ", type.getName() + "(", ")" ) ) + ) + .collect( Collectors.joining( ", " ) ) ); return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index f2226ab224..8d328d4796 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -125,7 +125,7 @@ public enum Message { GENERAL_AMBIGUOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), GENERAL_AMBIGUOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), GENERAL_AMBIGUOUS_PRESENCE_CHECK_METHOD( "Ambiguous presence check methods found for checking %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), - GENERAL_AMBIGUOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ), + GENERAL_AMBIGUOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s: %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ), GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ), GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ), GENERAL_VALID_DATE( "Given date format \"%s\" is valid.", Diagnostic.Kind.NOTE ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java index 2799e10847..5a8483f23b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/erroneous/ErroneousConstructorTest.java @@ -29,8 +29,10 @@ public class ErroneousConstructorTest { kind = javax.tools.Diagnostic.Kind.ERROR, line = 17, message = "Ambiguous constructors found for creating org.mapstruct.ap.test.constructor.erroneous" + - ".ErroneousAmbiguousConstructorsMapper.PersonWithMultipleConstructors. Either declare parameterless " + - "constructor or annotate the default constructor with an annotation named @Default." + ".ErroneousAmbiguousConstructorsMapper.PersonWithMultipleConstructors: " + + "PersonWithMultipleConstructors(java.lang.String), " + + "PersonWithMultipleConstructors(java.lang.String, int). Either declare parameterless constructor " + + "or annotate the default constructor with an annotation named @Default." ) }) public void shouldUseMultipleConstructors() { From eb12c485eee83d60c48b4b00d299b4563aa8fbb6 Mon Sep 17 00:00:00 2001 From: Bas Claessen <35045227+basclaessen@users.noreply.github.com> Date: Sat, 14 Aug 2021 09:07:22 +0200 Subject: [PATCH 193/597] #2515 add ambiguous constructors to ambiguous constructor error message From c52ff812aa9a4c5fa9cc758e8d19bb77a1d09e0c Mon Sep 17 00:00:00 2001 From: Adam Szatyin Date: Tue, 17 Aug 2021 19:21:07 +0200 Subject: [PATCH 194/597] #2552 Add built in conversion between URL and String --- .../chapter-5-data-type-conversions.asciidoc | 3 + .../internal/conversion/ConversionUtils.java | 12 +++ .../ap/internal/conversion/Conversions.java | 13 ++++ .../conversion/URLToStringConversion.java | 46 +++++++++++ .../ap/test/conversion/url/Source.java | 33 ++++++++ .../ap/test/conversion/url/Target.java | 30 ++++++++ .../conversion/url/URLConversionTest.java | 76 +++++++++++++++++++ .../ap/test/conversion/url/URLMapper.java | 23 ++++++ 8 files changed, 236 insertions(+) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/URLToStringConversion.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/url/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/url/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/url/URLConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/url/URLMapper.java diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 24f019fa63..91f4afb1cf 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -122,6 +122,9 @@ public interface CarMapper { * Between `String` and `StringBuilder` +* Between `java.net.URL` and `String`. +** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/URL[URL] otherwise a `MalformedURLException` is thrown. + [[mapping-object-references]] === Mapping object references diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java index 20fb8a2fee..e88bb93b86 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URL; import java.sql.Time; import java.sql.Timestamp; import java.text.DecimalFormat; @@ -254,4 +255,15 @@ public static String stringBuilder(ConversionContext conversionContext) { public static String uuid(ConversionContext conversionContext) { return typeReferenceName( conversionContext, UUID.class ); } + + /** + * Name for {@link java.net.URL}. + * + * @param conversionContext Conversion context + * + * @return Name or fully-qualified name. + */ + public static String url(ConversionContext conversionContext) { + return typeReferenceName( conversionContext, URL.class ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 4b3ab2a818..e012481054 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URL; import java.sql.Time; import java.sql.Timestamp; import java.time.Duration; @@ -193,6 +194,8 @@ public Conversions(TypeFactory typeFactory) { register( Currency.class, String.class, new CurrencyToStringConversion() ); register( UUID.class, String.class, new UUIDToStringConversion() ); + + registerURLConversion(); } private void registerJodaConversions() { @@ -294,6 +297,16 @@ private void registerBigDecimalConversion(Class targetType) { } } + private void registerURLConversion() { + if ( isJavaURLAvailable() ) { + register( URL.class, String.class, new URLToStringConversion() ); + } + } + + private boolean isJavaURLAvailable() { + return typeFactory.isTypeAvailable( "java.net.URL" ); + } + private void register(Class sourceClass, Class targetClass, ConversionProvider conversion) { Type sourceType = typeFactory.getType( sourceClass ); Type targetType = typeFactory.getType( targetClass ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/URLToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/URLToStringConversion.java new file mode 100644 index 0000000000..716dfb3bb2 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/URLToStringConversion.java @@ -0,0 +1,46 @@ +/* + * 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.internal.conversion; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import java.util.Set; + +import static org.mapstruct.ap.internal.conversion.ConversionUtils.url; +import static org.mapstruct.ap.internal.util.Collections.asSet; + +/** + * Conversion between {@link java.net.URL} and {@link String}. + * + * @author Adam Szatyin + */ +public class URLToStringConversion extends SimpleConversion { + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toString()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return "new " + url( conversionContext ) + "( )"; + } + + @Override + protected Set getFromConversionImportTypes(final ConversionContext conversionContext) { + return asSet( conversionContext.getTypeFactory().getType( URL.class ) ); + } + + @Override + protected List getFromConversionExceptionTypes(ConversionContext conversionContext) { + return java.util.Collections.singletonList( + conversionContext.getTypeFactory().getType( MalformedURLException.class ) + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/url/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/url/Source.java new file mode 100644 index 0000000000..ece0501a08 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/url/Source.java @@ -0,0 +1,33 @@ +/* + * 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.conversion.url; + +import java.net.URL; + +/** + * @author Adam Szatyin + */ +public class Source { + private URL url; + + private URL invalidURL; + + public URL getURL() { + return url; + } + + public void setURL(URL url) { + this.url = url; + } + + public URL getInvalidURL() { + return invalidURL; + } + + public void setInvalidURL(URL invalidURL) { + this.invalidURL = invalidURL; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/url/Target.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/url/Target.java new file mode 100644 index 0000000000..060f5582e1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/url/Target.java @@ -0,0 +1,30 @@ +/* + * 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.conversion.url; + +/** + * @author Adam Szatyin + */ +public class Target { + private String url; + private String invalidURL; + + public String getURL() { + return this.url; + } + + public void setURL(final String url) { + this.url = url; + } + + public String getInvalidURL() { + return this.invalidURL; + } + + public void setInvalidURL(final String invalidURL) { + this.invalidURL = invalidURL; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/url/URLConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/url/URLConversionTest.java new file mode 100644 index 0000000000..bd1eed1480 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/url/URLConversionTest.java @@ -0,0 +1,76 @@ +/* + * 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.conversion.url; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import java.net.MalformedURLException; +import java.net.URL; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Tests conversions between {@link java.net.URL} and String. + * + * @author Adam Szatyin + */ +@WithClasses({ Source.class, Target.class, URLMapper.class }) +public class URLConversionTest { + + @ProcessorTest + public void shouldApplyURLConversion() throws MalformedURLException { + Source source = new Source(); + source.setURL( new URL("https://mapstruct.org/") ); + + Target target = URLMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getURL() ).isEqualTo( source.getURL().toString() ); + } + + @ProcessorTest + public void shouldApplyReverseURLConversion() throws MalformedURLException { + Target target = new Target(); + target.setURL( "https://mapstruct.org/" ); + + Source source = URLMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getURL() ).isEqualTo( new URL( target.getURL() ) ); + } + + @ProcessorTest + public void shouldHandleInvalidURLString() { + Target target = new Target(); + target.setInvalidURL( "XXXXXXXXX" ); + + assertThatThrownBy( () -> URLMapper.INSTANCE.targetToSource( target ) ) + .isInstanceOf( RuntimeException.class ) + .getRootCause().isInstanceOf( MalformedURLException.class ); + } + + @ProcessorTest + public void shouldHandleInvalidURLStringWithMalformedURLException() { + Target target = new Target(); + target.setInvalidURL( "XXXXXXXXX" ); + + assertThatThrownBy( () -> URLMapper.INSTANCE.targetToSourceWithMalformedURLException( target ) ) + .isInstanceOf( MalformedURLException.class ); + } + + @ProcessorTest + public void shouldHandleNullURLString() { + Source source = new Source(); + + Target target = URLMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getURL() ).isNull(); + assertThat( target.getInvalidURL() ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/url/URLMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/url/URLMapper.java new file mode 100644 index 0000000000..18fc5bc7de --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/url/URLMapper.java @@ -0,0 +1,23 @@ +/* + * 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.conversion.url; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.net.MalformedURLException; + +@Mapper +public interface URLMapper { + + URLMapper INSTANCE = Mappers.getMapper( URLMapper.class ); + + Target sourceToTarget(Source source); + + Source targetToSource(Target target); + + Source targetToSourceWithMalformedURLException(Target target) throws MalformedURLException; +} From 7064e0bc977dc88222ff8fe8d23f9e36cc76fcdf Mon Sep 17 00:00:00 2001 From: Daniel Franco Date: Mon, 23 Aug 2021 15:19:02 +0100 Subject: [PATCH 195/597] Update maven wrapper version to 3.8.2 (#2557) --- .mvn/wrapper/maven-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 642d572ce9..abd303b673 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar From 9ed4e389f8328b5f62655c82119f05cb59c4a439 Mon Sep 17 00:00:00 2001 From: Tobias Meggendorfer Date: Mon, 30 Aug 2021 16:40:09 +0200 Subject: [PATCH 196/597] #2560 Ignore source properties if ignoreByDefault = true --- .../main/java/org/mapstruct/BeanMapping.java | 2 +- .../ap/internal/model/BeanMappingMethod.java | 4 ++ .../ErroneousSourceTargetMapper.java | 21 ++++++++++ .../IgnoreByDefaultSourcesTest.java | 38 +++++++++++++++++++ .../ap/test/ignorebydefaultsource/Source.java | 27 +++++++++++++ .../SourceTargetMapper.java | 23 +++++++++++ .../ap/test/ignorebydefaultsource/Target.java | 18 +++++++++ 7 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/ErroneousSourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/IgnoreByDefaultSourcesTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/SourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/Target.java diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java index f8c749fbde..3359cd8914 100644 --- a/core/src/main/java/org/mapstruct/BeanMapping.java +++ b/core/src/main/java/org/mapstruct/BeanMapping.java @@ -118,7 +118,7 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() /** * Default ignore all mappings. All mappings have to be defined manually. No automatic mapping will take place. No - * warning will be issued on missing target properties. + * warning will be issued on missing source or target properties. * * @return The ignore strategy (default false). * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 36dae0ba51..17bf2cb465 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1514,6 +1514,10 @@ private ReportingPolicyGem getUnmappedSourcePolicy() { if ( mappingReferences.isForForgedMethods() ) { return ReportingPolicyGem.IGNORE; } + // If we have ignoreByDefault = true, unprocessed source properties are not an issue. + if ( method.getOptions().getBeanMapping().isignoreByDefault() ) { + return ReportingPolicyGem.IGNORE; + } return method.getOptions().getMapper().unmappedSourcePolicy(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/ErroneousSourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/ErroneousSourceTargetMapper.java new file mode 100644 index 0000000000..4ec3c64500 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/ErroneousSourceTargetMapper.java @@ -0,0 +1,21 @@ +/* + * 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.ignorebydefaultsource; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper( + unmappedTargetPolicy = ReportingPolicy.IGNORE, + unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface ErroneousSourceTargetMapper { + ErroneousSourceTargetMapper INSTANCE = Mappers.getMapper( ErroneousSourceTargetMapper.class ); + + @Mapping(source = "one", target = "one") + Target sourceToTarget(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/IgnoreByDefaultSourcesTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/IgnoreByDefaultSourcesTest.java new file mode 100644 index 0000000000..98f2cde4c0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/IgnoreByDefaultSourcesTest.java @@ -0,0 +1,38 @@ +/* + * 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.ignorebydefaultsource; + +import javax.tools.Diagnostic.Kind; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +@IssueKey("2560") +public class IgnoreByDefaultSourcesTest { + + @ProcessorTest + @WithClasses({ SourceTargetMapper.class, Source.class, Target.class }) + public void shouldSucceed() { + } + + @ProcessorTest + @WithClasses({ ErroneousSourceTargetMapper.class, Source.class, Target.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousSourceTargetMapper.class, + kind = Kind.ERROR, + line = 20, + message = "Unmapped source property: \"other\".") + } + ) + public void shouldRaiseErrorDueToNonIgnoredSourceProperty() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/Source.java b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/Source.java new file mode 100644 index 0000000000..81d6441d57 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/Source.java @@ -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.ignorebydefaultsource; + +class Source { + private int one; + private int other; + + public int getOne() { + return one; + } + + public void setOne(int one) { + this.one = one; + } + + public int getOther() { + return other; + } + + public void setOther(int other) { + this.other = other; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/SourceTargetMapper.java new file mode 100644 index 0000000000..e029bd7391 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/SourceTargetMapper.java @@ -0,0 +1,23 @@ +/* + * 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.ignorebydefaultsource; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper( + unmappedTargetPolicy = ReportingPolicy.IGNORE, + unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface SourceTargetMapper { + SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + + @Mapping(source = "one", target = "one") + @BeanMapping(ignoreByDefault = true) + Target sourceToTarget(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/Target.java b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/Target.java new file mode 100644 index 0000000000..68ec5c8933 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/Target.java @@ -0,0 +1,18 @@ +/* + * 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.ignorebydefaultsource; + +class Target { + private int one; + + public int getOne() { + return one; + } + + public void setOne(int one) { + this.one = one; + } +} From 9057d68cd2411517df790e91b710244c7038e2ab Mon Sep 17 00:00:00 2001 From: Tobias Meggendorfer Date: Tue, 31 Aug 2021 21:45:25 +0200 Subject: [PATCH 197/597] Use DefaultLocale for more stable Issue2544MapperTest (#2569) --- .../ap/test/bugs/_2544/Issue2544Test.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Test.java index 5cc252bbf8..a070231839 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2544/Issue2544Test.java @@ -9,6 +9,7 @@ import java.math.BigDecimal; +import org.junitpioneer.jupiter.DefaultLocale; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -19,12 +20,23 @@ @IssueKey( "2544" ) @WithClasses( { Issue2544Mapper.class } ) public class Issue2544Test { + // Parsing numbers is sensitive to locale settings (e.g. decimal point) @ProcessorTest - public void shouldConvert() { + @DefaultLocale("en") + public void shouldConvertEn() { Issue2544Mapper.Target target = Issue2544Mapper.INSTANCE.map( "123.45679E6" ); assertThat( target ).isNotNull(); assertThat( target.getBigNumber() ).isEqualTo( new BigDecimal( "1.2345679E+8" ) ); } + + @ProcessorTest + @DefaultLocale("de") + public void shouldConvertDe() { + Issue2544Mapper.Target target = Issue2544Mapper.INSTANCE.map( "123,45679E6" ); + + assertThat( target ).isNotNull(); + assertThat( target.getBigNumber() ).isEqualTo( new BigDecimal( "1.2345679E+8" ) ); + } } From f0a13bb306e4e86f13d328512bde3be07ed0b72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Kemal=20=C3=96zcan?= Date: Mon, 6 Sep 2021 00:38:38 +0300 Subject: [PATCH 198/597] #2555 Add unmappedSourcePolicy annotation processor argument --- .../main/asciidoc/chapter-2-set-up.asciidoc | 13 ++++++ .../org/mapstruct/ap/MappingProcessor.java | 4 ++ .../internal/model/source/DefaultOptions.java | 3 ++ .../mapstruct/ap/internal/option/Options.java | 7 +++ ...SourceTargetMapperWithoutMapperConfig.java | 25 +++++++++++ .../unmappedsource/UnmappedSourceTest.java | 43 +++++++++++++++++++ 6 files changed, 95 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/unmappedsource/SourceTargetMapperWithoutMapperConfig.java diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index d26be72156..730433639d 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -247,6 +247,19 @@ Supported values are: If a policy is given for a specific mapper via `@Mapper#unmappedTargetPolicy()`, the value from the annotation takes precedence. If a policy is given for a specific bean mapping via `@BeanMapping#unmappedTargetPolicy()`, it takes precedence over both `@Mapper#unmappedTargetPolicy()` and the option. |`WARN` + +|`mapstruct.unmappedSourcePolicy` +|The default reporting policy to be applied in case an attribute of the source object of a mapping method is not populated with a target value. + +Supported values are: + +* `ERROR`: any unmapped source property will cause the mapping code generation to fail +* `WARN`: any unmapped source property will cause a warning at build time +* `IGNORE`: unmapped source properties are ignored + +If a policy is given for a specific mapper via `@Mapper#unmappedSourcePolicy()`, the value from the annotation takes precedence. +If a policy is given for a specific bean mapping via `@BeanMapping#ignoreUnmappedSourceProperties()`, it takes precedence over both `@Mapper#unmappedSourcePolicy()` and the option. +|`WARN` |=== === Using MapStruct with the Java Module System diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 8369a8b270..952257753a 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -84,6 +84,7 @@ MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP, MappingProcessor.SUPPRESS_GENERATOR_VERSION_INFO_COMMENT, MappingProcessor.UNMAPPED_TARGET_POLICY, + MappingProcessor.UNMAPPED_SOURCE_POLICY, MappingProcessor.DEFAULT_COMPONENT_MODEL, MappingProcessor.DEFAULT_INJECTION_STRATEGY, MappingProcessor.VERBOSE @@ -99,6 +100,7 @@ public class MappingProcessor extends AbstractProcessor { protected static final String SUPPRESS_GENERATOR_VERSION_INFO_COMMENT = "mapstruct.suppressGeneratorVersionInfoComment"; protected static final String UNMAPPED_TARGET_POLICY = "mapstruct.unmappedTargetPolicy"; + protected static final String UNMAPPED_SOURCE_POLICY = "mapstruct.unmappedSourcePolicy"; protected static final String DEFAULT_COMPONENT_MODEL = "mapstruct.defaultComponentModel"; protected static final String DEFAULT_INJECTION_STRATEGY = "mapstruct.defaultInjectionStrategy"; protected static final String ALWAYS_GENERATE_SERVICE_FILE = "mapstruct.alwaysGenerateServicesFile"; @@ -134,11 +136,13 @@ public synchronized void init(ProcessingEnvironment processingEnv) { private Options createOptions() { String unmappedTargetPolicy = processingEnv.getOptions().get( UNMAPPED_TARGET_POLICY ); + String unmappedSourcePolicy = processingEnv.getOptions().get( UNMAPPED_SOURCE_POLICY ); return new Options( Boolean.valueOf( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ), Boolean.valueOf( processingEnv.getOptions().get( SUPPRESS_GENERATOR_VERSION_INFO_COMMENT ) ), unmappedTargetPolicy != null ? ReportingPolicyGem.valueOf( unmappedTargetPolicy.toUpperCase() ) : null, + unmappedSourcePolicy != null ? ReportingPolicyGem.valueOf( unmappedSourcePolicy.toUpperCase() ) : null, processingEnv.getOptions().get( DEFAULT_COMPONENT_MODEL ), processingEnv.getOptions().get( DEFAULT_INJECTION_STRATEGY ), Boolean.valueOf( processingEnv.getOptions().get( ALWAYS_GENERATE_SERVICE_FILE ) ), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index 58f03ad55d..d52d094930 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -63,6 +63,9 @@ public ReportingPolicyGem unmappedTargetPolicy() { @Override public ReportingPolicyGem unmappedSourcePolicy() { + if ( options.getUnmappedSourcePolicy() != null ) { + return options.getUnmappedSourcePolicy(); + } return ReportingPolicyGem.valueOf( mapper.unmappedSourcePolicy().getDefaultValue() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java index 54b69c28b3..46a90d4b43 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java @@ -17,6 +17,7 @@ public class Options { private final boolean suppressGeneratorTimestamp; private final boolean suppressGeneratorVersionComment; private final ReportingPolicyGem unmappedTargetPolicy; + private final ReportingPolicyGem unmappedSourcePolicy; private final boolean alwaysGenerateSpi; private final String defaultComponentModel; private final String defaultInjectionStrategy; @@ -24,11 +25,13 @@ public class Options { public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVersionComment, ReportingPolicyGem unmappedTargetPolicy, + ReportingPolicyGem unmappedSourcePolicy, String defaultComponentModel, String defaultInjectionStrategy, boolean alwaysGenerateSpi, boolean verbose) { this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; this.suppressGeneratorVersionComment = suppressGeneratorVersionComment; this.unmappedTargetPolicy = unmappedTargetPolicy; + this.unmappedSourcePolicy = unmappedSourcePolicy; this.defaultComponentModel = defaultComponentModel; this.defaultInjectionStrategy = defaultInjectionStrategy; this.alwaysGenerateSpi = alwaysGenerateSpi; @@ -47,6 +50,10 @@ public ReportingPolicyGem getUnmappedTargetPolicy() { return unmappedTargetPolicy; } + public ReportingPolicyGem getUnmappedSourcePolicy() { + return unmappedSourcePolicy; + } + public String getDefaultComponentModel() { return defaultComponentModel; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/SourceTargetMapperWithoutMapperConfig.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/SourceTargetMapperWithoutMapperConfig.java new file mode 100644 index 0000000000..4ab2d3688c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/SourceTargetMapperWithoutMapperConfig.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.ap.test.unmappedsource; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.unmappedtarget.Source; +import org.mapstruct.ap.test.unmappedtarget.Target; +import org.mapstruct.factory.Mappers; + +/** + * + * @author Yusuf Kemal Ozcan + */ +@Mapper +public interface SourceTargetMapperWithoutMapperConfig { + + SourceTargetMapperWithoutMapperConfig INSTANCE = Mappers.getMapper( SourceTargetMapperWithoutMapperConfig.class ); + + Target sourceToTarget(Source source); + + Source targetToSource(Target target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java index 59d44a6baf..9b244592a2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java @@ -14,6 +14,7 @@ import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; import static org.assertj.core.api.Assertions.assertThat; @@ -67,4 +68,46 @@ public void shouldLeaveUnmappedSourcePropertyUnset() { ) public void shouldRaiseErrorDueToUnsetSourceProperty() { } + + @ProcessorTest + @WithClasses({ Source.class, Target.class, + org.mapstruct.ap.test.unmappedsource.SourceTargetMapperWithoutMapperConfig.class }) + @ProcessorOption(name = "mapstruct.unmappedTargetPolicy", value = "IGNORE") + @ProcessorOption(name = "mapstruct.unmappedSourcePolicy", value = "ERROR") + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = org.mapstruct.ap.test.unmappedsource.SourceTargetMapperWithoutMapperConfig.class, + kind = Kind.ERROR, + line = 22, + message = "Unmapped source property: \"qux\"."), + @Diagnostic(type = org.mapstruct.ap.test.unmappedsource.SourceTargetMapperWithoutMapperConfig.class, + kind = Kind.ERROR, + line = 24, + message = "Unmapped source property: \"bar\".") + } + ) + public void shouldRaiseErrorDueToUnsetSourcePropertyWithPolicySetViaProcessorOption() { + } + + @ProcessorTest + @WithClasses({ Source.class, Target.class, + org.mapstruct.ap.test.unmappedsource.SourceTargetMapperWithoutMapperConfig.class }) + @ProcessorOption(name = "mapstruct.unmappedTargetPolicy", value = "IGNORE") + @ProcessorOption(name = "mapstruct.unmappedSourcePolicy", value = "WARN") + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = org.mapstruct.ap.test.unmappedsource.SourceTargetMapperWithoutMapperConfig.class, + kind = Kind.WARNING, + line = 22, + message = "Unmapped source property: \"qux\"."), + @Diagnostic(type = org.mapstruct.ap.test.unmappedsource.SourceTargetMapperWithoutMapperConfig.class, + kind = Kind.WARNING, + line = 24, + message = "Unmapped source property: \"bar\".") + } + ) + public void shouldLeaveUnmappedSourcePropertyUnsetWithWarnPolicySetViaProcessorOption() { + } } From 2c23b935db4079ddeaeb65c47284d823b8451349 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 14 Aug 2021 13:50:20 +0200 Subject: [PATCH 199/597] #2541 fix incorrect name for TypeVar with ElementType.TYPE_USE for javac-with-errorprone --- .../ap/internal/model/common/TypeFactory.java | 12 ++++- .../ap/test/bugs/_2541/Issue2541Mapper.java | 47 +++++++++++++++++++ .../ap/test/bugs/_2541/Issue2541Test.java | 28 +++++++++++ .../ap/test/bugs/_2541/Nullable.java | 22 +++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Issue2541Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Issue2541Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Nullable.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 127ec74270..b209626725 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -278,7 +278,17 @@ else if (componentTypeMirror.getKind().isPrimitive()) { isInterface = false; // When the component type is primitive and is annotated with ElementType.TYPE_USE then // the typeMirror#toString returns (@CustomAnnotation :: byte) for the javac compiler - name = mirror.getKind().isPrimitive() ? NativeTypes.getName( mirror.getKind() ) : mirror.toString(); + if ( mirror.getKind().isPrimitive() ) { + name = NativeTypes.getName( mirror.getKind() ); + } + // When the component type is type var and is annotated with ElementType.TYPE_USE then + // the typeMirror#toString returns (@CustomAnnotation T) for the errorprone javac compiler + else if ( mirror.getKind() == TypeKind.TYPEVAR ) { + name = ( (TypeVariable) mirror ).asElement().getSimpleName().toString(); + } + else { + name = mirror.toString(); + } packageName = null; qualifiedName = name; typeElement = null; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Issue2541Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Issue2541Mapper.java new file mode 100644 index 0000000000..0531f5242c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Issue2541Mapper.java @@ -0,0 +1,47 @@ +/* + * 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._2541; + +import java.util.Optional; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2541Mapper { + + Issue2541Mapper INSTANCE = Mappers.getMapper( Issue2541Mapper.class ); + + Target map(Source source); + + default Optional toOptional(@Nullable T value) { + return Optional.ofNullable( value ); + } + + class Target { + private Optional value; + + public Optional getValue() { + return value; + } + + public void setValue(Optional value) { + this.value = value; + } + } + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Issue2541Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Issue2541Test.java new file mode 100644 index 0000000000..d1b8bd7f52 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Issue2541Test.java @@ -0,0 +1,28 @@ +/* + * 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._2541; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Issue2541Mapper.class, + Nullable.class, +}) +class Issue2541Test { + + @ProcessorTest + void shouldGenerateCorrectCode() { + Issue2541Mapper.Target target = Issue2541Mapper.INSTANCE.map( new Issue2541Mapper.Source( null ) ); + + assertThat( target.getValue() ).isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Nullable.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Nullable.java new file mode 100644 index 0000000000..24c52eb906 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2541/Nullable.java @@ -0,0 +1,22 @@ +/* + * 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._2541; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ + ElementType.METHOD, + ElementType.TYPE_USE +}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Nullable { + +} From b59a23965a0c77206851a118c5b1673819735dd4 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 29 Aug 2021 11:24:34 +0200 Subject: [PATCH 200/597] #2554 Records should not treat collections as alternative target accessors --- .../java/org/mapstruct/itest/records/Car.java | 24 ++++++++++++++ .../itest/records/CarAndWheelMapper.java | 31 +++++++++++++++++++ .../org/mapstruct/itest/records/CarDto.java | 15 +++++++++ .../itest/records/WheelPosition.java | 22 +++++++++++++ .../mapstruct/itest/records/RecordsTest.java | 14 +++++++++ .../ap/internal/model/common/Type.java | 7 +++++ 6 files changed, 113 insertions(+) create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Car.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CarAndWheelMapper.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CarDto.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/WheelPosition.java diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Car.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Car.java new file mode 100644 index 0000000000..9332c47dfb --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Car.java @@ -0,0 +1,24 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class Car { + + private List wheelPositions; + + public List getWheelPositions() { + return wheelPositions; + } + + public void setWheelPositions(List wheelPositions) { + this.wheelPositions = wheelPositions; + } +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CarAndWheelMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CarAndWheelMapper.java new file mode 100644 index 0000000000..cc69ae7605 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CarAndWheelMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface CarAndWheelMapper { + + CarAndWheelMapper INSTANCE = Mappers.getMapper( CarAndWheelMapper.class ); + + default String stringFromWheelPosition(WheelPosition source) { + return source == null ? null : source.getPosition(); + } + + default WheelPosition wheelPositionFromString(String source) { + return source == null ? null : new WheelPosition(source); + } + + CarDto carDtoFromCar(Car source); + + Car carFromCarDto(CarDto source); +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CarDto.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CarDto.java new file mode 100644 index 0000000000..5470550060 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CarDto.java @@ -0,0 +1,15 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public record CarDto(List wheelPositions) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/WheelPosition.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/WheelPosition.java new file mode 100644 index 0000000000..fe8016ddbf --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/WheelPosition.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +/** + * @author Filip Hrisafov + */ +public class WheelPosition { + + private final String position; + + public WheelPosition(String position) { + this.position = position; + } + + public String getPosition() { + return position; + } +} diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java index b404681806..e3d055345e 100644 --- a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java @@ -5,6 +5,8 @@ */ package org.mapstruct.itest.records; +import java.util.Arrays; + import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; @@ -47,4 +49,16 @@ public void shouldMapIntoGenericRecord() { assertThat( value ).isNotNull(); assertThat( value.value() ).isEqualTo( "Kermit" ); } + + @Test + public void shouldMapIntoRecordWithList() { + Car car = new Car(); + car.setWheelPositions( Arrays.asList( new WheelPosition( "left" ) ) ); + + CarDto carDto = CarAndWheelMapper.INSTANCE.carDtoFromCar(car); + + assertThat( carDto ).isNotNull(); + assertThat( carDto.wheelPositions() ) + .containsExactly( "left" ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 4da9128f98..e13d7b9ddd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -874,6 +874,13 @@ private List getAdders() { * @return an unmodifiable list of alternative target accessors. */ private List getAlternativeTargetAccessors() { + if ( alternativeTargetAccessors != null ) { + return alternativeTargetAccessors; + } + + if ( isRecord() ) { + alternativeTargetAccessors = Collections.emptyList(); + } if ( alternativeTargetAccessors == null ) { From 8b84f5b7d78979be979940b8ded1cbbeb868e32c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Tue, 21 Sep 2021 22:17:24 +0200 Subject: [PATCH 201/597] #2591 Update dependencies so tests run on Java 18 Update GitHub Actions for tests to run on Java 11, 13, 16, 17 and 18-ea --- .github/workflows/java-ea.yml | 4 ++-- .github/workflows/main.yml | 4 ++-- parent/pom.xml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index 598a8979a6..f2b2a99be4 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - java: [17-ea] + java: [18-ea] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: @@ -21,4 +21,4 @@ jobs: with: java-version: ${{ matrix.java }} - name: 'Test' - run: ./mvnw ${MAVEN_ARGS} install -DskipDistribution=true + run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 741f8bce61..b023631628 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - java: [11, 13, 16] + java: [11, 13, 16, 17] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: @@ -23,7 +23,7 @@ jobs: with: java-version: ${{ matrix.java }} - name: 'Test' - run: ./mvnw ${MAVEN_ARGS} install -DskipDistribution=true + run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true linux: name: 'Linux JDK 8' runs-on: ubuntu-latest diff --git a/parent/pom.xml b/parent/pom.xml index 8ce94764ea..f4edccfc85 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -25,7 +25,7 @@ 3.0.0-M3 3.0.0-M5 3.1.0 - 5.3.3 + 5.3.10 1.6.0 8.36.1 5.8.0-M1 From f167e7a20c7e1bf4f87aab8e760983a359b43bbb Mon Sep 17 00:00:00 2001 From: valery1707 Date: Tue, 21 Sep 2021 10:42:25 +0300 Subject: [PATCH 202/597] Fix typo in JavaDoc --- core/src/main/java/org/mapstruct/Mapping.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 0af73f3687..7ca637f0ca 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -54,7 +54,7 @@ *

      Example 2: Mapping properties with different names

      *
      
        * // We need map Human.companyName to HumanDto.company
      - * // we can use @Mapping with parameters {@link #source()} and {@link #source()}
      + * // we can use @Mapping with parameters {@link #source()} and {@link #target()}
        * @Mapper
        * public interface HumanMapper {
        *    @Mapping(source="companyName", target="company")
      @@ -476,6 +476,4 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy()
            */
           Class mappingControl() default MappingControl.class;
       
      -
      -
       }
      
      From 5df6b7a75b97e58687027470e9048e6f6534bca5 Mon Sep 17 00:00:00 2001
      From: Zegveld <41897697+Zegveld@users.noreply.github.com>
      Date: Tue, 19 Oct 2021 20:44:25 +0200
      Subject: [PATCH 203/597] #131, #2438, #366 Add support for Type-Refinement
       (Downcast) Mapping (#2512)
      
      Add new `@SubclassMapping` for creating Downcast mapping.
      When a parent mapping method is annotated with `@SubclassMapping`
      it will now generate an instanceof check inside the parent mapping
      and generate the subclass mappings if they are not manually defined.
      
      There is also `SubclassExhaustiveStrategy` for controlling what MapStruct should do in case the target type is abstract and there is no suitable way to create it.
      ---
       .../main/java/org/mapstruct/BeanMapping.java  |  12 +
       core/src/main/java/org/mapstruct/Mapper.java  |  12 +
       .../main/java/org/mapstruct/MapperConfig.java |  12 +
       .../mapstruct/SubclassExhaustiveStrategy.java |  28 +++
       .../java/org/mapstruct/SubclassMapping.java   |  84 +++++++
       .../java/org/mapstruct/SubclassMappings.java  |  58 +++++
       ...apter-10-advanced-mapping-options.asciidoc |  43 ++++
       .../ap/internal/gem/GemGenerator.java         |   4 +
       .../gem/SubclassExhaustiveStrategyGem.java    |  27 +++
       .../model/AbstractMappingMethodBuilder.java   |  47 +++-
       .../ap/internal/model/BeanMappingMethod.java  | 138 ++++++++++-
       .../ap/internal/model/ForgedMethod.java       |  35 ++-
       .../ap/internal/model/SubclassMapping.java    |  69 ++++++
       .../model/assignment/ReturnWrapper.java       |  20 ++
       .../model/source/BeanMappingOptions.java      |  20 +-
       .../internal/model/source/DefaultOptions.java |   9 +-
       .../model/source/DelegatingOptions.java       |   7 +-
       .../model/source/MapperConfigOptions.java     |   8 +
       .../internal/model/source/MapperOptions.java  |   8 +
       .../model/source/MappingMethodOptions.java    |  31 ++-
       .../internal/model/source/SourceMethod.java   |  13 +-
       .../model/source/SubclassMappingOptions.java  | 171 +++++++++++++
       .../model/source/SubclassValidator.java       |  53 ++++
       .../processor/MethodRetrievalProcessor.java   | 229 +++++++++++++++---
       .../mapstruct/ap/internal/util/Message.java   |   6 +-
       .../ap/internal/model/BeanMappingMethod.ftl   |  15 ++
       .../model/assignment/ReturnWrapper.ftl        |  17 ++
       .../ErroneousSubclassMapper1.java             |  24 ++
       .../ErroneousSubclassUpdateMapper.java        |  28 +++
       .../MappingInheritanceTest.java               |  50 ++++
       .../subclassmapping/SimpleSubclassMapper.java |  31 +++
       .../test/subclassmapping/SubclassMapper.java  |  31 +++
       .../SubclassMapperUsingExistingMappings.java  |  36 +++
       .../subclassmapping/SubclassMappingTest.java  | 139 +++++++++++
       .../SubclassOrderWarningMapper.java           |  31 +++
       .../AbstractSuperClassTest.java               |  70 ++++++
       .../abstractsuperclass/AbstractVehicle.java   |  18 ++
       .../abstractsuperclass/Bike.java              |  18 ++
       .../abstractsuperclass/BikeDto.java           |  18 ++
       .../abstractsuperclass/Car.java               |  18 ++
       .../abstractsuperclass/CarDto.java            |  18 ++
       ...sSubclassWithAbstractSuperClassMapper.java |  27 +++
       .../abstractsuperclass/Motorcycle.java        |  10 +
       .../abstractsuperclass/MotorcycleDto.java     |  10 +
       .../SubclassWithAbstractSuperClassMapper.java |  26 ++
       .../abstractsuperclass/VehicleCollection.java |  17 ++
       .../VehicleCollectionDto.java                 |  17 ++
       .../abstractsuperclass/VehicleDto.java        |  18 ++
       .../fixture/AbstractParentSource.java         |  10 +
       .../fixture/AbstractParentTarget.java         |  10 +
       .../fixture/ImplementedParentSource.java      |  10 +
       .../fixture/ImplementedParentTarget.java      |  10 +
       .../fixture/InterfaceParentSource.java        |  10 +
       .../fixture/InterfaceParentTarget.java        |  10 +
       .../subclassmapping/fixture/SubSource.java    |  18 ++
       .../fixture/SubSourceOther.java               |  18 ++
       .../subclassmapping/fixture/SubTarget.java    |  18 ++
       .../fixture/SubTargetOther.java               |  18 ++
       .../fixture/SubclassAbstractMapper.java       |  21 ++
       .../fixture/SubclassFixtureTest.java          |  53 ++++
       .../fixture/SubclassImplementedMapper.java    |  17 ++
       .../fixture/SubclassInterfaceMapper.java      |  19 ++
       .../test/subclassmapping/mappables/Bike.java  |  18 ++
       .../subclassmapping/mappables/BikeDto.java    |  18 ++
       .../test/subclassmapping/mappables/Car.java   |  18 ++
       .../subclassmapping/mappables/CarDto.java     |  18 ++
       .../subclassmapping/mappables/HatchBack.java  |  18 ++
       .../mappables/HatchBackDto.java               |  18 ++
       .../subclassmapping/mappables/Source.java     |  42 ++++
       .../mappables/SourceSubclass.java             |  33 +++
       .../subclassmapping/mappables/Target.java     |  54 +++++
       .../subclassmapping/mappables/Vehicle.java    |  27 +++
       .../mappables/VehicleCollection.java          |  17 ++
       .../mappables/VehicleCollectionDto.java       |  17 ++
       .../subclassmapping/mappables/VehicleDto.java |  27 +++
       .../fixture/SubclassAbstractMapperImpl.java   |  59 +++++
       .../SubclassImplementedMapperImpl.java        |  61 +++++
       .../fixture/SubclassInterfaceMapperImpl.java  |  59 +++++
       78 files changed, 2505 insertions(+), 72 deletions(-)
       create mode 100644 core/src/main/java/org/mapstruct/SubclassExhaustiveStrategy.java
       create mode 100644 core/src/main/java/org/mapstruct/SubclassMapping.java
       create mode 100644 core/src/main/java/org/mapstruct/SubclassMappings.java
       create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/gem/SubclassExhaustiveStrategyGem.java
       create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/SubclassMapping.java
       create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.java
       create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java
       create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassValidator.java
       create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.ftl
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousSubclassMapper1.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousSubclassUpdateMapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/MappingInheritanceTest.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SimpleSubclassMapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMapperUsingExistingMappings.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassOrderWarningMapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/AbstractSuperClassTest.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/AbstractVehicle.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Bike.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/BikeDto.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Car.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CarDto.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/ErroneousSubclassWithAbstractSuperClassMapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Motorcycle.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MotorcycleDto.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/SubclassWithAbstractSuperClassMapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleCollection.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleCollectionDto.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleDto.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentSource.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentTarget.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentSource.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentTarget.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/InterfaceParentSource.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/InterfaceParentTarget.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSource.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceOther.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTarget.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTargetOther.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassFixtureTest.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Bike.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/BikeDto.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Car.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/CarDto.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/HatchBack.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/HatchBackDto.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Source.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/SourceSubclass.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Target.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Vehicle.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleCollection.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleCollectionDto.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleDto.java
       create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java
       create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapperImpl.java
       create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapperImpl.java
      
      diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java
      index 3359cd8914..be3df0d29d 100644
      --- a/core/src/main/java/org/mapstruct/BeanMapping.java
      +++ b/core/src/main/java/org/mapstruct/BeanMapping.java
      @@ -14,6 +14,7 @@
       import org.mapstruct.control.MappingControl;
       
       import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
      +import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
       
       /**
        * Configures the mapping between two bean types.
      @@ -116,6 +117,17 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy()
            */
           NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
       
      +    /**
      +     * Determines how to handle missing implementation for super classes when using the {@link SubclassMapping}.
      +     *
      +     * Overrides the setting on {@link MapperConfig} and {@link Mapper}.
      +     *
      +     * @return strategy to handle missing implementation combined with {@link SubclassMappings}.
      +     *
      +     * @since 1.5
      +     */
      +    SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR;
      +
           /**
            * Default ignore all mappings. All mappings have to be defined manually. No automatic mapping will take place. No
            * warning will be issued on missing source or target properties.
      diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java
      index 98022f1a6c..9ddd9bff5c 100644
      --- a/core/src/main/java/org/mapstruct/Mapper.java
      +++ b/core/src/main/java/org/mapstruct/Mapper.java
      @@ -15,6 +15,7 @@
       import org.mapstruct.factory.Mappers;
       
       import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
      +import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
       
       /**
        * Marks an interface or abstract class as a mapper and activates the generation of a implementation of that type via
      @@ -237,6 +238,17 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default
            */
           NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
       
      +    /**
      +     * Determines how to handle missing implementation for super classes when using the {@link SubclassMapping}.
      +     *
      +     * Can be overridden by the one on {@link BeanMapping}, but overrides {@link MapperConfig}.
      +     *
      +     * @return strategy to handle missing implementation combined with {@link SubclassMappings}.
      +     *
      +     * @since 1.5
      +     */
      +    SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR;
      +
           /**
            * Determines whether to use field or constructor injection. This is only used on annotated based component models
            * such as CDI, Spring and JSR 330.
      diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java
      index ecfd888ca2..877495867c 100644
      --- a/core/src/main/java/org/mapstruct/MapperConfig.java
      +++ b/core/src/main/java/org/mapstruct/MapperConfig.java
      @@ -15,6 +15,7 @@
       import org.mapstruct.factory.Mappers;
       
       import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
      +import static org.mapstruct.SubclassExhaustiveStrategy.COMPILE_ERROR;
       
       /**
        * Marks a class or interface as configuration source for generated mappers. This allows to share common configurations
      @@ -210,6 +211,17 @@ MappingInheritanceStrategy mappingInheritanceStrategy()
            */
           NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
       
      +    /**
      +     * Determines how to handle missing implementation for super classes when using the {@link SubclassMapping}.
      +     *
      +     * Can be overridden by the one on {@link BeanMapping} or {@link Mapper}.
      +     *
      +     * @return strategy to handle missing implementation combined with {@link SubclassMappings}.
      +     *
      +     * @since 1.5
      +     */
      +    SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR;
      +
           /**
            * Determines whether to use field or constructor injection. This is only used on annotated based component models
            * such as CDI, Spring and JSR 330.
      diff --git a/core/src/main/java/org/mapstruct/SubclassExhaustiveStrategy.java b/core/src/main/java/org/mapstruct/SubclassExhaustiveStrategy.java
      new file mode 100644
      index 0000000000..a60d067faa
      --- /dev/null
      +++ b/core/src/main/java/org/mapstruct/SubclassExhaustiveStrategy.java
      @@ -0,0 +1,28 @@
      +/*
      + * Copyright MapStruct Authors.
      + *
      + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
      + */
      +package org.mapstruct;
      +
      +/**
      + * Strategy for dealing with subclassMapping annotated methods.
      + *
      + * @since 1.5
      + * @author Ben Zegveld
      + */
      +public enum SubclassExhaustiveStrategy {
      +
      +    /**
      +     * If there is no valid constructor or known method to create the return value of a with `@SubclassMapping`
      +     * annotated mapping then a compilation error will be thrown.
      +     */
      +    COMPILE_ERROR,
      +
      +    /**
      +     * If there is no valid constructor or known method to create the return value of a with `@SubclassMapping`
      +     * annotated mapping then an {@link IllegalArgumentException} will be thrown if a call is made with a type for which
      +     * there is no {@link SubclassMapping} available.
      +     */
      +    RUNTIME_EXCEPTION;
      +}
      diff --git a/core/src/main/java/org/mapstruct/SubclassMapping.java b/core/src/main/java/org/mapstruct/SubclassMapping.java
      new file mode 100644
      index 0000000000..bfd4f9bec5
      --- /dev/null
      +++ b/core/src/main/java/org/mapstruct/SubclassMapping.java
      @@ -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;
      +
      +import java.lang.annotation.ElementType;
      +import java.lang.annotation.Repeatable;
      +import java.lang.annotation.Retention;
      +import java.lang.annotation.RetentionPolicy;
      +import java.lang.annotation.Target;
      +
      +import org.mapstruct.util.Experimental;
      +
      +/**
      + * Configures the mapping to handle hierarchy of the source type.
      + * 

      + * The subclass to be mapped is to be specified via {@link #source()}. + * The subclass to map to is to be specified via {@link #target()}. + *

      + *

      + * This annotation can be combined with @Mapping annotations. + *

      + * + *
      
      + * @Mapper
      + * public interface MyMapper {
      + *    @SubclassMapping (target = TargetSubclass.class, source = SourceSubclass.class)
      + *    TargetParent mapParent(SourceParent parent);
      + *
      + *    TargetSubclass mapSubclass(SourceSubclass subInstant);
      + * }
      + * 
      + * Below follow examples of the implementation for the mapParent method. + * Example 1: For parents that cannot be created. (e.g. abstract classes or interfaces) + *
      
      + * // generates
      + * @Override
      + * public TargetParent mapParent(SourceParent parent) {
      + *     if (parent instanceof SourceSubclass) {
      + *         return mapSubclass( (SourceSubclass) parent );
      + *     }
      + *     else {
      + *         throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for "
      + *                    + parent.getClass());
      + *     }
      + * }
      + * 
      + * Example 2: For parents that can be created. (e.g. normal classes or interfaces with + * @Mappper( uses = ObjectFactory.class ) ) + *
      
      + * // generates
      + * @Override
      + * public TargetParent mapParent(SourceParent parent) {
      + *     TargetParent targetParent1;
      + *     if (parent instanceof SourceSubclass) {
      + *         targetParent1 = mapSubclass( (SourceSubclass) parent );
      + *     }
      + *     else {
      + *         targetParent1 = new TargetParent();
      + *         // ...
      + *     }
      + * }
      + * 
      + * + * @author Ben Zegveld + * @since 1.5 + */ +@Repeatable(value = SubclassMappings.class) +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +@Experimental +public @interface SubclassMapping { + /** + * @return the source subclass to check for before using the default mapping as fallback. + */ + Class source(); + + /** + * @return the target subclass to map the source to. + */ + Class target(); +} diff --git a/core/src/main/java/org/mapstruct/SubclassMappings.java b/core/src/main/java/org/mapstruct/SubclassMappings.java new file mode 100644 index 0000000000..938b836f77 --- /dev/null +++ b/core/src/main/java/org/mapstruct/SubclassMappings.java @@ -0,0 +1,58 @@ +/* + * 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; + +import org.mapstruct.util.Experimental; + +/** + * Configures the SubclassMappings of several subclasses. + *

      + * TIP: When using java 8 or later, you can omit the @SubclassMappings + * Wrapper annotation and directly specify several @SubclassMapping annotations + * on one method. + *

      + *

      These two examples are equal. + *

      + *
      
      + * // before java 8
      + * @Mapper
      + * public interface MyMapper {
      + *     @SubclassMappings({
      + *         @SubclassMapping(source = FirstSub.class, target = FirstTargetSub.class),
      + *         @SubclassMapping(source = SecondSub.class, target = SecondTargetSub.class)
      + *     })
      + *     ParentTarget toParentTarget(Parent parent);
      + * }
      + * 
      + *
      
      + * // java 8 and later
      + * @Mapper
      + * public interface MyMapper {
      + *     @SubclassMapping(source = First.class, target = FirstTargetSub.class),
      + *     @SubclassMapping(source = SecondSub.class, target = SecondTargetSub.class)
      + *     ParentTarget toParentTarget(Parent parent);
      + * }
      + * 
      + * + * @author Ben Zegveld + * @since 1.5 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +@Experimental +public @interface SubclassMappings { + + /** + * @return the subclassMappings to apply. + */ + SubclassMapping[] value(); + +} diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index d3ab2424b1..5d7da371a1 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -110,6 +110,49 @@ public interface SourceTargetMapper { The example demonstrates how to use defaultExpression to set an `ID` field if the source field is null, this could be used to take the existing `sourceId` from the source object if it is set, or create a new `Id` if it isn't. Please note that the fully qualified package name is specified because MapStruct does not take care of the import of the `UUID` class (unless it’s used otherwise explicitly in the `SourceTargetMapper`). This can be resolved by defining imports on the @Mapper annotation (see <>). +[[sub-class-mappings]] +=== Subclass Mapping + +When both input and result types have an inheritance relation, you would want the correct specialization be mapped to the matching specialization. +Suppose an `Apple` and a `Banana`, which are both specializations of `Fruit`. + +.Specifying the sub class mappings of a fruit mapping +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface FruitMapper { + + @SubclassMapping( source = AppleDto.class, target = Apple.class ) + @SubclassMapping( source = BananaDto.class, target = Banana.class ) + Fruit map( FruitDto source ); + +} +---- +==== + +If you would just use a normal mapping both the `AppleDto` and the `BananaDto` would be made into a `Fruit` object, instead of an `Apple` and a `Banana` object. +By using the subclass mapping an `AppleDtoToApple` mapping will be used for `AppleDto` objects, and an `BananaDtoToBanana` mapping will be used for `BananaDto` objects. +If you try to map a `GrapeDto` it would still turn it into a `Fruit`. + +In the case that the `Fruit` is an abstract class or an interface, you would get a compile error. + +To allow mappings for abstract classes or interfaces you need to set the `subclassExhaustiveStrategy` to `RUNTIME_EXCEPTION`, you can do this at the `@MapperConfig`, `@Mapper` or `@BeanMapping` annotations. If you then pass a `GrapeDto` an `IllegalArgumentException` will be thrown because it is unknown how to map a `GrapeDto`. +Adding the missing (`@SubclassMapping`) for it will fix that. + +[TIP] +==== +If the mapping method for the subclasses does not exist it will be created and any other annotations on the fruit mapping method will be inherited by the newly generated mappings. +==== + +[NOTE] +==== +Combining `@SubclassMapping` with update methods is not supported. +If you try to use subclass mappings there will be a compile error. +The same issue exists for the `@Context` and `@TargetType` parameters. +==== + [[determining-result-type]] === Determining the result type diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index df5a90d5c2..e0491cbc4e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -28,6 +28,8 @@ import org.mapstruct.Named; import org.mapstruct.ObjectFactory; import org.mapstruct.Qualifier; +import org.mapstruct.SubclassMapping; +import org.mapstruct.SubclassMappings; import org.mapstruct.TargetType; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; @@ -47,6 +49,8 @@ @GemDefinition(BeanMapping.class) @GemDefinition(EnumMapping.class) @GemDefinition(MapMapping.class) +@GemDefinition(SubclassMapping.class) +@GemDefinition(SubclassMappings.class) @GemDefinition(TargetType.class) @GemDefinition(MappingTarget.class) @GemDefinition(DecoratedWith.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/SubclassExhaustiveStrategyGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/SubclassExhaustiveStrategyGem.java new file mode 100644 index 0000000000..2367d649a9 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/SubclassExhaustiveStrategyGem.java @@ -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.internal.gem; + +/** + * Gem for the enum {@link org.mapstruct.SubclassExhaustiveStrategy} + * + * @author Ben Zegveld + */ +public enum SubclassExhaustiveStrategyGem { + + COMPILE_ERROR( false ), + RUNTIME_EXCEPTION( true ); + + private final boolean abstractReturnTypeAllowed; + + SubclassExhaustiveStrategyGem(boolean abstractReturnTypeAllowed) { + this.abstractReturnTypeAllowed = abstractReturnTypeAllowed; + } + + public boolean isAbstractReturnTypeAllowed() { + return abstractReturnTypeAllowed; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index 91b8427630..efc7a71469 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -5,26 +5,45 @@ */ package org.mapstruct.ap.internal.model; +import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.gem.BuilderGem; +import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.util.Strings; -import static org.mapstruct.ap.internal.model.ForgedMethod.forElementMapping; - /** * An abstract builder that can be reused for building {@link MappingMethod}(s). * * @author Filip Hrisafov */ public abstract class AbstractMappingMethodBuilder, - M extends MappingMethod> extends AbstractBaseBuilder { + M extends MappingMethod> + extends AbstractBaseBuilder { public AbstractMappingMethodBuilder(Class selfType) { super( selfType ); } + private interface ForgeMethodCreator { + ForgedMethod createMethod(String name, Type sourceType, Type returnType, Method basedOn, + ForgedMethodHistory history, boolean forgedNameBased); + + static ForgeMethodCreator forSubclassMapping(MappingReferences mappingReferences) { + return (name, sourceType, targetType, method, description, + forgedNameBased) -> ForgedMethod + .forSubclassMapping( + name, + sourceType, + targetType, + method, + mappingReferences, + description, + forgedNameBased ); + } + } + public abstract M build(); private ForgedMethodHistory description; @@ -35,6 +54,20 @@ public AbstractMappingMethodBuilder(Class selfType) { protected abstract boolean shouldUsePropertyNamesInHistory(); Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) { + return forgeMapping( sourceRHS, sourceType, targetType, ForgedMethod::forElementMapping ); + } + + Assignment forgeSubclassMapping(SourceRHS sourceRHS, Type sourceType, Type targetType, + MappingReferences mappingReferences) { + return forgeMapping( + sourceRHS, + sourceType, + targetType, + ForgeMethodCreator.forSubclassMapping( mappingReferences ) ); + } + + private Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType, + ForgeMethodCreator forgeMethodCreator) { if ( !canGenerateAutoSubMappingBetween( sourceType, targetType ) ) { return null; } @@ -55,14 +88,14 @@ Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) { shouldUsePropertyNamesInHistory(), sourceRHS.getSourceErrorMessagePart() ); - ForgedMethod forgedMethod = forElementMapping( name, sourceType, targetType, method, description, true ); + ForgedMethod forgedMethod = + forgeMethodCreator.createMethod( name, sourceType, targetType, method, description, true ); BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); return createForgedAssignment( sourceRHS, ctx.getTypeFactory().builderTypeFor( targetType, builder ), - forgedMethod - ); + forgedMethod ); } private String getName(Type sourceType, Type targetType) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 17bf2cb465..0b299067f5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -41,10 +41,14 @@ import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; import org.mapstruct.ap.internal.model.beanmapping.SourceReference; import org.mapstruct.ap.internal.model.beanmapping.TargetReference; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.BuilderType; +import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.ParameterBinding; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder; import org.mapstruct.ap.internal.model.source.BeanMappingOptions; @@ -52,7 +56,9 @@ import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; +import org.mapstruct.ap.internal.model.source.SubclassMappingOptions; import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; +import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; @@ -82,16 +88,15 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { private final Map> constructorMappingsByParameter; private final List constantMappings; private final List constructorConstantMappings; + private final List subclassMappings; private final Type returnTypeToConstruct; private final BuilderType returnTypeBuilder; private final MethodReference finalizerMethod; private final MappingReferences mappingReferences; - public static class Builder { + public static class Builder extends AbstractMappingMethodBuilder { - private MappingBuilderContext ctx; - private Method method; private Type userDefinedReturnType; /* returnType to construct can have a builder */ @@ -110,9 +115,13 @@ public static class Builder { private MethodReference factoryMethod; private boolean hasFactoryMethod; - public Builder mappingContext(MappingBuilderContext mappingContext) { - this.ctx = mappingContext; - return this; + public Builder() { + super( Builder.class ); + } + + @Override + protected boolean shouldUsePropertyNamesInHistory() { + return true; } public Builder userDefinedReturnType(Type userDefinedReturnType) { @@ -126,12 +135,12 @@ public Builder returnTypeBuilder( BuilderType returnTypeBuilder ) { } public Builder sourceMethod(SourceMethod sourceMethod) { - this.method = sourceMethod; + method( sourceMethod ); return this; } public Builder forgedMethod(ForgedMethod forgedMethod) { - this.method = forgedMethod; + method( forgedMethod ); mappingReferences = forgedMethod.getMappingReferences(); Parameter sourceParameter = first( Parameter.getSourceParameters( forgedMethod.getParameters() ) ); for ( MappingReference mappingReference: mappingReferences.getMappingReferences() ) { @@ -165,7 +174,9 @@ public BeanMappingMethod build() { // the userDefinedReturn type can also require a builder. That buildertype is already set returnTypeImpl = returnTypeBuilder.getBuilder(); initializeFactoryMethod( returnTypeImpl, selectionParameters ); - if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) { + if ( factoryMethod != null + || allowsAbstractReturnTypeAndIsEitherAbstractOrCanBeConstructed( returnTypeImpl ) + || doesNotAllowAbstractReturnTypeAndCanBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } else { @@ -185,7 +196,9 @@ else if ( userDefinedReturnType != null ) { else if ( !method.isUpdateMethod() ) { returnTypeImpl = method.getReturnType(); initializeFactoryMethod( returnTypeImpl, selectionParameters ); - if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) { + if ( factoryMethod != null + || allowsAbstractReturnTypeAndIsEitherAbstractOrCanBeConstructed( returnTypeImpl ) + || doesNotAllowAbstractReturnTypeAndCanBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; } else { @@ -333,6 +346,11 @@ else if ( !method.isUpdateMethod() ) { } + List subclasses = new ArrayList<>(); + for ( SubclassMappingOptions subclassMappingOptions : method.getOptions().getSubclassMappings() ) { + subclasses.add( createSubclassMapping( subclassMappingOptions ) ); + } + MethodReference finalizeMethod = null; if ( shouldCallFinalizerMethod( returnTypeToConstruct ) ) { @@ -350,10 +368,74 @@ else if ( !method.isUpdateMethod() ) { beforeMappingMethods, afterMappingMethods, finalizeMethod, - mappingReferences + mappingReferences, + subclasses ); } + private boolean doesNotAllowAbstractReturnTypeAndCanBeConstructed(Type returnTypeImpl) { + return !isAbstractReturnTypeAllowed() + && canReturnTypeBeConstructed( returnTypeImpl ); + } + + private boolean allowsAbstractReturnTypeAndIsEitherAbstractOrCanBeConstructed(Type returnTypeImpl) { + return isAbstractReturnTypeAllowed() + && isReturnTypeAbstractOrCanBeConstructed( returnTypeImpl ); + } + + private SubclassMapping createSubclassMapping(SubclassMappingOptions subclassMappingOptions) { + TypeFactory typeFactory = ctx.getTypeFactory(); + Type sourceType = typeFactory.getType( subclassMappingOptions.getSource() ); + Type targetType = typeFactory.getType( subclassMappingOptions.getTarget() ); + + SourceRHS rightHandSide = new SourceRHS( + "subclassMapping", + sourceType, + Collections.emptySet(), + "SubclassMapping for " + sourceType.getFullyQualifiedName() ); + SelectionCriteria criteria = + SelectionCriteria + .forMappingMethods( + new SelectionParameters( + Collections.emptyList(), + Collections.emptyList(), + subclassMappingOptions.getTarget(), + ctx.getTypeUtils() ).withSourceRHS( rightHandSide ), + null, + null, + false ); + Assignment assignment = ctx + .getMappingResolver() + .getTargetAssignment( + method, + null, + targetType, + FormattingParameters.EMPTY, + criteria, + rightHandSide, + null, + () -> forgeSubclassMapping( + rightHandSide, + sourceType, + targetType, + mappingReferences ) ); + String sourceArgument = null; + for ( Parameter parameter : method.getSourceParameters() ) { + if ( ctx + .getTypeUtils() + .isAssignable( sourceType.getTypeMirror(), parameter.getType().getTypeMirror() ) ) { + sourceArgument = parameter.getName(); + assignment.setSourceLocalVarName( "(" + sourceType.createReferenceName() + ") " + sourceArgument ); + } + } + return new SubclassMapping( sourceType, sourceArgument, targetType, assignment ); + } + + private boolean isAbstractReturnTypeAllowed() { + return method.getOptions().getBeanMapping().getSubclassExhaustiveStrategy().isAbstractReturnTypeAllowed() + && !method.getOptions().getSubclassMappings().isEmpty(); + } + private void initializeMappingReferencesIfNeeded(Type resultTypeToMap) { if ( mappingReferences == null && method instanceof SourceMethod ) { Set readAndWriteTargetProperties = new HashSet<>( unprocessedTargetProperties.keySet() ); @@ -561,6 +643,20 @@ else if ( !returnType.hasAccessibleConstructor() ) { return error; } + private boolean isReturnTypeAbstractOrCanBeConstructed(Type returnType) { + boolean error = true; + if ( !returnType.isAbstract() && !returnType.hasAccessibleConstructor() ) { + ctx + .getMessager() + .printMessage( + method.getExecutable(), + Message.GENERAL_NO_SUITABLE_CONSTRUCTOR, + returnType.describe() ); + error = false; + } + return error; + } + /** * Find a factory method for a return type or for a builder. * @param returnTypeImpl the return type implementation to construct @@ -1613,7 +1709,8 @@ private BeanMappingMethod(Method method, List beforeMappingReferences, List afterMappingReferences, MethodReference finalizerMethod, - MappingReferences mappingReferences) { + MappingReferences mappingReferences, + List subclassMappings) { super( method, existingVariableNames, @@ -1659,6 +1756,7 @@ else if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) { } } this.returnTypeToConstruct = returnTypeToConstruct; + this.subclassMappings = subclassMappings; } public List getConstantMappings() { @@ -1669,6 +1767,10 @@ public List getConstructorConstantMappings() { return constructorConstantMappings; } + public List getSubclassMappings() { + return subclassMappings; + } + public List propertyMappingsByParameter(Parameter parameter) { // issues: #909 and #1244. FreeMarker has problem getting values from a map when the search key is size or value return mappingsByParameter.getOrDefault( parameter.getName(), Collections.emptyList() ); @@ -1683,6 +1785,15 @@ public Type getReturnTypeToConstruct() { return returnTypeToConstruct; } + public boolean hasSubclassMappings() { + return !subclassMappings.isEmpty(); + } + + public boolean isAbstractReturnType() { + return getFactoryMethod() == null && !hasConstructorMappings() && returnTypeToConstruct != null + && returnTypeToConstruct.isAbstract(); + } + public boolean hasConstructorMappings() { return !constructorMappingsByParameter.isEmpty() || !constructorConstantMappings.isEmpty(); } @@ -1702,6 +1813,9 @@ public Set getImportTypes() { types.addAll( propertyMapping.getTargetType().getImportTypes() ); } } + for ( SubclassMapping subclassMapping : subclassMappings ) { + types.addAll( subclassMapping.getImportTypes() ); + } if ( returnTypeToConstruct != null ) { types.addAll( returnTypeToConstruct.getImportTypes() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java index b97d8be111..a1f5b091ce 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; + import javax.lang.model.element.ExecutableElement; import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; @@ -42,6 +43,7 @@ public class ForgedMethod implements Method { private final Method basedOn; private final boolean forgedNameBased; + private MappingMethodOptions options; /** * Creates a new forged method with the given name for mapping a method parameter to a property. @@ -121,6 +123,33 @@ public static ForgedMethod forElementMapping(String name, Type sourceType, Type ); } + /** + * Creates a new forged method for mapping a SubclassMapping element + * + * @param name the (unique name) for this method + * @param sourceType the source type + * @param returnType the return type. + * @param basedOn the method that (originally) triggered this nested method generation. + * @param history a parent forged method if this is a forged method within a forged method + * @param forgedNameBased forges a name based (matched) mapping method + * + * @return a new forge method + */ + public static ForgedMethod forSubclassMapping(String name, Type sourceType, Type returnType, Method basedOn, + MappingReferences mappingReferences, ForgedMethodHistory history, + boolean forgedNameBased) { + return new ForgedMethod( + name, + sourceType, + returnType, + basedOn.getContextParameters(), + basedOn, + history, + mappingReferences == null ? MappingReferences.empty() : mappingReferences, + forgedNameBased + ); + } + private ForgedMethod(String name, Type sourceType, Type returnType, List additionalParameters, Method basedOn, ForgedMethodHistory history, MappingReferences mappingReferences, boolean forgedNameBased) { @@ -155,6 +184,8 @@ private ForgedMethod(String name, Type sourceType, Type returnType, List getImportTypes() { + return Collections.singleton( sourceType ); + } + + public AssignmentWrapper getAssignment() { + return new ReturnWrapper( assignment ); + } + + public String getSourceArgument() { + return sourceArgument; + } + + @Override + public boolean equals(final Object other) { + if ( !( other instanceof SubclassMapping ) ) { + return false; + } + SubclassMapping castOther = (SubclassMapping) other; + return Objects.equals( sourceType, castOther.sourceType ) && Objects.equals( targetType, castOther.targetType ); + } + + @Override + public int hashCode() { + return Objects.hash( sourceType, targetType ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.java new file mode 100644 index 0000000000..76e2c19fa7 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.java @@ -0,0 +1,20 @@ +/* + * 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.internal.model.assignment; + +import org.mapstruct.ap.internal.model.common.Assignment; + +/** + * Decorates an assignment as a return variable. + * + * @author Ben Zegveld + */ +public class ReturnWrapper extends AssignmentWrapper { + + public ReturnWrapper(Assignment decoratedAssignment) { + super( decoratedAssignment, false ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index 9567dd80bf..cc03878362 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -12,18 +12,18 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import org.mapstruct.ap.internal.gem.ReportingPolicyGem; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.util.TypeUtils; - -import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.gem.BeanMappingGem; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; +import org.mapstruct.ap.internal.gem.SubclassExhaustiveStrategyGem; +import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.tools.gem.GemValue; /** @@ -92,6 +92,7 @@ private static boolean isConsistent(BeanMappingGem gem, ExecutableElement method && !gem.nullValueCheckStrategy().hasValue() && !gem.nullValuePropertyMappingStrategy().hasValue() && !gem.nullValueMappingStrategy().hasValue() + && !gem.subclassExhaustiveStrategy().hasValue() && !gem.unmappedTargetPolicy().hasValue() && !gem.ignoreByDefault().hasValue() && !gem.builder().hasValue() ) { @@ -139,6 +140,15 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { .orElse( next().getNullValueMappingStrategy() ); } + @Override + public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::subclassExhaustiveStrategy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( SubclassExhaustiveStrategyGem::valueOf ) + .orElse( next().getSubclassExhaustiveStrategy() ); + } + @Override public ReportingPolicyGem unmappedTargetPolicy() { return Optional.ofNullable( beanMapping ).map( BeanMappingGem::unmappedTargetPolicy ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index d52d094930..5a7161b4c8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -9,9 +9,7 @@ import java.util.Set; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.gem.InjectionStrategyGem; @@ -21,6 +19,9 @@ import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; +import org.mapstruct.ap.internal.gem.SubclassExhaustiveStrategyGem; +import org.mapstruct.ap.internal.option.Options; +import org.mapstruct.ap.internal.util.ElementUtils; public class DefaultOptions extends DelegatingOptions { @@ -119,6 +120,10 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { return NullValueMappingStrategyGem.valueOf( mapper.nullValueMappingStrategy().getDefaultValue() ); } + public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { + return SubclassExhaustiveStrategyGem.valueOf( mapper.subclassExhaustiveStrategy().getDefaultValue() ); + } + public BuilderGem getBuilder() { // TODO: I realized this is not correct, however it needs to be null in order to keep downward compatibility // but assuming a default @Builder will make testcases fail. Not having a default means that you need to diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java index 564358ec98..b2b36b68a6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -10,7 +10,6 @@ import java.util.Set; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; @@ -20,6 +19,8 @@ import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; +import org.mapstruct.ap.internal.gem.SubclassExhaustiveStrategyGem; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.spi.TypeHierarchyErroneousException; /** @@ -97,6 +98,10 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { return next.getNullValueMappingStrategy(); } + public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { + return next.getSubclassExhaustiveStrategy(); + } + public BuilderGem getBuilder() { return next.getBuilder(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java index 7f8df7bf42..5288ab1304 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -19,6 +19,7 @@ import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; +import org.mapstruct.ap.internal.gem.SubclassExhaustiveStrategyGem; public class MapperConfigOptions extends DelegatingOptions { @@ -126,6 +127,13 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { next().getNullValueMappingStrategy(); } + @Override + public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { + return mapperConfig.subclassExhaustiveStrategy().hasValue() ? + SubclassExhaustiveStrategyGem.valueOf( mapperConfig.subclassExhaustiveStrategy().get() ) : + next().getSubclassExhaustiveStrategy(); + } + @Override public BuilderGem getBuilder() { return mapperConfig.builder().hasValue() ? mapperConfig.builder().get() : next().getBuilder(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index 82d17a9846..86aae34052 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -25,6 +25,7 @@ import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; +import org.mapstruct.ap.internal.gem.SubclassExhaustiveStrategyGem; public class MapperOptions extends DelegatingOptions { @@ -155,6 +156,13 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { next().getNullValueMappingStrategy(); } + @Override + public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { + return mapper.subclassExhaustiveStrategy().hasValue() ? + SubclassExhaustiveStrategyGem.valueOf( mapper.subclassExhaustiveStrategy().get() ) : + next().getSubclassExhaustiveStrategy(); + } + @Override public BuilderGem getBuilder() { return mapper.builder().hasValue() ? mapper.builder().get() : next().getBuilder(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 859a4c6f5f..45b1175f8d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -34,7 +34,8 @@ public class MappingMethodOptions { null, null, null, - Collections.emptyList() + Collections.emptyList(), + Collections.emptySet() ); private MapperOptions mapper; @@ -45,12 +46,14 @@ public class MappingMethodOptions { private EnumMappingOptions enumMappingOptions; private List valueMappings; private boolean fullyInitialized; + private Set subclassMapping; public MappingMethodOptions(MapperOptions mapper, Set mappings, IterableMappingOptions iterableMapping, MapMappingOptions mapMapping, BeanMappingOptions beanMapping, EnumMappingOptions enumMappingOptions, - List valueMappings) { + List valueMappings, + Set subclassMapping) { this.mapper = mapper; this.mappings = mappings; this.iterableMapping = iterableMapping; @@ -58,6 +61,7 @@ public MappingMethodOptions(MapperOptions mapper, Set mappings, this.beanMapping = beanMapping; this.enumMappingOptions = enumMappingOptions; this.valueMappings = valueMappings; + this.subclassMapping = subclassMapping; } /** @@ -97,6 +101,10 @@ public List getValueMappings() { return valueMappings; } + public Set getSubclassMappings() { + return subclassMapping; + } + public void setIterableMapping(IterableMappingOptions iterableMapping) { this.iterableMapping = iterableMapping; } @@ -188,6 +196,8 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } } + // Do NOT inherit subclass mapping options!!! + Set newMappings = new LinkedHashSet<>(); for ( MappingOptions mapping : templateOptions.getMappings() ) { if ( isInverse ) { @@ -315,4 +325,21 @@ private String getFirstTargetPropertyName(MappingOptions mapping) { return getPropertyEntries( mapping )[0]; } + /** + * SubclassMappingOptions are not inherited to forged methods. They would result in an infinite loop if they were. + * + * @return a MappingMethodOptions without SubclassMappingOptions. + */ + public static MappingMethodOptions getForgedMethodInheritedOptions(MappingMethodOptions options) { + return new MappingMethodOptions( + options.mapper, + options.mappings, + options.iterableMapping, + options.mapMapping, + options.beanMapping, + options.enumMappingOptions, + options.valueMappings, + Collections.emptySet() ); + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 86f19618d4..5895ecba2a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -95,6 +95,7 @@ public static class Builder { private EnumMappingOptions enumMappingOptions; private ParameterProvidedMethods contextProvidedMethods; private List typeParameters; + private Set subclassMappings; private boolean verboseLogging; @@ -153,6 +154,11 @@ public Builder setEnumMappingOptions(EnumMappingOptions enumMappingOptions) { return this; } + public Builder setSubclassMappings(Set subclassMappings) { + this.subclassMappings = subclassMappings; + return this; + } + public Builder setTypeUtils(TypeUtils typeUtils) { this.typeUtils = typeUtils; return this; @@ -194,6 +200,10 @@ public SourceMethod build() { mappings = Collections.emptySet(); } + if ( subclassMappings == null ) { + subclassMappings = Collections.emptySet(); + } + MappingMethodOptions mappingMethodOptions = new MappingMethodOptions( mapper, mappings, @@ -201,7 +211,8 @@ public SourceMethod build() { mapMapping, beanMapping, enumMappingOptions, - valueMappings + valueMappings, + subclassMappings ); this.typeParameters = this.executable.getTypeParameters() diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java new file mode 100644 index 0000000000..54a0ba4565 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java @@ -0,0 +1,171 @@ +/* + * 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.internal.model.source; + +import java.util.List; +import java.util.Set; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.internal.gem.SubclassMappingGem; +import org.mapstruct.ap.internal.gem.SubclassMappingsGem; +import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.TypeUtils; +import org.mapstruct.ap.spi.TypeHierarchyErroneousException; + +import static org.mapstruct.ap.internal.util.Message.SUBCLASSMAPPING_ILLEGAL_SUBCLASS; +import static org.mapstruct.ap.internal.util.Message.SUBCLASSMAPPING_NO_VALID_SUPERCLASS; +import static org.mapstruct.ap.internal.util.Message.SUBCLASSMAPPING_UPDATE_METHODS_NOT_SUPPORTED; + +/** + * Represents a subclass mapping as configured via {@code @SubclassMapping}. + * + * @author Ben Zegveld + */ +public class SubclassMappingOptions extends DelegatingOptions { + + private final TypeMirror source; + private final TypeMirror target; + + public SubclassMappingOptions(TypeMirror source, TypeMirror target, DelegatingOptions next) { + super( next ); + this.source = source; + this.target = target; + } + + @Override + public boolean hasAnnotation() { + return source != null && target != null; + } + + private static boolean isConsistent(SubclassMappingGem gem, ExecutableElement method, FormattingMessager messager, + TypeUtils typeUtils, List sourceParameters, Type resultType, + SubclassValidator subclassValidator) { + + if ( resultType == null ) { + messager.printMessage( method, gem.mirror(), SUBCLASSMAPPING_UPDATE_METHODS_NOT_SUPPORTED ); + return false; + } + + TypeMirror sourceSubclass = gem.source().getValue(); + TypeMirror targetSubclass = gem.target().getValue(); + TypeMirror targetParentType = resultType.getTypeMirror(); + validateTypeMirrors( sourceSubclass, targetSubclass, targetParentType ); + + boolean isConsistent = true; + + boolean isChildOfAParameter = false; + for ( Parameter sourceParameter : sourceParameters ) { + TypeMirror sourceParentType = sourceParameter.getType().getTypeMirror(); + validateTypeMirrors( sourceParentType ); + isChildOfAParameter = isChildOfAParameter || isChildOfParent( typeUtils, sourceSubclass, sourceParentType ); + } + if ( !isChildOfAParameter ) { + messager + .printMessage( + method, + gem.mirror(), + SUBCLASSMAPPING_NO_VALID_SUPERCLASS, + sourceSubclass.toString() ); + isConsistent = false; + } + if ( !isChildOfParent( typeUtils, targetSubclass, targetParentType ) ) { + messager + .printMessage( + method, + gem.mirror(), + SUBCLASSMAPPING_ILLEGAL_SUBCLASS, + targetParentType.toString(), + targetSubclass.toString() ); + isConsistent = false; + } + subclassValidator.isInCorrectOrder( method, gem.mirror(), targetSubclass ); + return isConsistent; + } + + private static void validateTypeMirrors(TypeMirror... typeMirrors) { + for ( TypeMirror typeMirror : typeMirrors ) { + if ( typeMirror == null ) { + // When a class used in uses or imports is created by another annotation processor + // then javac will not return correct TypeMirror with TypeKind#ERROR, but rather a string "" + // the gem tools would return a null TypeMirror in that case. + // Therefore throw TypeHierarchyErroneousException so we can postpone the generation of the mapper + throw new TypeHierarchyErroneousException( typeMirror ); + } + } + } + + private static boolean isChildOfParent(TypeUtils typeUtils, TypeMirror childType, TypeMirror parentType) { + return typeUtils.isSubtype( childType, parentType ); + } + + public TypeMirror getSource() { + return source; + } + + public TypeMirror getTarget() { + return target; + } + + public static void addInstances(SubclassMappingsGem gem, ExecutableElement method, + BeanMappingOptions beanMappingOptions, FormattingMessager messager, + TypeUtils typeUtils, Set mappings, + List sourceParameters, Type resultType) { + SubclassValidator subclassValidator = new SubclassValidator( messager, typeUtils ); + for ( SubclassMappingGem subclassMappingGem : gem.value().get() ) { + addAndValidateInstance( + subclassMappingGem, + method, + beanMappingOptions, + messager, + typeUtils, + mappings, + sourceParameters, + resultType, + subclassValidator ); + } + } + + public static void addInstance(SubclassMappingGem subclassMapping, ExecutableElement method, + BeanMappingOptions beanMappingOptions, FormattingMessager messager, + TypeUtils typeUtils, Set mappings, + List sourceParameters, Type resultType) { + addAndValidateInstance( + subclassMapping, + method, + beanMappingOptions, + messager, + typeUtils, + mappings, + sourceParameters, + resultType, + new SubclassValidator( messager, typeUtils ) ); + } + + private static void addAndValidateInstance(SubclassMappingGem subclassMapping, ExecutableElement method, + BeanMappingOptions beanMappingOptions, FormattingMessager messager, + TypeUtils typeUtils, Set mappings, + List sourceParameters, Type resultType, + SubclassValidator subclassValidator) { + if ( !isConsistent( + subclassMapping, + method, + messager, + typeUtils, + sourceParameters, + resultType, + subclassValidator ) ) { + return; + } + + TypeMirror sourceSubclass = subclassMapping.source().getValue(); + TypeMirror targetSubclass = subclassMapping.target().getValue(); + + mappings.add( new SubclassMappingOptions( sourceSubclass, targetSubclass, beanMappingOptions ) ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassValidator.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassValidator.java new file mode 100644 index 0000000000..dd79ba0c02 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassValidator.java @@ -0,0 +1,53 @@ +/* + * 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.internal.model.source; + +import java.util.ArrayList; +import java.util.List; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.TypeUtils; + +/** + * Handles the validation of multiple @SubclassMapping annotations on the same method. + * + * @author Ben Zegveld + */ +class SubclassValidator { + + private final FormattingMessager messager; + private final List handledSubclasses = new ArrayList<>(); + private final TypeUtils typeUtils; + + SubclassValidator(FormattingMessager messager, TypeUtils typeUtils) { + this.messager = messager; + this.typeUtils = typeUtils; + } + + public boolean isInCorrectOrder(Element e, AnnotationMirror annotation, TypeMirror sourceType) { + for ( TypeMirror typeMirror : handledSubclasses ) { + if ( typeUtils.isAssignable( sourceType, typeMirror ) ) { + messager + .printMessage( + e, + annotation, + Message.SUBCLASSMAPPING_ILLOGICAL_ORDER, + sourceType, + typeMirror, + sourceType, + typeMirror ); + return true; + } + } + handledSubclasses.add( sourceType ); + return false; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index c6e510d315..9bf55e731f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -29,6 +29,8 @@ import org.mapstruct.ap.internal.gem.MappingGem; import org.mapstruct.ap.internal.gem.MappingsGem; import org.mapstruct.ap.internal.gem.ObjectFactoryGem; +import org.mapstruct.ap.internal.gem.SubclassMappingGem; +import org.mapstruct.ap.internal.gem.SubclassMappingsGem; import org.mapstruct.ap.internal.gem.ValueMappingGem; import org.mapstruct.ap.internal.gem.ValueMappingsGem; import org.mapstruct.ap.internal.model.common.Parameter; @@ -42,6 +44,7 @@ import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SourceMethod; +import org.mapstruct.ap.internal.model.source.SubclassMappingOptions; import org.mapstruct.ap.internal.model.source.ValueMappingOptions; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.util.AccessorNamingUtils; @@ -52,6 +55,7 @@ import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.spi.EnumTransformationStrategy; +import org.mapstruct.tools.gem.Gem; /** * A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s @@ -67,6 +71,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor mappingOptions = - getMappings( method, method, beanMappingOptions, new LinkedHashSet<>(), new HashSet<>() ); + RepeatableMappings repeatableMappings = new RepeatableMappings(); + Set mappingOptions = repeatableMappings.getMappings( method, beanMappingOptions ); IterableMappingOptions iterableMappingOptions = IterableMappingOptions.fromGem( IterableMappingGem.instanceOn( method ), @@ -299,6 +305,15 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, messager ); + // We want to get as much error reporting as possible. + // If targetParameter is not null it means we have an update method + Set subclassMappingOptions = getSubclassMappings( + sourceParameters, + targetParameter != null ? null : resultType, + method, + beanMappingOptions + ); + return new SourceMethod.Builder() .setExecutable( method ) .setParameters( parameters ) @@ -311,6 +326,7 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, .setMapMappingOptions( mapMappingOptions ) .setValueMappingOptionss( getValueMappings( method ) ) .setEnumMappingOptions( enumMappingOptions ) + .setSubclassMappings( subclassMappingOptions ) .setTypeUtils( typeUtils ) .setTypeFactory( typeFactory ) .setPrototypeMethods( prototypeMethods ) @@ -554,56 +570,189 @@ private boolean isStreamTypeOrIterableFromJavaStdLib(Type type) { } /** - * Retrieves the mappings configured via {@code @Mapping} from the given - * method. + * Retrieves the mappings configured via {@code @Mapping} from the given method. * * @param method The method of interest - * @param element Element of interest: method, or (meta) annotation * @param beanMapping options coming from bean mapping method - * @param mappingOptions LinkedSet of mappings found so far - * * @return The mappings for the given method, keyed by target property name */ - private Set getMappings(ExecutableElement method, Element element, - BeanMappingOptions beanMapping, Set mappingOptions, - Set handledElements) { - - for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { - Element lElement = annotationMirror.getAnnotationType().asElement(); - if ( isAnnotation( lElement, MAPPING_FQN ) ) { - // although getInstanceOn does a search on annotation mirrors, the order is preserved - MappingGem mapping = MappingGem.instanceOn( element ); - MappingOptions.addInstance( mapping, method, beanMapping, messager, typeUtils, mappingOptions ); - } - else if ( isAnnotation( lElement, MAPPINGS_FQN ) ) { - // although getInstanceOn does a search on annotation mirrors, the order is preserved - MappingsGem mappings = MappingsGem.instanceOn( element ); - MappingOptions.addInstances( mappings, method, beanMapping, messager, typeUtils, mappingOptions ); - } - else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) - && !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG ) - && !handledElements.contains( lElement ) - ) { - // recur over annotation mirrors - handledElements.add( lElement ); - getMappings( method, lElement, beanMapping, mappingOptions, handledElements ); - } + private Set getMappings(ExecutableElement method, BeanMappingOptions beanMapping) { + return new RepeatableMappings().getMappings( method, beanMapping ); + } + + /** + * Retrieves the subclass mappings configured via {@code @SubclassMapping} from the given method. + * + * @param method The method of interest + * @param beanMapping options coming from bean mapping method + * + * @return The subclass mappings for the given method + */ + private Set getSubclassMappings(List sourceParameters, Type resultType, + ExecutableElement method, BeanMappingOptions beanMapping) { + return new RepeatableSubclassMappings( sourceParameters, resultType ).getMappings( method, beanMapping ); + } + + private class RepeatableMappings extends RepeatableMappingAnnotations { + RepeatableMappings() { + super( MAPPING_FQN, MAPPINGS_FQN ); + } + + @Override + MappingGem singularInstanceOn(Element element) { + return MappingGem.instanceOn( element ); + } + + @Override + MappingsGem multipleInstanceOn(Element element) { + return MappingsGem.instanceOn( element ); + } + + @Override + void addInstance(MappingGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, + Set mappings) { + MappingOptions.addInstance( gem, method, beanMappingOptions, messager, typeUtils, mappings ); + } + + @Override + void addInstances(MappingsGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, + Set mappings) { + MappingOptions.addInstances( gem, method, beanMappingOptions, messager, typeUtils, mappings ); } - return mappingOptions; } - private boolean isAnnotationInPackage(Element element, String packageFQN ) { - if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { - return packageFQN.equals( elementUtils.getPackageOf( element ).getQualifiedName().toString() ); + private class RepeatableSubclassMappings + extends RepeatableMappingAnnotations { + private final List sourceParameters; + private final Type resultType; + + RepeatableSubclassMappings(List sourceParameters, Type resultType) { + super( SUB_CLASS_MAPPING_FQN, SUB_CLASS_MAPPINGS_FQN ); + this.sourceParameters = sourceParameters; + this.resultType = resultType; + } + + @Override + SubclassMappingGem singularInstanceOn(Element element) { + return SubclassMappingGem.instanceOn( element ); + } + + @Override + SubclassMappingsGem multipleInstanceOn(Element element) { + return SubclassMappingsGem.instanceOn( element ); + } + + @Override + void addInstance(SubclassMappingGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, + Set mappings) { + SubclassMappingOptions + .addInstance( + gem, + method, + beanMappingOptions, + messager, + typeUtils, + mappings, + sourceParameters, + resultType ); + } + + @Override + void addInstances(SubclassMappingsGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, + Set mappings) { + SubclassMappingOptions + .addInstances( + gem, + method, + beanMappingOptions, + messager, + typeUtils, + mappings, + sourceParameters, + resultType ); } - return false; } - private boolean isAnnotation(Element element, String annotationFQN) { - if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { - return annotationFQN.equals( ( (TypeElement) element ).getQualifiedName().toString() ); + private abstract class RepeatableMappingAnnotations { + + private final String singularFqn; + private final String multipleFqn; + + RepeatableMappingAnnotations(String singularFqn, String multipleFqn) { + this.singularFqn = singularFqn; + this.multipleFqn = multipleFqn; + } + + abstract SINGULAR singularInstanceOn(Element element); + + abstract MULTIPLE multipleInstanceOn(Element element); + + abstract void addInstance(SINGULAR gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, + Set mappings); + + abstract void addInstances(MULTIPLE gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, + Set mappings); + + /** + * Retrieves the mappings configured via {@code @Mapping} from the given method. + * + * @param method The method of interest + * @param beanMapping options coming from bean mapping method + * @return The mappings for the given method, keyed by target property name + */ + public Set getMappings(ExecutableElement method, BeanMappingOptions beanMapping) { + return getMappings( method, method, beanMapping, new LinkedHashSet<>(), new HashSet<>() ); + } + + /** + * Retrieves the mappings configured via {@code @Mapping} from the given method. + * + * @param method The method of interest + * @param element Element of interest: method, or (meta) annotation + * @param beanMapping options coming from bean mapping method + * @param mappingOptions LinkedSet of mappings found so far + * @return The mappings for the given method, keyed by target property name + */ + private Set getMappings(ExecutableElement method, Element element, + BeanMappingOptions beanMapping, LinkedHashSet mappingOptions, + Set handledElements) { + + for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { + Element lElement = annotationMirror.getAnnotationType().asElement(); + if ( isAnnotation( lElement, singularFqn ) ) { + // although getInstanceOn does a search on annotation mirrors, the order is preserved + SINGULAR mapping = singularInstanceOn( element ); + addInstance( mapping, method, beanMapping, mappingOptions ); + } + else if ( isAnnotation( lElement, multipleFqn ) ) { + // although getInstanceOn does a search on annotation mirrors, the order is preserved + MULTIPLE mappings = multipleInstanceOn( element ); + addInstances( mappings, method, beanMapping, mappingOptions ); + } + else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) + && !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG ) + && !handledElements.contains( lElement ) ) { + // recur over annotation mirrors + handledElements.add( lElement ); + getMappings( method, lElement, beanMapping, mappingOptions, handledElements ); + } + } + return mappingOptions; + } + + private boolean isAnnotationInPackage(Element element, String packageFQN) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return packageFQN.equals( elementUtils.getPackageOf( element ).getQualifiedName().toString() ); + } + return false; + } + + private boolean isAnnotation(Element element, String annotationFQN) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return annotationFQN.equals( ( (TypeElement) element ).getQualifiedName().toString() ); + } + return false; } - return false; } /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 8d328d4796..66b37de7a1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -114,6 +114,11 @@ public enum Message { ENUMMAPPING_NO_ELEMENTS( "'nameTransformationStrategy', 'configuration' and 'unexpectedValueMappingException' are undefined in @EnumMapping, define at least one of them." ), ENUMMAPPING_ILLEGAL_TRANSFORMATION( "Illegal transformation for '%s' EnumTransformationStrategy. Error: '%s'." ), + SUBCLASSMAPPING_ILLEGAL_SUBCLASS( "Class '%s' is not a subclass of '%s'." ), + SUBCLASSMAPPING_NO_VALID_SUPERCLASS( "Could not find a parameter that is a superclass for '%s'." ), + SUBCLASSMAPPING_UPDATE_METHODS_NOT_SUPPORTED( "SubclassMapping annotation can not be used for update methods." ), + SUBCLASSMAPPING_ILLOGICAL_ORDER( "SubclassMapping annotation for '%s' found after '%s', but all '%s' objects are also instances of '%s'.", Diagnostic.Kind.WARNING ), + LIFECYCLEMETHOD_AMBIGUOUS_PARAMETERS( "Lifecycle method has multiple matching parameters (e. g. same type), in this case please ensure to name the parameters in the lifecycle and mapping method identical. This lifecycle method will not be used for the mapping method '%s'.", Diagnostic.Kind.WARNING), DECORATOR_NO_SUBTYPE( "Specified decorator type is no subtype of the annotated mapper type." ), @@ -188,7 +193,6 @@ public enum Message { ; // CHECKSTYLE:ON - private final String description; private final Diagnostic.Kind kind; diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index f5d6e799e1..12a03c06ca 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -24,6 +24,17 @@ } + <#if hasSubclassMappings()> + <#list subclassMappings as subclass> + <#if subclass_index > 0>else if (${subclass.sourceArgument} instanceof <@includeModel object=subclass.sourceType/>) { + <@includeModel object=subclass.assignment existingInstanceMapping=existingInstanceMapping/> + } + + else { + + <#if isAbstractReturnType()> + throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for " + ${subclassMappings[0].sourceArgument}.getClass()); + <#else> <#if !existingInstanceMapping> <#if hasConstructorMappings()> <#if (sourceParameters?size > 1)> @@ -120,6 +131,10 @@ return ${resultName}; + + <#if hasSubclassMappings()> + } + } <#macro throws> <#if (thrownTypes?size > 0)><#lt> throws <@compress single_line=true> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.ftl new file mode 100644 index 0000000000..9f46f3604f --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.ftl @@ -0,0 +1,17 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.assignment.ReturnWrapper" --> +return <@_assignment/>; +<#macro _assignment> + <@includeModel object=assignment + targetBeanName=ext.targetBeanName + existingInstanceMapping=ext.existingInstanceMapping + targetReadAccessorName=ext.targetReadAccessorName + targetWriteAccessorName=ext.targetWriteAccessorName + targetType=ext.targetType/> + diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousSubclassMapper1.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousSubclassMapper1.java new file mode 100644 index 0000000000..fc5e37d7b5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousSubclassMapper1.java @@ -0,0 +1,24 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousSubclassMapper1 { + ErroneousSubclassMapper1 INSTANCE = Mappers.getMapper( ErroneousSubclassMapper1.class ); + + @SubclassMapping( source = Bike.class, target = BikeDto.class ) + @Mapping( target = "maker", ignore = true ) + CarDto map(Car vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousSubclassUpdateMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousSubclassUpdateMapper.java new file mode 100644 index 0000000000..b002b1e32d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousSubclassUpdateMapper.java @@ -0,0 +1,28 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousSubclassUpdateMapper { + ErroneousSubclassUpdateMapper INSTANCE = Mappers.getMapper( ErroneousSubclassUpdateMapper.class ); + + @SubclassMapping( source = Car.class, target = CarDto.class ) + @Mapping( source = "vehicleManufacturingCompany", target = "maker" ) + void map(@MappingTarget VehicleDto target, Car vehicle); + + @SubclassMapping( source = Car.class, target = CarDto.class ) + @Mapping( source = "vehicleManufacturingCompany", target = "maker" ) + VehicleDto mapWithReturnType(@MappingTarget VehicleDto target, Car vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/MappingInheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/MappingInheritanceTest.java new file mode 100644 index 0000000000..4cedd30510 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/MappingInheritanceTest.java @@ -0,0 +1,50 @@ +/* + * 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.subclassmapping; + +import org.assertj.core.api.Assertions; +import org.mapstruct.ap.test.subclassmapping.mappables.Source; +import org.mapstruct.ap.test.subclassmapping.mappables.SourceSubclass; +import org.mapstruct.ap.test.subclassmapping.mappables.Target; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.factory.Mappers; + +@IssueKey( "2438" ) +@WithClasses( { Source.class, SourceSubclass.class, Target.class } ) +class MappingInheritanceTest { + + @ProcessorTest + @WithClasses( { SubclassMapper.class } ) + void inheritance() { + SubclassMapper mapper = Mappers.getMapper( SubclassMapper.class ); + SourceSubclass sourceSubclass = new SourceSubclass( "f1", "f2", "f3", "f4", "f5" ); + + Target result = mapper.mapSuperclass( sourceSubclass ); + + Assertions.assertThat( result.getTarget1() ).isEqualTo( "f1" ); + Assertions.assertThat( result.getTarget2() ).isEqualTo( "f2" ); + Assertions.assertThat( result.getTarget3() ).isEqualTo( "f3" ); + Assertions.assertThat( result.getTarget4() ).isEqualTo( "f4" ); + Assertions.assertThat( result.getTarget5() ).isEqualTo( "f5" ); + } + + @ProcessorTest + @WithClasses( { SubclassMapper.class } ) + void superclass() { + SubclassMapper mapper = Mappers.getMapper( SubclassMapper.class ); + Source source = new Source( "f1", "f2", "f3" ); + + Target result = mapper.mapSuperclass( source ); + + Assertions.assertThat( result.getTarget1() ).isEqualTo( "f1" ); + Assertions.assertThat( result.getTarget2() ).isEqualTo( "f2" ); + Assertions.assertThat( result.getTarget3() ).isEqualTo( "f3" ); + Assertions.assertThat( result.getTarget4() ).isNull(); + Assertions.assertThat( result.getTarget5() ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SimpleSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SimpleSubclassMapper.java new file mode 100644 index 0000000000..eb71797864 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SimpleSubclassMapper.java @@ -0,0 +1,31 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollection; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollectionDto; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SimpleSubclassMapper { + SimpleSubclassMapper INSTANCE = Mappers.getMapper( SimpleSubclassMapper.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + @SubclassMapping( source = Car.class, target = CarDto.class ) + @SubclassMapping( source = Bike.class, target = BikeDto.class ) + @Mapping( source = "vehicleManufacturingCompany", target = "maker") + VehicleDto map(Vehicle vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMapper.java new file mode 100644 index 0000000000..8e77297dda --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMapper.java @@ -0,0 +1,31 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.InheritConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Source; +import org.mapstruct.ap.test.subclassmapping.mappables.SourceSubclass; +import org.mapstruct.ap.test.subclassmapping.mappables.Target; + +@Mapper +public interface SubclassMapper { + + @SubclassMapping( source = SourceSubclass.class, target = Target.class ) + @Mapping( target = "target1", source = "property1" ) + @Mapping( target = "target2", source = "property2" ) + @Mapping( target = "target3", source = "property3" ) + @Mapping( target = "target4", ignore = true ) + @Mapping( target = "target5", ignore = true ) + Target mapSuperclass(Source source); + + @InheritConfiguration( name = "mapSuperclass" ) + @Mapping( target = "target4", source = "subclassProperty" ) + @Mapping( target = "target5" ) // Have to declare in order to override ignore with default behavior + Target mapSubclass(SourceSubclass source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMapperUsingExistingMappings.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMapperUsingExistingMappings.java new file mode 100644 index 0000000000..efdd357e3d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMapperUsingExistingMappings.java @@ -0,0 +1,36 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollection; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollectionDto; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.factory.Mappers; + +@Mapper( unmappedTargetPolicy = ReportingPolicy.IGNORE ) +public interface SubclassMapperUsingExistingMappings { + SubclassMapperUsingExistingMappings INSTANCE = Mappers.getMapper( SubclassMapperUsingExistingMappings.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + default CarDto existingMappingMethod(Car domain) { + CarDto dto = new CarDto(); + dto.setName( "created through existing mapping." ); + return dto; + } + + @SubclassMapping( source = Car.class, target = CarDto.class ) + @SubclassMapping( source = Bike.class, target = BikeDto.class ) + VehicleDto map(Vehicle vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java new file mode 100644 index 0000000000..e5a77b56d3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java @@ -0,0 +1,139 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; +import org.mapstruct.ap.test.subclassmapping.mappables.HatchBack; +import org.mapstruct.ap.test.subclassmapping.mappables.HatchBackDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollection; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollectionDto; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("131") +@WithClasses({ + Bike.class, + BikeDto.class, + Car.class, + CarDto.class, + VehicleCollection.class, + VehicleCollectionDto.class, + Vehicle.class, + VehicleDto.class, + SimpleSubclassMapper.class, + SubclassMapperUsingExistingMappings.class, +}) +public class SubclassMappingTest { + + @ProcessorTest + void mappingIsDoneUsingSubclassMapping() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Car() ); + vehicles.getVehicles().add( new Bike() ); + + VehicleCollectionDto result = SimpleSubclassMapper.INSTANCE.map( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( CarDto.class, BikeDto.class ); + } + + @ProcessorTest + void existingMappingsAreUsedWhenFound() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Car() ); + + VehicleCollectionDto result = SubclassMapperUsingExistingMappings.INSTANCE.map( vehicles ); + + assertThat( result.getVehicles() ) + .extracting( VehicleDto::getName ) + .containsExactly( "created through existing mapping." ); + } + + @ProcessorTest + void subclassMappingInheritsMapping() { + VehicleCollection vehicles = new VehicleCollection(); + Car car = new Car(); + car.setVehicleManufacturingCompany( "BenZ" ); + vehicles.getVehicles().add( car ); + + VehicleCollectionDto result = SimpleSubclassMapper.INSTANCE.map( vehicles ); + + assertThat( result.getVehicles() ) + .extracting( VehicleDto::getMaker ) + .containsExactly( "BenZ" ); + } + + @ProcessorTest + @WithClasses({ + HatchBack.class, + HatchBackDto.class, + SubclassOrderWarningMapper.class, + }) + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diagnostics = { + @Diagnostic(type = SubclassOrderWarningMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 28, + alternativeLine = 30, + message = "SubclassMapping annotation for " + + "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBackDto' found after " + + "'org.mapstruct.ap.test.subclassmapping.mappables.CarDto', but all " + + "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBackDto' " + + "objects are also instances of " + + "'org.mapstruct.ap.test.subclassmapping.mappables.CarDto'.") + }) + void subclassOrderWarning() { + } + + @ProcessorTest + @WithClasses({ ErroneousSubclassUpdateMapper.class }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { + @Diagnostic(type = ErroneousSubclassUpdateMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 21, + message = "SubclassMapping annotation can not be used for update methods." + ), + @Diagnostic(type = ErroneousSubclassUpdateMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 25, + message = "SubclassMapping annotation can not be used for update methods." + ) + }) + void unsupportedUpdateMethod() { + } + + @ProcessorTest + @WithClasses({ ErroneousSubclassMapper1.class }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { + @Diagnostic(type = ErroneousSubclassMapper1.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 21, + message = "Could not find a parameter that is a superclass for " + + "'org.mapstruct.ap.test.subclassmapping.mappables.Bike'." + ), + @Diagnostic(type = ErroneousSubclassMapper1.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 21, + message = "Class 'org.mapstruct.ap.test.subclassmapping.mappables.CarDto'" + + " is not a subclass of " + + "'org.mapstruct.ap.test.subclassmapping.mappables.BikeDto'." + ) + }) + void erroneousMethodWithSourceTargetType() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassOrderWarningMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassOrderWarningMapper.java new file mode 100644 index 0000000000..2ff7a7d836 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassOrderWarningMapper.java @@ -0,0 +1,31 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; +import org.mapstruct.ap.test.subclassmapping.mappables.HatchBack; +import org.mapstruct.ap.test.subclassmapping.mappables.HatchBackDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollection; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollectionDto; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SubclassOrderWarningMapper { + SubclassOrderWarningMapper INSTANCE = Mappers.getMapper( SubclassOrderWarningMapper.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + @SubclassMapping( source = Car.class, target = CarDto.class ) + @SubclassMapping( source = HatchBack.class, target = HatchBackDto.class ) + @Mapping( source = "vehicleManufacturingCompany", target = "maker") + VehicleDto map(Vehicle vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/AbstractSuperClassTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/AbstractSuperClassTest.java new file mode 100644 index 0000000000..31fbf1e00e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/AbstractSuperClassTest.java @@ -0,0 +1,70 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@IssueKey("366") +@WithClasses({ + AbstractVehicle.class, + VehicleCollection.class, + Bike.class, + BikeDto.class, + Car.class, + CarDto.class, + VehicleCollectionDto.class, + VehicleDto.class, +}) +public class AbstractSuperClassTest { + + @ProcessorTest + @WithClasses( SubclassWithAbstractSuperClassMapper.class ) + void downcastMappingInCollection() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Car() ); + vehicles.getVehicles().add( new Bike() ); + + VehicleCollectionDto result = SubclassWithAbstractSuperClassMapper.INSTANCE.map( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( CarDto.class, BikeDto.class ); + } + + @ProcessorTest + @WithClasses( SubclassWithAbstractSuperClassMapper.class ) + void mappingOfUnknownChildThrowsIllegalArgumentException() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Car() ); + vehicles.getVehicles().add( new Motorcycle() ); + + assertThatThrownBy( () -> SubclassWithAbstractSuperClassMapper.INSTANCE.map( vehicles ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Not all subclasses are supported for this mapping. " + + "Missing for class org.mapstruct.ap.test.subclassmapping.abstractsuperclass.Motorcycle" ); + } + + @WithClasses( ErroneousSubclassWithAbstractSuperClassMapper.class ) + @ProcessorTest + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = { + @Diagnostic(type = ErroneousSubclassWithAbstractSuperClassMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 26, + message = "The return type VehicleDto is an abstract class or interface. " + + "Provide a non abstract / non interface result type or a factory method.") + }) + void compileErrorWithAbstractClass() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/AbstractVehicle.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/AbstractVehicle.java new file mode 100644 index 0000000000..7cb155c402 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/AbstractVehicle.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +public abstract class AbstractVehicle { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Bike.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Bike.java new file mode 100644 index 0000000000..72a84f629e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Bike.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +public class Bike extends AbstractVehicle { + private int numberOfGears; + + public int getNumberOfGears() { + return numberOfGears; + } + + public void setNumberOfGears(int numberOfGears) { + this.numberOfGears = numberOfGears; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/BikeDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/BikeDto.java new file mode 100644 index 0000000000..8950e26086 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/BikeDto.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +public class BikeDto extends VehicleDto { + private int numberOfGears; + + public int getNumberOfGears() { + return numberOfGears; + } + + public void setNumberOfGears(int numberOfGears) { + this.numberOfGears = numberOfGears; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Car.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Car.java new file mode 100644 index 0000000000..acd7e85f99 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Car.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +public class Car extends AbstractVehicle { + private boolean manual; + + public boolean isManual() { + return manual; + } + + public void setManual(boolean manual) { + this.manual = manual; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CarDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CarDto.java new file mode 100644 index 0000000000..6353b4c590 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CarDto.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +public class CarDto extends VehicleDto { + private boolean manual; + + public boolean isManual() { + return manual; + } + + public void setManual(boolean manual) { + this.manual = manual; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/ErroneousSubclassWithAbstractSuperClassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/ErroneousSubclassWithAbstractSuperClassMapper.java new file mode 100644 index 0000000000..a3406bff08 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/ErroneousSubclassWithAbstractSuperClassMapper.java @@ -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.subclassmapping.abstractsuperclass; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper( subclassExhaustiveStrategy = SubclassExhaustiveStrategy.COMPILE_ERROR ) +public interface ErroneousSubclassWithAbstractSuperClassMapper { + ErroneousSubclassWithAbstractSuperClassMapper INSTANCE = + Mappers.getMapper( ErroneousSubclassWithAbstractSuperClassMapper.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + CarDto map(Car car); + + BikeDto map(Bike bike); + + @SubclassMapping( source = Car.class, target = CarDto.class ) + @SubclassMapping( source = Bike.class, target = BikeDto.class ) + VehicleDto map(AbstractVehicle vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Motorcycle.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Motorcycle.java new file mode 100644 index 0000000000..f52edfd71f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/Motorcycle.java @@ -0,0 +1,10 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +public class Motorcycle extends AbstractVehicle { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MotorcycleDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MotorcycleDto.java new file mode 100644 index 0000000000..cada5e8e65 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MotorcycleDto.java @@ -0,0 +1,10 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +public class MotorcycleDto extends VehicleDto { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/SubclassWithAbstractSuperClassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/SubclassWithAbstractSuperClassMapper.java new file mode 100644 index 0000000000..b0185f1f87 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/SubclassWithAbstractSuperClassMapper.java @@ -0,0 +1,26 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper( subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION ) +public interface SubclassWithAbstractSuperClassMapper { + SubclassWithAbstractSuperClassMapper INSTANCE = Mappers.getMapper( SubclassWithAbstractSuperClassMapper.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + CarDto map(Car car); + + BikeDto map(Bike bike); + + @SubclassMapping( source = Car.class, target = CarDto.class ) + @SubclassMapping( source = Bike.class, target = BikeDto.class ) + VehicleDto map(AbstractVehicle vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleCollection.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleCollection.java new file mode 100644 index 0000000000..cbc847ff42 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleCollection.java @@ -0,0 +1,17 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +import java.util.ArrayList; +import java.util.Collection; + +public class VehicleCollection { + private Collection vehicles = new ArrayList<>(); + + public Collection getVehicles() { + return vehicles; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleCollectionDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleCollectionDto.java new file mode 100644 index 0000000000..1ea4e018a5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleCollectionDto.java @@ -0,0 +1,17 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +import java.util.ArrayList; +import java.util.Collection; + +public class VehicleCollectionDto { + private Collection vehicles = new ArrayList<>(); + + public Collection getVehicles() { + return vehicles; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleDto.java new file mode 100644 index 0000000000..5f38db68d2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/VehicleDto.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +public abstract class VehicleDto { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentSource.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentSource.java new file mode 100644 index 0000000000..7f9f19a2ab --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentSource.java @@ -0,0 +1,10 @@ +/* + * 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.subclassmapping.fixture; + +public abstract class AbstractParentSource { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentTarget.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentTarget.java new file mode 100644 index 0000000000..3215aee644 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentTarget.java @@ -0,0 +1,10 @@ +/* + * 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.subclassmapping.fixture; + +public abstract class AbstractParentTarget { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentSource.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentSource.java new file mode 100644 index 0000000000..94dde481b0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentSource.java @@ -0,0 +1,10 @@ +/* + * 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.subclassmapping.fixture; + +public class ImplementedParentSource extends AbstractParentSource { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentTarget.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentTarget.java new file mode 100644 index 0000000000..7f3d254e21 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentTarget.java @@ -0,0 +1,10 @@ +/* + * 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.subclassmapping.fixture; + +public class ImplementedParentTarget extends AbstractParentTarget { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/InterfaceParentSource.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/InterfaceParentSource.java new file mode 100644 index 0000000000..b1ad034102 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/InterfaceParentSource.java @@ -0,0 +1,10 @@ +/* + * 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.subclassmapping.fixture; + +public interface InterfaceParentSource { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/InterfaceParentTarget.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/InterfaceParentTarget.java new file mode 100644 index 0000000000..0e41f58b36 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/InterfaceParentTarget.java @@ -0,0 +1,10 @@ +/* + * 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.subclassmapping.fixture; + +public interface InterfaceParentTarget { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSource.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSource.java new file mode 100644 index 0000000000..abb4c0dfba --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSource.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.fixture; + +public class SubSource extends ImplementedParentSource implements InterfaceParentSource { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceOther.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceOther.java new file mode 100644 index 0000000000..6b83b78973 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceOther.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.fixture; + +public class SubSourceOther extends ImplementedParentSource implements InterfaceParentSource { + private final String finalValue; + + public SubSourceOther(String finalValue) { + this.finalValue = finalValue; + } + + public String getFinalValue() { + return finalValue; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTarget.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTarget.java new file mode 100644 index 0000000000..0227ec325d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTarget.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.fixture; + +public class SubTarget extends ImplementedParentTarget implements InterfaceParentTarget { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTargetOther.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTargetOther.java new file mode 100644 index 0000000000..f77c7c7a24 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTargetOther.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.fixture; + +public class SubTargetOther extends ImplementedParentTarget implements InterfaceParentTarget { + private final String finalValue; + + public SubTargetOther(String finalValue) { + this.finalValue = finalValue; + } + + public String getFinalValue() { + return finalValue; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapper.java new file mode 100644 index 0000000000..8ef7908c30 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapper.java @@ -0,0 +1,21 @@ +/* + * 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.subclassmapping.fixture; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.SubclassMapping; + +import static org.mapstruct.SubclassExhaustiveStrategy.RUNTIME_EXCEPTION; + +@Mapper +public interface SubclassAbstractMapper { + + @BeanMapping( subclassExhaustiveStrategy = RUNTIME_EXCEPTION ) + @SubclassMapping( source = SubSource.class, target = SubTarget.class ) + @SubclassMapping( source = SubSourceOther.class, target = SubTargetOther.class ) + AbstractParentTarget map(AbstractParentSource item); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassFixtureTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassFixtureTest.java new file mode 100644 index 0000000000..14dc16d4ab --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassFixtureTest.java @@ -0,0 +1,53 @@ +/* + * 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.subclassmapping.fixture; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +@WithClasses({ + AbstractParentSource.class, + AbstractParentTarget.class, + ImplementedParentSource.class, + ImplementedParentTarget.class, + InterfaceParentSource.class, + InterfaceParentTarget.class, + SubSource.class, + SubSourceOther.class, + SubTarget.class, + SubTargetOther.class, +}) +public class SubclassFixtureTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses( { + SubclassInterfaceMapper.class, + } ) + void subclassInterfaceParentFixture() { + generatedSource.addComparisonToFixtureFor( SubclassInterfaceMapper.class ); + } + + @ProcessorTest + @WithClasses( { + SubclassAbstractMapper.class, + } ) + void subclassAbstractParentFixture() { + generatedSource.addComparisonToFixtureFor( SubclassAbstractMapper.class ); + } + + @ProcessorTest + @WithClasses( { + SubclassImplementedMapper.class, + } ) + void subclassImplementationParentFixture() { + generatedSource.addComparisonToFixtureFor( SubclassImplementedMapper.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapper.java new file mode 100644 index 0000000000..0ebb2a26ff --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapper.java @@ -0,0 +1,17 @@ +/* + * 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.subclassmapping.fixture; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassMapping; + +@Mapper +public interface SubclassImplementedMapper { + + @SubclassMapping( source = SubSource.class, target = SubTarget.class ) + @SubclassMapping( source = SubSourceOther.class, target = SubTargetOther.class ) + ImplementedParentTarget map(ImplementedParentSource item); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapper.java new file mode 100644 index 0000000000..ac7acaafdb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapper.java @@ -0,0 +1,19 @@ +/* + * 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.subclassmapping.fixture; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassMapping; + +import static org.mapstruct.SubclassExhaustiveStrategy.RUNTIME_EXCEPTION; + +@Mapper( subclassExhaustiveStrategy = RUNTIME_EXCEPTION ) +public interface SubclassInterfaceMapper { + + @SubclassMapping( source = SubSource.class, target = SubTarget.class ) + @SubclassMapping( source = SubSourceOther.class, target = SubTargetOther.class ) + InterfaceParentTarget map(InterfaceParentSource item); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Bike.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Bike.java new file mode 100644 index 0000000000..b414f733c4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Bike.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.mappables; + +public class Bike extends Vehicle { + private int numberOfGears; + + public int getNumberOfGears() { + return numberOfGears; + } + + public void setNumberOfGears(int numberOfGears) { + this.numberOfGears = numberOfGears; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/BikeDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/BikeDto.java new file mode 100644 index 0000000000..0eefc8f0c9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/BikeDto.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.mappables; + +public class BikeDto extends VehicleDto { + private int numberOfGears; + + public int getNumberOfGears() { + return numberOfGears; + } + + public void setNumberOfGears(int numberOfGears) { + this.numberOfGears = numberOfGears; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Car.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Car.java new file mode 100644 index 0000000000..71b2446d34 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Car.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.mappables; + +public class Car extends Vehicle { + private boolean manual; + + public boolean isManual() { + return manual; + } + + public void setManual(boolean manual) { + this.manual = manual; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/CarDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/CarDto.java new file mode 100644 index 0000000000..bfaba22685 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/CarDto.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.mappables; + +public class CarDto extends VehicleDto { + private boolean manual; + + public boolean isManual() { + return manual; + } + + public void setManual(boolean manual) { + this.manual = manual; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/HatchBack.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/HatchBack.java new file mode 100644 index 0000000000..d03a57aa30 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/HatchBack.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.mappables; + +public class HatchBack extends Car { + private String doorType; + + public String getDoorType() { + return doorType; + } + + public void setDoorType(String doorType) { + this.doorType = doorType; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/HatchBackDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/HatchBackDto.java new file mode 100644 index 0000000000..6ec4d90f1b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/HatchBackDto.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.mappables; + +public class HatchBackDto extends CarDto { + private String doorType; + + public String getDoorType() { + return doorType; + } + + public void setDoorType(String doorType) { + this.doorType = doorType; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Source.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Source.java new file mode 100644 index 0000000000..52f07943ee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Source.java @@ -0,0 +1,42 @@ +/* + * 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.subclassmapping.mappables; + +public class Source { + private String property1; + private String property2; + private String property3; + + public Source(String prop1, String prop2, String prop3) { + setProperty1( prop1 ); + setProperty2( prop2 ); + setProperty3( prop3 ); + } + + public String getProperty1() { + return property1; + } + + public String getProperty2() { + return property2; + } + + public String getProperty3() { + return property3; + } + + public void setProperty1(String property1) { + this.property1 = property1; + } + + public void setProperty2(String property2) { + this.property2 = property2; + } + + public void setProperty3(String property3) { + this.property3 = property3; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/SourceSubclass.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/SourceSubclass.java new file mode 100644 index 0000000000..600d4f383e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/SourceSubclass.java @@ -0,0 +1,33 @@ +/* + * 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.subclassmapping.mappables; + +public class SourceSubclass extends Source { + private String subclassProperty; + private String target5; + + public SourceSubclass(String prop1, String prop2, String prop3, String subProp, String target5) { + super( prop1, prop2, prop3 ); + setSubclassProperty( subProp ); + setTarget5( target5 ); + } + + public String getSubclassProperty() { + return subclassProperty; + } + + public String getTarget5() { + return target5; + } + + public void setSubclassProperty(String subclassProperty) { + this.subclassProperty = subclassProperty; + } + + public void setTarget5(String target5) { + this.target5 = target5; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Target.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Target.java new file mode 100644 index 0000000000..cb5e3d6bc3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Target.java @@ -0,0 +1,54 @@ +/* + * 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.subclassmapping.mappables; + +public class Target { + private String target1; + private String target2; + private String target3; + private String target4; + private String target5; + + public String getTarget1() { + return target1; + } + + public String getTarget2() { + return target2; + } + + public String getTarget3() { + return target3; + } + + public String getTarget4() { + return target4; + } + + public String getTarget5() { + return target5; + } + + public void setTarget1(String target1) { + this.target1 = target1; + } + + public void setTarget2(String target2) { + this.target2 = target2; + } + + public void setTarget3(String target3) { + this.target3 = target3; + } + + public void setTarget4(String target4) { + this.target4 = target4; + } + + public void setTarget5(String target5) { + this.target5 = target5; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Vehicle.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Vehicle.java new file mode 100644 index 0000000000..9c3406fe53 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Vehicle.java @@ -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.subclassmapping.mappables; + +public class Vehicle { + private String name; + private String vehicleManufacturingCompany; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVehicleManufacturingCompany() { + return vehicleManufacturingCompany; + } + + public void setVehicleManufacturingCompany(String vehicleManufacturingCompany) { + this.vehicleManufacturingCompany = vehicleManufacturingCompany; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleCollection.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleCollection.java new file mode 100644 index 0000000000..af4efbaef4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleCollection.java @@ -0,0 +1,17 @@ +/* + * 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.subclassmapping.mappables; + +import java.util.ArrayList; +import java.util.Collection; + +public class VehicleCollection { + private Collection vehicles = new ArrayList<>(); + + public Collection getVehicles() { + return vehicles; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleCollectionDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleCollectionDto.java new file mode 100644 index 0000000000..9f06ee4b72 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleCollectionDto.java @@ -0,0 +1,17 @@ +/* + * 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.subclassmapping.mappables; + +import java.util.ArrayList; +import java.util.Collection; + +public class VehicleCollectionDto { + private Collection vehicles = new ArrayList<>(); + + public Collection getVehicles() { + return vehicles; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleDto.java new file mode 100644 index 0000000000..fb6b40e93b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/VehicleDto.java @@ -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.subclassmapping.mappables; + +public class VehicleDto { + private String name; + private String maker; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMaker() { + return maker; + } + + public void setMaker(String maker) { + this.maker = maker; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java new file mode 100644 index 0000000000..6deed338c1 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.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.subclassmapping.fixture; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-09-12T14:37:10+0200", + comments = "version: , compiler: Eclipse JDT (Batch) 3.20.0.v20191203-2131, environment: Java 11.0.12 (Azul Systems, Inc.)" +) +public class SubclassAbstractMapperImpl implements SubclassAbstractMapper { + + @Override + public AbstractParentTarget map(AbstractParentSource item) { + if ( item == null ) { + return null; + } + + if (item instanceof SubSource) { + return subSourceToSubTarget( (SubSource) item ); + } + else if (item instanceof SubSourceOther) { + return subSourceOtherToSubTargetOther( (SubSourceOther) item ); + } + else { + throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for " + item.getClass()); + } + } + + protected SubTarget subSourceToSubTarget(SubSource subSource) { + if ( subSource == null ) { + return null; + } + + SubTarget subTarget = new SubTarget(); + + subTarget.setValue( subSource.getValue() ); + + return subTarget; + } + + protected SubTargetOther subSourceOtherToSubTargetOther(SubSourceOther subSourceOther) { + if ( subSourceOther == null ) { + return null; + } + + String finalValue = null; + + finalValue = subSourceOther.getFinalValue(); + + SubTargetOther subTargetOther = new SubTargetOther( finalValue ); + + return subTargetOther; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapperImpl.java new file mode 100644 index 0000000000..444b012078 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapperImpl.java @@ -0,0 +1,61 @@ +/* + * 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.subclassmapping.fixture; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-09-12T14:37:10+0200", + comments = "version: , compiler: Eclipse JDT (Batch) 3.20.0.v20191203-2131, environment: Java 11.0.12 (Azul Systems, Inc.)" +) +public class SubclassImplementedMapperImpl implements SubclassImplementedMapper { + + @Override + public ImplementedParentTarget map(ImplementedParentSource item) { + if ( item == null ) { + return null; + } + + if (item instanceof SubSource) { + return subSourceToSubTarget( (SubSource) item ); + } + else if (item instanceof SubSourceOther) { + return subSourceOtherToSubTargetOther( (SubSourceOther) item ); + } + else { + ImplementedParentTarget implementedParentTarget = new ImplementedParentTarget(); + + return implementedParentTarget; + } + } + + protected SubTarget subSourceToSubTarget(SubSource subSource) { + if ( subSource == null ) { + return null; + } + + SubTarget subTarget = new SubTarget(); + + subTarget.setValue( subSource.getValue() ); + + return subTarget; + } + + protected SubTargetOther subSourceOtherToSubTargetOther(SubSourceOther subSourceOther) { + if ( subSourceOther == null ) { + return null; + } + + String finalValue = null; + + finalValue = subSourceOther.getFinalValue(); + + SubTargetOther subTargetOther = new SubTargetOther( finalValue ); + + return subTargetOther; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapperImpl.java new file mode 100644 index 0000000000..8366d53ea9 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapperImpl.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.subclassmapping.fixture; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-09-12T14:37:10+0200", + comments = "version: , compiler: Eclipse JDT (Batch) 3.20.0.v20191203-2131, environment: Java 11.0.12 (Azul Systems, Inc.)" +) +public class SubclassInterfaceMapperImpl implements SubclassInterfaceMapper { + + @Override + public InterfaceParentTarget map(InterfaceParentSource item) { + if ( item == null ) { + return null; + } + + if (item instanceof SubSource) { + return subSourceToSubTarget( (SubSource) item ); + } + else if (item instanceof SubSourceOther) { + return subSourceOtherToSubTargetOther( (SubSourceOther) item ); + } + else { + throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for " + item.getClass()); + } + } + + protected SubTarget subSourceToSubTarget(SubSource subSource) { + if ( subSource == null ) { + return null; + } + + SubTarget subTarget = new SubTarget(); + + subTarget.setValue( subSource.getValue() ); + + return subTarget; + } + + protected SubTargetOther subSourceOtherToSubTargetOther(SubSourceOther subSourceOther) { + if ( subSourceOther == null ) { + return null; + } + + String finalValue = null; + + finalValue = subSourceOther.getFinalValue(); + + SubTargetOther subTargetOther = new SubTargetOther( finalValue ); + + return subTargetOther; + } +} From e86c0faf04109a99ac4049ce9c510653ce973f11 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 16 Oct 2021 19:10:40 +0200 Subject: [PATCH 204/597] #2611 Scope org.eclipse.tycho:tycho-compiler-jdt under test Since the removal of the Eclipse Specific compiler workarounds in c2e803403027f3fae92bd15b0ba50ab7df5063e6 the org.eclipse.tycho:tycho-compiler-jdt dependency is no longer needed in the compile code, we only need it for tests --- processor/pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/processor/pom.xml b/processor/pom.xml index fcc6dbbab8..4f2476ceb0 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -44,8 +44,7 @@ org.eclipse.tycho tycho-compiler-jdt - provided - true + test From 935c03e822d0df436f374349d4272d3e62fe9874 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 16 Oct 2021 22:00:30 +0200 Subject: [PATCH 205/597] #2614 Do not use FQN when mapping Stream to Array --- .../java/org/mapstruct/ap/internal/model/common/Type.java | 7 +++++-- .../mapstruct/ap/internal/model/StreamMappingMethod.ftl | 2 +- .../org/mapstruct/ap/test/bugs/_1707/ConverterImpl.java | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index e13d7b9ddd..caa7a9c626 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -444,8 +444,11 @@ public boolean isToBeImported() { } private boolean shouldUseSimpleName() { - String fqn = notToBeImportedTypes.get( name ); - return this.qualifiedName.equals( fqn ); + // Using trimSimpleClassName since the same is used in the isToBeImported() + // to check whether notToBeImportedTypes contains it + String trimmedName = trimSimpleClassName( name ); + String fqn = notToBeImportedTypes.get( trimmedName ); + return trimSimpleClassName( this.qualifiedName ).equals( fqn ); } public Type erasure() { diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl index f9a7309b5d..ae48a3477a 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl @@ -55,7 +55,7 @@ <#if needVarDefine> <#assign needVarDefine = false /> <#-- We create a null array which later will be directly assigned from the stream--> - ${resultElementType}[] ${resultName} = null; + <@includeModel object=resultElementType/>[] ${resultName} = null; <#elseif resultType.iterableType> <#if existingInstanceMapping> diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1707/ConverterImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1707/ConverterImpl.java index abe4a62a3d..b822f3a61d 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1707/ConverterImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1707/ConverterImpl.java @@ -36,12 +36,12 @@ public Set convert(Stream source) { } @Override - public org.mapstruct.ap.test.bugs._1707.Converter.Target[] convertArray(Stream source) { + public Target[] convertArray(Stream source) { if ( source == null ) { return null; } - org.mapstruct.ap.test.bugs._1707.Converter.Target[] targetTmp = null; + Target[] targetTmp = null; targetTmp = source.map( source1 -> convert( source1 ) ) .toArray( Target[]::new ); From 564455ee452b0b7b3862be969b69d501f5ea118b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 16 Oct 2021 18:50:04 +0200 Subject: [PATCH 206/597] #2596 Record components should have the highest priority for read accessors --- .../mapstruct/itest/records/MemberDto.java | 13 ++++++++ .../mapstruct/itest/records/MemberEntity.java | 31 +++++++++++++++++++ .../mapstruct/itest/records/MemberMapper.java | 24 ++++++++++++++ .../mapstruct/itest/records/RecordsTest.java | 22 +++++++++++++ .../ap/internal/model/common/Type.java | 28 ++++++++++++----- 5 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberDto.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberEntity.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberMapper.java diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberDto.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberDto.java new file mode 100644 index 0000000000..bf3ce6cd94 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberDto.java @@ -0,0 +1,13 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +/** + * @author Filip Hrisafov + */ +public record MemberDto(Boolean isActive, Boolean premium) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberEntity.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberEntity.java new file mode 100644 index 0000000000..50a7b1ce00 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberEntity.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +/** + * @author Filip Hrisafov + */ +public class MemberEntity { + + private Boolean isActive; + private Boolean premium; + + public Boolean getIsActive() { + return isActive; + } + + public void setIsActive(Boolean active) { + isActive = active; + } + + public Boolean getPremium() { + return premium; + } + + public void setPremium(Boolean premium) { + this.premium = premium; + } +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberMapper.java new file mode 100644 index 0000000000..3460aeed69 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberMapper.java @@ -0,0 +1,24 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface MemberMapper { + + MemberMapper INSTANCE = Mappers.getMapper( MemberMapper.class ); + + MemberEntity fromRecord(MemberDto record); + + MemberDto toRecord(MemberEntity entity); + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java index e3d055345e..e98df8797e 100644 --- a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java @@ -61,4 +61,26 @@ public void shouldMapIntoRecordWithList() { assertThat( carDto.wheelPositions() ) .containsExactly( "left" ); } + + @Test + public void shouldMapMemberRecord() { + MemberEntity member = MemberMapper.INSTANCE.fromRecord( new MemberDto( true, false ) ); + + assertThat( member ).isNotNull(); + assertThat( member.getIsActive() ).isTrue(); + assertThat( member.getPremium() ).isFalse(); + } + + @Test + public void shouldMapIntoMemberRecord() { + MemberEntity entity = new MemberEntity(); + entity.setIsActive( false ); + entity.setPremium( true ); + + MemberDto value = MemberMapper.INSTANCE.toRecord( entity ); + + assertThat( value ).isNotNull(); + assertThat( value.isActive() ).isEqualTo( false ); + assertThat( value.premium() ).isEqualTo( true ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index caa7a9c626..bba33a2764 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -576,14 +576,33 @@ public Type asRawType() { */ public Map getPropertyReadAccessors() { if ( readAccessors == null ) { - List getterList = filters.getterMethodsIn( getAllMethods() ); Map modifiableGetters = new LinkedHashMap<>(); + + Map recordAccessors = filters.recordAccessorsIn( getRecordComponents() ); + modifiableGetters.putAll( recordAccessors ); + + List getterList = filters.getterMethodsIn( getAllMethods() ); for ( Accessor getter : getterList ) { + String simpleName = getter.getSimpleName(); + if ( recordAccessors.containsKey( simpleName ) ) { + // If there is already a record accessor that contains the simple name + // then it means that the getter is actually a record component. + // In that case we need to ignore it. + // e.g. record component named isActive. + // The DefaultAccessorNamingStrategy will return active as property name, + // but the property name is isActive, since it is a record + continue; + } String propertyName = getPropertyName( getter ); + + if ( recordAccessors.containsKey( propertyName ) ) { + // If there is already a record accessor, the property needs to be ignored + continue; + } if ( modifiableGetters.containsKey( propertyName ) ) { // In the DefaultAccessorNamingStrategy, this can only be the case for Booleans: isFoo() and // getFoo(); The latter is preferred. - if ( !getter.getSimpleName().startsWith( "is" ) ) { + if ( !simpleName.startsWith( "is" ) ) { modifiableGetters.put( propertyName, getter ); } @@ -593,11 +612,6 @@ public Map getPropertyReadAccessors() { } } - Map recordAccessors = filters.recordAccessorsIn( getRecordComponents() ); - for ( Map.Entry recordEntry : recordAccessors.entrySet() ) { - modifiableGetters.putIfAbsent( recordEntry.getKey(), recordEntry.getValue() ); - } - List fieldsList = filters.fieldsIn( getAllFields() ); for ( Accessor field : fieldsList ) { String propertyName = getPropertyName( field ); From 80d26a1a9c0ce2a7c5312d9ed498ade8d5cdedc3 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 25 Oct 2021 08:22:26 +0200 Subject: [PATCH 207/597] #148, #1386, #2593 Only import top level classes Instead of importing all classes, inner classes will be used through their top level classes only. This also fixes the problem in #2593 since inner classes are no longer imported but used through their top classes --- .../itest/tests/MavenIntegrationTest.java | 5 + .../main/java/DefaultPackageObject.java | 97 +++++++++++++++++++ .../src/test/resources/defaultPackage/pom.xml | 21 ++++ .../test/java/DefaultPackageTest.java | 31 ++++++ .../ap/internal/model/common/Type.java | 69 ++++++++++++- .../test/imports/InnerClassesImportsTest.java | 9 +- .../imports/nested/NestedImportsTest.java | 54 +++++++++++ .../NestedSourceInOtherPackageMapper.java | 18 ++++ .../ap/test/imports/nested/Source.java | 48 +++++++++ .../nested/SourceInOtherPackageMapper.java | 18 ++++ .../ap/test/imports/nested/Target.java | 48 +++++++++ .../nested/TargetInOtherPackageMapper.java | 18 ++++ .../nested/other/SourceInOtherPackage.java | 48 +++++++++ .../nested/other/TargetInOtherPackage.java | 48 +++++++++ .../BeanWithInnerEnumMapperImpl.java | 48 +++++++++ .../innerclasses/InnerClassMapperImpl.java | 55 +++++++++++ .../NestedSourceInOtherPackageMapperImpl.java | 42 ++++++++ .../SourceInOtherPackageMapperImpl.java | 54 +++++++++++ .../TargetInOtherPackageMapperImpl.java | 54 +++++++++++ 19 files changed, 776 insertions(+), 9 deletions(-) create mode 100644 integrationtest/src/test/resources/defaultPackage/main/java/DefaultPackageObject.java create mode 100644 integrationtest/src/test/resources/defaultPackage/pom.xml create mode 100644 integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/nested/NestedImportsTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/nested/NestedSourceInOtherPackageMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/nested/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/nested/SourceInOtherPackageMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/nested/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/nested/TargetInOtherPackageMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/nested/other/SourceInOtherPackage.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/nested/other/TargetInOtherPackage.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnumMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/innerclasses/InnerClassMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/NestedSourceInOtherPackageMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/SourceInOtherPackageMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/TargetInOtherPackageMapperImpl.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 2ec0ba9aa9..59aa48b053 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -128,6 +128,11 @@ void kotlinDataTest() { void simpleTest() { } + // for issue #2593 + @ProcessorTest(baseDir = "defaultPackage") + void defaultPackageTest() { + } + @ProcessorTest(baseDir = "springTest") void springTest() { } diff --git a/integrationtest/src/test/resources/defaultPackage/main/java/DefaultPackageObject.java b/integrationtest/src/test/resources/defaultPackage/main/java/DefaultPackageObject.java new file mode 100644 index 0000000000..ba684850b4 --- /dev/null +++ b/integrationtest/src/test/resources/defaultPackage/main/java/DefaultPackageObject.java @@ -0,0 +1,97 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +public class DefaultPackageObject { + public enum CarType { + SEDAN, CAMPER, X4, TRUCK; + } + + static public class Car { + + private String make; + private int numberOfSeats; + private CarType type; + + public Car(String string, int numberOfSeats, CarType sedan) { + this.make = string; + this.numberOfSeats = numberOfSeats; + this.type = sedan; + } + + + public String getMake() { + return make; + } + + public void setMake(String make) { + this.make = make; + } + + public int getNumberOfSeats() { + return numberOfSeats; + } + + public void setNumberOfSeats(int numberOfSeats) { + this.numberOfSeats = numberOfSeats; + } + + public CarType getType() { + return type; + } + + public void setType(CarType type) { + this.type = type; + } + } + + static public class CarDto { + + private String make; + private int seatCount; + private String type; + + public String getMake() { + return make; + } + + public void setMake(String make) { + this.make = make; + } + + public int getSeatCount() { + return seatCount; + } + + public void setSeatCount(int seatCount) { + this.seatCount = seatCount; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + } + + + @Mapper + public interface CarMapper { + + CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); + + @Mapping(source = "numberOfSeats", target = "seatCount") + CarDto carToCarDto(Car car); + } +} diff --git a/integrationtest/src/test/resources/defaultPackage/pom.xml b/integrationtest/src/test/resources/defaultPackage/pom.xml new file mode 100644 index 0000000000..d72300f618 --- /dev/null +++ b/integrationtest/src/test/resources/defaultPackage/pom.xml @@ -0,0 +1,21 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + defaultPackage + jar + diff --git a/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java b/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java new file mode 100644 index 0000000000..ddc834cc8e --- /dev/null +++ b/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ + +public class DefaultPackageTest { + + @Test + public void shouldWorkCorrectlyInDefaultPackage() { + DefaultPackageObject.CarDto carDto = DefaultPackageObject.CarMapper.INSTANCE.carToCarDto( + new DefaultPackageObject.Car( + "Morris", + 5, + DefaultPackageObject.CarType.SEDAN + ) ); + + assertThat( carDto ).isNotNull(); + assertThat( carDto.getMake() ).isEqualTo( "Morris" ); + assertThat( carDto.getSeatCount() ).isEqualTo( 5 ); + assertThat( carDto.getType() ).isEqualTo( "SEDAN" ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index bba33a2764..00bcae371c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -5,9 +5,11 @@ */ package org.mapstruct.ap.internal.model.common; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -22,6 +24,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; @@ -71,9 +74,11 @@ public class Type extends ModelElement implements Comparable { private final ImplementationType implementationType; private final Type componentType; + private final Type topLevelType; private final String packageName; private final String name; + private final String nameWithTopLevelTypeName; private final String qualifiedName; private final boolean isInterface; @@ -172,6 +177,9 @@ public Type(TypeUtils typeUtils, ElementUtils elementUtils, TypeFactory typeFact this.filters = new Filters( accessorNaming, typeUtils, typeMirror ); this.loggingVerbose = loggingVerbose; + + this.topLevelType = topLevelType( this.typeElement, this.typeFactory ); + this.nameWithTopLevelTypeName = nameWithTopLevelTypeName( this.typeElement ); } //CHECKSTYLE:ON @@ -199,11 +207,23 @@ public String getName() { *

      * If the {@code java.time} variant is referred to first, the {@code java.time.LocalDateTime} will be imported * and the {@code org.joda} variant will be referred to with its FQN. + *

      + * If the type is nested and its top level type is to be imported + * then the name including its top level type will be returned. * - * @return Just the name if this {@link Type} will be imported, otherwise the fully-qualified name. + * @return Just the name if this {@link Type} will be imported, the name up to the top level {@link Type} + * (if the top level type is important, otherwise the fully-qualified name. */ public String createReferenceName() { - return isToBeImported() ? name : ( shouldUseSimpleName() ? name : qualifiedName ); + if ( isToBeImported() || shouldUseSimpleName() ) { + return name; + } + + if ( isTopLevelTypeToBeImported() && nameWithTopLevelTypeName != null ) { + return nameWithTopLevelTypeName; + } + + return qualifiedName; } public List getTypeParameters() { @@ -402,6 +422,10 @@ public Set getImportTypes() { result.addAll( componentType.getImportTypes() ); } + if ( topLevelType != null ) { + result.addAll( topLevelType.getImportTypes() ); + } + for ( Type parameter : typeParameters ) { result.addAll( parameter.getImportTypes() ); } @@ -413,6 +437,10 @@ public Set getImportTypes() { return result; } + protected boolean isTopLevelTypeToBeImported() { + return topLevelType != null && topLevelType.isToBeImported(); + } + /** * Whether this type is to be imported by means of an import statement in the currently generated source file * (it can be referenced in the generated source using its simple name) or not (referenced using the FQN). @@ -435,7 +463,7 @@ public boolean isToBeImported() { isToBeImported = true; } } - else { + else if ( typeElement == null || !typeElement.getNestingKind().isNested() ) { toBeImportedTypes.put( trimmedName, trimmedQualifiedName ); isToBeImported = true; } @@ -1492,4 +1520,39 @@ private String trimSimpleClassName(String className) { return trimmedClassName; } + private static String nameWithTopLevelTypeName(TypeElement element) { + if ( element == null ) { + return null; + } + if ( !element.getNestingKind().isNested() ) { + return element.getSimpleName().toString(); + } + + Deque elements = new ArrayDeque<>(); + elements.addFirst( element.getSimpleName() ); + Element parent = element.getEnclosingElement(); + while ( parent != null && parent.getKind() != ElementKind.PACKAGE ) { + elements.addFirst( parent.getSimpleName() ); + parent = parent.getEnclosingElement(); + } + + return String.join( ".", elements ); + } + + private static Type topLevelType(TypeElement typeElement, TypeFactory typeFactory) { + if ( typeElement == null || typeElement.getNestingKind() == NestingKind.TOP_LEVEL ) { + return null; + } + + Element parent = typeElement.getEnclosingElement(); + while ( parent != null ) { + if ( parent.getEnclosingElement() != null && + parent.getEnclosingElement().getKind() == ElementKind.PACKAGE ) { + break; + } + parent = parent.getEnclosingElement(); + } + return parent == null ? null : typeFactory.getType( parent.asType() ); + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java b/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java index 91e5df6a27..abad5e4177 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java @@ -14,7 +14,6 @@ import org.mapstruct.ap.test.imports.innerclasses.SourceWithInnerClass; import org.mapstruct.ap.test.imports.innerclasses.SourceWithInnerClass.SourceInnerClass; import org.mapstruct.ap.test.imports.innerclasses.TargetWithInnerClass; -import org.mapstruct.ap.test.imports.innerclasses.TargetWithInnerClass.TargetInnerClass; import org.mapstruct.ap.test.imports.innerclasses.TargetWithInnerClass.TargetInnerClass.TargetInnerInnerClass; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; @@ -47,8 +46,7 @@ public void mapperRequiresInnerClassImports() { assertThat( target ).isNotNull(); assertThat( target.getInnerClassMember().getValue() ).isEqualTo( 412 ); - generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( SourceInnerClass.class ); - generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( TargetInnerClass.class ); + generatedSource.addComparisonToFixtureFor( InnerClassMapper.class ); } @ProcessorTest @@ -61,8 +59,7 @@ public void mapperRequiresInnerInnerClassImports() { assertThat( target ).isNotNull(); assertThat( target.getValue() ).isEqualTo( 412 ); - generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( SourceInnerClass.class ); - generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( TargetInnerInnerClass.class ); + generatedSource.addComparisonToFixtureFor( InnerClassMapper.class ); } @ProcessorTest @@ -82,6 +79,6 @@ public void mapperRequiresInnerEnumImports() { assertThat( sourceAgain ).isNotNull(); assertThat( sourceAgain.getInnerEnum() ).isEqualTo( InnerEnum.A ); - generatedSource.forMapper( BeanWithInnerEnumMapper.class ).containsImportFor( InnerEnum.class ); + generatedSource.addComparisonToFixtureFor( BeanWithInnerEnumMapper.class ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/nested/NestedImportsTest.java b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/NestedImportsTest.java new file mode 100644 index 0000000000..37c522e4cd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/NestedImportsTest.java @@ -0,0 +1,54 @@ +/* + * 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.imports.nested; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage; +import org.mapstruct.ap.test.imports.nested.other.TargetInOtherPackage; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + SourceInOtherPackage.class, + TargetInOtherPackage.class, + Source.class, + Target.class +}) +@IssueKey("1386,148") +class NestedImportsTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses( { + SourceInOtherPackageMapper.class + } ) + void shouldGenerateNestedInnerClassesForSourceInOtherPackage() { + generatedSource.addComparisonToFixtureFor( SourceInOtherPackageMapper.class ); + } + + @ProcessorTest + @WithClasses( { + NestedSourceInOtherPackageMapper.class + } ) + void shouldGenerateCorrectImportsForTopLevelClassesFromOnlyNestedInnerClasses() { + generatedSource.addComparisonToFixtureFor( NestedSourceInOtherPackageMapper.class ); + } + + @ProcessorTest + @WithClasses( { + TargetInOtherPackageMapper.class + } ) + void shouldGenerateNestedInnerClassesForTargetInOtherPackage() { + generatedSource.addComparisonToFixtureFor( TargetInOtherPackageMapper.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/nested/NestedSourceInOtherPackageMapper.java b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/NestedSourceInOtherPackageMapper.java new file mode 100644 index 0000000000..7f98737d1d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/NestedSourceInOtherPackageMapper.java @@ -0,0 +1,18 @@ +/* + * 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.imports.nested; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface NestedSourceInOtherPackageMapper { + + Target.Nested map(SourceInOtherPackage.Nested source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/nested/Source.java b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/Source.java new file mode 100644 index 0000000000..2d2e9aa215 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/Source.java @@ -0,0 +1,48 @@ +/* + * 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.imports.nested; + +/** + * @author Filip Hrisafov + */ +public class Source { + + private Nested value; + + public Nested getValue() { + return value; + } + + public void setValue(Nested value) { + this.value = value; + } + + public static class Nested { + + private Inner inner; + + public static class Inner { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public Inner getInner() { + return inner; + } + + public void setInner(Inner inner) { + this.inner = inner; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/nested/SourceInOtherPackageMapper.java b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/SourceInOtherPackageMapper.java new file mode 100644 index 0000000000..7150823c1a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/SourceInOtherPackageMapper.java @@ -0,0 +1,18 @@ +/* + * 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.imports.nested; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SourceInOtherPackageMapper { + + Target map(SourceInOtherPackage source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/nested/Target.java b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/Target.java new file mode 100644 index 0000000000..78a50fcb15 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/Target.java @@ -0,0 +1,48 @@ +/* + * 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.imports.nested; + +/** + * @author Filip Hrisafov + */ +public class Target { + + private Nested value; + + public Nested getValue() { + return value; + } + + public void setValue(Nested value) { + this.value = value; + } + + public static class Nested { + + private Inner inner; + + public static class Inner { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public Inner getInner() { + return inner; + } + + public void setInner(Inner inner) { + this.inner = inner; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/nested/TargetInOtherPackageMapper.java b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/TargetInOtherPackageMapper.java new file mode 100644 index 0000000000..a40d52eb1c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/TargetInOtherPackageMapper.java @@ -0,0 +1,18 @@ +/* + * 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.imports.nested; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.imports.nested.other.TargetInOtherPackage; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface TargetInOtherPackageMapper { + + TargetInOtherPackage map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/nested/other/SourceInOtherPackage.java b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/other/SourceInOtherPackage.java new file mode 100644 index 0000000000..f6646c7160 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/other/SourceInOtherPackage.java @@ -0,0 +1,48 @@ +/* + * 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.imports.nested.other; + +/** + * @author Filip Hrisafov + */ +public class SourceInOtherPackage { + + private Nested value; + + public Nested getValue() { + return value; + } + + public void setValue(Nested value) { + this.value = value; + } + + public static class Nested { + + private Inner inner; + + public static class Inner { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public Inner getInner() { + return inner; + } + + public void setInner(Inner inner) { + this.inner = inner; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/nested/other/TargetInOtherPackage.java b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/other/TargetInOtherPackage.java new file mode 100644 index 0000000000..da6b596c06 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/nested/other/TargetInOtherPackage.java @@ -0,0 +1,48 @@ +/* + * 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.imports.nested.other; + +/** + * @author Filip Hrisafov + */ +public class TargetInOtherPackage { + + private Nested value; + + public Nested getValue() { + return value; + } + + public void setValue(Nested value) { + this.value = value; + } + + public static class Nested { + + private Inner inner; + + public static class Inner { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public Inner getInner() { + return inner; + } + + public void setInner(Inner inner) { + this.inner = inner; + } + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnumMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnumMapperImpl.java new file mode 100644 index 0000000000..1428fabd8f --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnumMapperImpl.java @@ -0,0 +1,48 @@ +/* + * 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.imports.innerclasses; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-10-16T21:06:53+0200", + comments = "version: , compiler: javac, environment: Java 17 (Oracle Corporation)" +) +public class BeanWithInnerEnumMapperImpl implements BeanWithInnerEnumMapper { + + @Override + public BeanWithInnerEnum fromFacade(BeanFacade beanFacade) { + if ( beanFacade == null ) { + return null; + } + + BeanWithInnerEnum beanWithInnerEnum = new BeanWithInnerEnum(); + + beanWithInnerEnum.setTest( beanFacade.getTest() ); + if ( beanFacade.getInnerEnum() != null ) { + beanWithInnerEnum.setInnerEnum( Enum.valueOf( BeanWithInnerEnum.InnerEnum.class, beanFacade.getInnerEnum() ) ); + } + + return beanWithInnerEnum; + } + + @Override + public BeanFacade toFacade(BeanWithInnerEnum beanWithInnerEnum) { + if ( beanWithInnerEnum == null ) { + return null; + } + + BeanFacade beanFacade = new BeanFacade(); + + beanFacade.setTest( beanWithInnerEnum.getTest() ); + if ( beanWithInnerEnum.getInnerEnum() != null ) { + beanFacade.setInnerEnum( beanWithInnerEnum.getInnerEnum().name() ); + } + + return beanFacade; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/innerclasses/InnerClassMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/innerclasses/InnerClassMapperImpl.java new file mode 100644 index 0000000000..bd85a7abee --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/innerclasses/InnerClassMapperImpl.java @@ -0,0 +1,55 @@ +/* + * 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.imports.innerclasses; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-10-16T21:06:53+0200", + comments = "version: , compiler: javac, environment: Java 17 (Oracle Corporation)" +) +public class InnerClassMapperImpl implements InnerClassMapper { + + @Override + public TargetWithInnerClass sourceToTarget(SourceWithInnerClass source) { + if ( source == null ) { + return null; + } + + TargetWithInnerClass targetWithInnerClass = new TargetWithInnerClass(); + + targetWithInnerClass.setInnerClassMember( innerSourceToInnerTarget( source.getInnerClassMember() ) ); + + return targetWithInnerClass; + } + + @Override + public TargetWithInnerClass.TargetInnerClass innerSourceToInnerTarget(SourceWithInnerClass.SourceInnerClass source) { + if ( source == null ) { + return null; + } + + TargetWithInnerClass.TargetInnerClass targetInnerClass = new TargetWithInnerClass.TargetInnerClass(); + + targetInnerClass.setValue( source.getValue() ); + + return targetInnerClass; + } + + @Override + public TargetWithInnerClass.TargetInnerClass.TargetInnerInnerClass innerSourceToInnerInnerTarget(SourceWithInnerClass.SourceInnerClass source) { + if ( source == null ) { + return null; + } + + TargetWithInnerClass.TargetInnerClass.TargetInnerInnerClass targetInnerInnerClass = new TargetWithInnerClass.TargetInnerClass.TargetInnerInnerClass(); + + targetInnerInnerClass.setValue( source.getValue() ); + + return targetInnerInnerClass; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/NestedSourceInOtherPackageMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/NestedSourceInOtherPackageMapperImpl.java new file mode 100644 index 0000000000..53dd91b9e2 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/NestedSourceInOtherPackageMapperImpl.java @@ -0,0 +1,42 @@ +/* + * 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.imports.nested; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-10-24T19:26:14+0200", + comments = "version: , compiler: javac, environment: Java 1.8.0_161 (Oracle Corporation)" +) +public class NestedSourceInOtherPackageMapperImpl implements NestedSourceInOtherPackageMapper { + + @Override + public Target.Nested map(SourceInOtherPackage.Nested source) { + if ( source == null ) { + return null; + } + + Target.Nested nested = new Target.Nested(); + + nested.setInner( innerToInner( source.getInner() ) ); + + return nested; + } + + protected Target.Nested.Inner innerToInner(SourceInOtherPackage.Nested.Inner inner) { + if ( inner == null ) { + return null; + } + + Target.Nested.Inner inner1 = new Target.Nested.Inner(); + + inner1.setValue( inner.getValue() ); + + return inner1; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/SourceInOtherPackageMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/SourceInOtherPackageMapperImpl.java new file mode 100644 index 0000000000..a167518464 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/SourceInOtherPackageMapperImpl.java @@ -0,0 +1,54 @@ +/* + * 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.imports.nested; + +import javax.annotation.Generated; +import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2018-08-19T19:13:35+0200", + comments = "version: , compiler: javac, environment: Java 1.8.0_161 (Oracle Corporation)" +) +public class SourceInOtherPackageMapperImpl implements SourceInOtherPackageMapper { + + @Override + public Target map(SourceInOtherPackage source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + target.setValue( nestedToNested( source.getValue() ) ); + + return target; + } + + protected Target.Nested.Inner innerToInner(SourceInOtherPackage.Nested.Inner inner) { + if ( inner == null ) { + return null; + } + + Target.Nested.Inner inner1 = new Target.Nested.Inner(); + + inner1.setValue( inner.getValue() ); + + return inner1; + } + + protected Target.Nested nestedToNested(SourceInOtherPackage.Nested nested) { + if ( nested == null ) { + return null; + } + + Target.Nested nested1 = new Target.Nested(); + + nested1.setInner( innerToInner( nested.getInner() ) ); + + return nested1; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/TargetInOtherPackageMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/TargetInOtherPackageMapperImpl.java new file mode 100644 index 0000000000..b1e5d09f40 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/imports/nested/TargetInOtherPackageMapperImpl.java @@ -0,0 +1,54 @@ +/* + * 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.imports.nested; + +import javax.annotation.processing.Generated; +import org.mapstruct.ap.test.imports.nested.other.TargetInOtherPackage; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2021-10-24T13:45:02+0200", + comments = "version: , compiler: javac, environment: Java 1.8.0_161 (Oracle Corporation)" +) +public class TargetInOtherPackageMapperImpl implements TargetInOtherPackageMapper { + + @Override + public TargetInOtherPackage map(Source source) { + if ( source == null ) { + return null; + } + + TargetInOtherPackage targetInOtherPackage = new TargetInOtherPackage(); + + targetInOtherPackage.setValue( nestedToNested( source.getValue() ) ); + + return targetInOtherPackage; + } + + protected TargetInOtherPackage.Nested.Inner innerToInner(Source.Nested.Inner inner) { + if ( inner == null ) { + return null; + } + + TargetInOtherPackage.Nested.Inner inner1 = new TargetInOtherPackage.Nested.Inner(); + + inner1.setValue( inner.getValue() ); + + return inner1; + } + + protected TargetInOtherPackage.Nested nestedToNested(Source.Nested nested) { + if ( nested == null ) { + return null; + } + + TargetInOtherPackage.Nested nested1 = new TargetInOtherPackage.Nested(); + + nested1.setInner( innerToInner( nested.getInner() ) ); + + return nested1; + } +} From e32fc8c283085da5080044c9a41b1dd1e71198ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20P=C3=B6ttker?= Date: Mon, 25 Oct 2021 12:20:11 +0200 Subject: [PATCH 208/597] #2351 NullValueMappingStrategy for Maps and Iterables With two new parameters for Mapper and MapperConfig, it is now possible to override the nullValueMappingStrategy specifically for MapMapping and IterableMapping. --- core/src/main/java/org/mapstruct/Mapper.java | 26 +++ .../main/java/org/mapstruct/MapperConfig.java | 22 +++ .../internal/model/source/DefaultOptions.java | 8 + .../model/source/DelegatingOptions.java | 8 + .../model/source/IterableMappingOptions.java | 8 +- .../model/source/MapMappingOptions.java | 2 +- .../model/source/MapperConfigOptions.java | 22 +++ .../internal/model/source/MapperOptions.java | 22 +++ .../ap/test/nullvaluemapping/CarMapper.java | 15 +- .../CarMapperIterableSettingOnConfig.java | 35 ++++ .../CarMapperIterableSettingOnMapper.java | 40 +++++ .../CarMapperMapSettingOnConfig.java | 35 ++++ .../CarMapperMapSettingOnMapper.java | 40 +++++ .../CarMapperSettingOnConfig.java | 9 +- .../CarMapperSettingOnMapper.java | 9 +- .../CentralIterableMappingConfig.java | 17 ++ .../CentralMapMappingConfig.java | 17 ++ .../NullValueMappingTest.java | 150 +++++++++++++++++- 18 files changed, 453 insertions(+), 32 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperIterableSettingOnConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperIterableSettingOnMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperMapSettingOnConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperMapSettingOnMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CentralIterableMappingConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CentralMapMappingConfig.java diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 9ddd9bff5c..26442e4ff0 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -203,6 +203,32 @@ */ NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL; + /** + * The strategy to be applied when {@code null} is passed as source argument value to an {@link IterableMapping} of + * this mapper. If unset, the strategy set with {@link #nullValueMappingStrategy()} will be applied. If neither + * strategy is configured, the strategy given via {@link MapperConfig#nullValueIterableMappingStrategy()} will be + * applied, using {@link NullValueMappingStrategy#RETURN_NULL} by default. + * + * @since 1.5 + * + * @return The strategy to be applied when {@code null} is passed as source value to an {@link IterableMapping} of + * this mapper. + */ + NullValueMappingStrategy nullValueIterableMappingStrategy() default NullValueMappingStrategy.RETURN_NULL; + + /** + * The strategy to be applied when {@code null} is passed as source argument value to a {@link MapMapping} of this + * mapper. If unset, the strategy set with {@link #nullValueMappingStrategy()} will be applied. If neither strategy + * is configured, the strategy given via {@link MapperConfig#nullValueMapMappingStrategy()} will be applied, using + * {@link NullValueMappingStrategy#RETURN_NULL} by default. + * + * @since 1.5 + * + * @return The strategy to be applied when {@code null} is passed as source value to a {@link MapMapping} of this + * mapper. + */ + NullValueMappingStrategy nullValueMapMappingStrategy() default NullValueMappingStrategy.RETURN_NULL; + /** * The strategy to be applied when a source bean property is {@code null} or not present. If no strategy is * configured, the strategy given via {@link MapperConfig#nullValuePropertyMappingStrategy()} will be applied, diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java index 877495867c..ce239322a2 100644 --- a/core/src/main/java/org/mapstruct/MapperConfig.java +++ b/core/src/main/java/org/mapstruct/MapperConfig.java @@ -177,6 +177,28 @@ */ NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL; + /** + * The strategy to be applied when {@code null} is passed as source argument value to an {@link IterableMapping}. + * If no strategy is configured, the strategy given via {@link #nullValueMappingStrategy()} will be applied, using + * {@link NullValueMappingStrategy#RETURN_NULL} by default. + * + * @since 1.5 + * + * @return The strategy to be applied when {@code null} is passed as source value to an {@link IterableMapping}. + */ + NullValueMappingStrategy nullValueIterableMappingStrategy() default NullValueMappingStrategy.RETURN_NULL; + + /** + * The strategy to be applied when {@code null} is passed as source argument value to a {@link MapMapping}. + * If no strategy is configured, the strategy given via {@link #nullValueMappingStrategy()} will be applied, using + * {@link NullValueMappingStrategy#RETURN_NULL} by default. + * + * @since 1.5 + * + * @return The strategy to be applied when {@code null} is passed as source value to a {@link MapMapping}. + */ + NullValueMappingStrategy nullValueMapMappingStrategy() default NullValueMappingStrategy.RETURN_NULL; + /** * The strategy to be applied when a source bean property is {@code null} or not present. If no strategy is * configured, {@link NullValuePropertyMappingStrategy#SET_TO_NULL} will be used by default. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index 5a7161b4c8..1258707e43 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -124,6 +124,14 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { return SubclassExhaustiveStrategyGem.valueOf( mapper.subclassExhaustiveStrategy().getDefaultValue() ); } + public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { + return NullValueMappingStrategyGem.valueOf( mapper.nullValueIterableMappingStrategy().getDefaultValue() ); + } + + public NullValueMappingStrategyGem getNullValueMapMappingStrategy() { + return NullValueMappingStrategyGem.valueOf( mapper.nullValueMapMappingStrategy().getDefaultValue() ); + } + public BuilderGem getBuilder() { // TODO: I realized this is not correct, however it needs to be null in order to keep downward compatibility // but assuming a default @Builder will make testcases fail. Not having a default means that you need to diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java index b2b36b68a6..12f610f1ac 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -102,6 +102,14 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { return next.getSubclassExhaustiveStrategy(); } + public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { + return next.getNullValueIterableMappingStrategy(); + } + + public NullValueMappingStrategyGem getNullValueMapMappingStrategy() { + return next.getNullValueMapMappingStrategy(); + } + public BuilderGem getBuilder() { return next.getBuilder(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java index 40b5b25353..4affcd93e2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java @@ -30,11 +30,11 @@ public class IterableMappingOptions extends DelegatingOptions { private final IterableMappingGem iterableMapping; public static IterableMappingOptions fromGem(IterableMappingGem iterableMapping, - MapperOptions mappperOptions, ExecutableElement method, + MapperOptions mapperOptions, ExecutableElement method, FormattingMessager messager, TypeUtils typeUtils) { if ( iterableMapping == null || !isConsistent( iterableMapping, method, messager ) ) { - IterableMappingOptions options = new IterableMappingOptions( null, null, null, mappperOptions ); + IterableMappingOptions options = new IterableMappingOptions( null, null, null, mapperOptions ); return options; } @@ -54,7 +54,7 @@ public static IterableMappingOptions fromGem(IterableMappingGem iterableMapping, ); IterableMappingOptions options = - new IterableMappingOptions( formatting, selection, iterableMapping, mappperOptions ); + new IterableMappingOptions( formatting, selection, iterableMapping, mapperOptions ); return options; } @@ -99,7 +99,7 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { .filter( GemValue::hasValue ) .map( GemValue::getValue ) .map( NullValueMappingStrategyGem::valueOf ) - .orElse( next().getNullValueMappingStrategy() ); + .orElse( next().getNullValueIterableMappingStrategy() ); } public MappingControl getElementMappingControl(ElementUtils elementUtils) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java index c35a8e363d..ca8f1ea544 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java @@ -144,7 +144,7 @@ public NullValueMappingStrategyGem getNullValueMappingStrategy() { .filter( GemValue::hasValue ) .map( GemValue::getValue ) .map( NullValueMappingStrategyGem::valueOf ) - .orElse( next().getNullValueMappingStrategy() ); + .orElse( next().getNullValueMapMappingStrategy() ); } public MappingControl getKeyMappingControl(ElementUtils elementUtils) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java index 5288ab1304..623b97bc78 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -134,6 +134,28 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { next().getSubclassExhaustiveStrategy(); } + @Override + public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { + if ( mapperConfig.nullValueIterableMappingStrategy().hasValue() ) { + return NullValueMappingStrategyGem.valueOf( mapperConfig.nullValueIterableMappingStrategy().get() ); + } + if ( mapperConfig.nullValueMappingStrategy().hasValue() ) { + return NullValueMappingStrategyGem.valueOf( mapperConfig.nullValueMappingStrategy().get() ); + } + return next().getNullValueIterableMappingStrategy(); + } + + @Override + public NullValueMappingStrategyGem getNullValueMapMappingStrategy() { + if ( mapperConfig.nullValueMapMappingStrategy().hasValue() ) { + return NullValueMappingStrategyGem.valueOf( mapperConfig.nullValueMapMappingStrategy().get() ); + } + if ( mapperConfig.nullValueMappingStrategy().hasValue() ) { + return NullValueMappingStrategyGem.valueOf( mapperConfig.nullValueMappingStrategy().get() ); + } + return next().getNullValueMapMappingStrategy(); + } + @Override public BuilderGem getBuilder() { return mapperConfig.builder().hasValue() ? mapperConfig.builder().get() : next().getBuilder(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index 86aae34052..3261205116 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -163,6 +163,28 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { next().getSubclassExhaustiveStrategy(); } + @Override + public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { + if ( mapper.nullValueIterableMappingStrategy().hasValue() ) { + return NullValueMappingStrategyGem.valueOf( mapper.nullValueIterableMappingStrategy().get() ); + } + if ( mapper.nullValueMappingStrategy().hasValue() ) { + return NullValueMappingStrategyGem.valueOf( mapper.nullValueMappingStrategy().get() ); + } + return next().getNullValueIterableMappingStrategy(); + } + + @Override + public NullValueMappingStrategyGem getNullValueMapMappingStrategy() { + if ( mapper.nullValueMapMappingStrategy().hasValue() ) { + return NullValueMappingStrategyGem.valueOf( mapper.nullValueMapMappingStrategy().get() ); + } + if ( mapper.nullValueMappingStrategy().hasValue() ) { + return NullValueMappingStrategyGem.valueOf( mapper.nullValueMappingStrategy().get() ); + } + return next().getNullValueMapMappingStrategy(); + } + @Override public BuilderGem getBuilder() { return mapper.builder().hasValue() ? mapper.builder().get() : next().getBuilder(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapper.java index d087d5ef4a..26e3f30e6b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapper.java @@ -16,7 +16,6 @@ import org.mapstruct.MapMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.Mappings; import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; import org.mapstruct.ap.test.nullvaluemapping._target.DriverAndCarDto; import org.mapstruct.ap.test.nullvaluemapping.source.Car; @@ -29,18 +28,14 @@ public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); @BeanMapping(nullValueMappingStrategy = RETURN_DEFAULT) - @Mappings({ - @Mapping(target = "seatCount", source = "numberOfSeats"), - @Mapping(target = "model", constant = "ModelT"), - @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") - }) + @Mapping(target = "seatCount", source = "numberOfSeats") + @Mapping(target = "model", constant = "ModelT") + @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") CarDto carToCarDto(Car car); @BeanMapping(nullValueMappingStrategy = RETURN_DEFAULT) - @Mappings({ - @Mapping(target = "seatCount", source = "car.numberOfSeats"), - @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") - }) + @Mapping(target = "seatCount", source = "car.numberOfSeats") + @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") CarDto carToCarDto(Car car, String model); @IterableMapping(nullValueMappingStrategy = RETURN_DEFAULT) diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperIterableSettingOnConfig.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperIterableSettingOnConfig.java new file mode 100644 index 0000000000..8fd9447073 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperIterableSettingOnConfig.java @@ -0,0 +1,35 @@ +/* + * 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.nullvaluemapping; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.mapstruct.IterableMapping; +import org.mapstruct.MapMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.factory.Mappers; + +@Mapper(imports = UUID.class, config = CentralIterableMappingConfig.class) +public interface CarMapperIterableSettingOnConfig { + + CarMapperIterableSettingOnConfig INSTANCE = Mappers.getMapper( CarMapperIterableSettingOnConfig.class ); + + @Mapping(target = "seatCount", source = "numberOfSeats") + @Mapping(target = "model", constant = "ModelT") + @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") + CarDto carToCarDto(Car car); + + @IterableMapping(dateFormat = "dummy") + List carsToCarDtos(List cars); + + @MapMapping(valueDateFormat = "dummy") + Map carsToCarDtoMap(Map cars); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperIterableSettingOnMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperIterableSettingOnMapper.java new file mode 100644 index 0000000000..0e1e123c52 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperIterableSettingOnMapper.java @@ -0,0 +1,40 @@ +/* + * 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.nullvaluemapping; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.mapstruct.IterableMapping; +import org.mapstruct.MapMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueMappingStrategy; +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.factory.Mappers; + +@Mapper( + imports = UUID.class, + nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT, + nullValueIterableMappingStrategy = NullValueMappingStrategy.RETURN_NULL +) +public interface CarMapperIterableSettingOnMapper { + + CarMapperIterableSettingOnMapper INSTANCE = Mappers.getMapper( CarMapperIterableSettingOnMapper.class ); + + @Mapping(target = "seatCount", source = "numberOfSeats") + @Mapping(target = "model", constant = "ModelT") + @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") + CarDto carToCarDto(Car car); + + @IterableMapping(dateFormat = "dummy") + List carsToCarDtos(List cars); + + @MapMapping(valueDateFormat = "dummy") + Map carsToCarDtoMap(Map cars); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperMapSettingOnConfig.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperMapSettingOnConfig.java new file mode 100644 index 0000000000..47f0f34f63 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperMapSettingOnConfig.java @@ -0,0 +1,35 @@ +/* + * 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.nullvaluemapping; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.mapstruct.IterableMapping; +import org.mapstruct.MapMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.factory.Mappers; + +@Mapper(imports = UUID.class, config = CentralMapMappingConfig.class) +public interface CarMapperMapSettingOnConfig { + + CarMapperMapSettingOnConfig INSTANCE = Mappers.getMapper( CarMapperMapSettingOnConfig.class ); + + @Mapping(target = "seatCount", source = "numberOfSeats") + @Mapping(target = "model", constant = "ModelT") + @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") + CarDto carToCarDto(Car car); + + @IterableMapping(dateFormat = "dummy") + List carsToCarDtos(List cars); + + @MapMapping(valueDateFormat = "dummy") + Map carsToCarDtoMap(Map cars); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperMapSettingOnMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperMapSettingOnMapper.java new file mode 100644 index 0000000000..0caad062c5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperMapSettingOnMapper.java @@ -0,0 +1,40 @@ +/* + * 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.nullvaluemapping; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.mapstruct.IterableMapping; +import org.mapstruct.MapMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueMappingStrategy; +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.factory.Mappers; + +@Mapper( + imports = UUID.class, + nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT, + nullValueMapMappingStrategy = NullValueMappingStrategy.RETURN_NULL +) +public interface CarMapperMapSettingOnMapper { + + CarMapperMapSettingOnMapper INSTANCE = Mappers.getMapper( CarMapperMapSettingOnMapper.class ); + + @Mapping(target = "seatCount", source = "numberOfSeats") + @Mapping(target = "model", constant = "ModelT") + @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") + CarDto carToCarDto(Car car); + + @IterableMapping(dateFormat = "dummy") + List carsToCarDtos(List cars); + + @MapMapping(valueDateFormat = "dummy") + Map carsToCarDtoMap(Map cars); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperSettingOnConfig.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperSettingOnConfig.java index f8f15a3e77..8e4ca2dac6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperSettingOnConfig.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperSettingOnConfig.java @@ -13,7 +13,6 @@ import org.mapstruct.MapMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.Mappings; import org.mapstruct.NullValueMappingStrategy; import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; import org.mapstruct.ap.test.nullvaluemapping.source.Car; @@ -24,11 +23,9 @@ public interface CarMapperSettingOnConfig { CarMapperSettingOnConfig INSTANCE = Mappers.getMapper( CarMapperSettingOnConfig.class ); - @Mappings({ - @Mapping(target = "seatCount", source = "numberOfSeats"), - @Mapping(target = "model", constant = "ModelT"), - @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") - }) + @Mapping(target = "seatCount", source = "numberOfSeats") + @Mapping(target = "model", constant = "ModelT") + @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") CarDto carToCarDto(Car car); @IterableMapping(dateFormat = "dummy") diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperSettingOnMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperSettingOnMapper.java index c3f7d5f2c7..2306b3add4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperSettingOnMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapperSettingOnMapper.java @@ -13,7 +13,6 @@ import org.mapstruct.MapMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.Mappings; import org.mapstruct.NullValueMappingStrategy; import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; import org.mapstruct.ap.test.nullvaluemapping.source.Car; @@ -24,11 +23,9 @@ public interface CarMapperSettingOnMapper { CarMapperSettingOnMapper INSTANCE = Mappers.getMapper( CarMapperSettingOnMapper.class ); - @Mappings({ - @Mapping(target = "seatCount", source = "numberOfSeats"), - @Mapping(target = "model", constant = "ModelT"), - @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") - }) + @Mapping(target = "seatCount", source = "numberOfSeats") + @Mapping(target = "model", constant = "ModelT") + @Mapping(target = "catalogId", expression = "java( UUID.randomUUID().toString() )") CarDto carToCarDto(Car car); @IterableMapping(nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT) diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CentralIterableMappingConfig.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CentralIterableMappingConfig.java new file mode 100644 index 0000000000..559c3520a9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CentralIterableMappingConfig.java @@ -0,0 +1,17 @@ +/* + * 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.nullvaluemapping; + +import org.mapstruct.MapperConfig; +import org.mapstruct.NullValueMappingStrategy; + +@MapperConfig( + nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT, + nullValueIterableMappingStrategy = NullValueMappingStrategy.RETURN_NULL +) +public class CentralIterableMappingConfig { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CentralMapMappingConfig.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CentralMapMappingConfig.java new file mode 100644 index 0000000000..7737e07de6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CentralMapMappingConfig.java @@ -0,0 +1,17 @@ +/* + * 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.nullvaluemapping; + +import org.mapstruct.MapperConfig; +import org.mapstruct.NullValueMappingStrategy; + +@MapperConfig( + nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT, + nullValueMapMappingStrategy = NullValueMappingStrategy.RETURN_NULL +) +public class CentralMapMappingConfig { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java index 4bd9324a9a..a027acaba3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMappingTest.java @@ -33,8 +33,14 @@ DriverAndCarDto.class, CarMapper.class, CarMapperSettingOnMapper.class, + CarMapperIterableSettingOnMapper.class, + CarMapperMapSettingOnMapper.class, CentralConfig.class, - CarMapperSettingOnConfig.class + CarMapperSettingOnConfig.class, + CentralIterableMappingConfig.class, + CarMapperIterableSettingOnConfig.class, + CentralMapMappingConfig.class, + CarMapperMapSettingOnConfig.class, }) public class NullValueMappingTest { @@ -161,8 +167,7 @@ public void shouldMapIterableWithNullArgOnMapper() { List carDtos = CarMapperSettingOnMapper.INSTANCE.carsToCarDtos( null ); //then - assertThat( carDtos ).isNotNull(); - assertThat( carDtos.isEmpty() ).isTrue(); + assertThat( carDtos ).isEmpty(); } @ProcessorTest @@ -175,6 +180,74 @@ public void shouldMapMapWithNullArgOnMapper() { assertThat( carDtoMap ).isNull(); } + @ProcessorTest + public void shouldMapExpressionAndConstantRegardlessOfIterableNullArgOnMapper() { + + //when + CarDto carDto = CarMapperIterableSettingOnMapper.INSTANCE.carToCarDto( null ); + + //then + assertThat( carDto ).isNotNull(); + assertThat( carDto.getMake() ).isNull(); + assertThat( carDto.getSeatCount() ).isEqualTo( 0 ); + assertThat( carDto.getModel() ).isEqualTo( "ModelT" ); + assertThat( carDto.getCatalogId() ).isNotEmpty(); + } + + @ProcessorTest + public void shouldMapIterableToNullWithIterableNullArgOnMapper() { + + //when + List carDtos = CarMapperIterableSettingOnMapper.INSTANCE.carsToCarDtos( null ); + + //then + assertThat( carDtos ).isNull(); + } + + @ProcessorTest + public void shouldMapMapRegardlessOfIterableNullArgOnMapper() { + + //when + Map carDtoMap = CarMapperIterableSettingOnMapper.INSTANCE.carsToCarDtoMap( null ); + + //then + assertThat( carDtoMap ).isEmpty(); + } + + @ProcessorTest + public void shouldMapExpressionAndConstantRegardlessMapNullArgOnMapper() { + + //when + CarDto carDto = CarMapperMapSettingOnMapper.INSTANCE.carToCarDto( null ); + + //then + assertThat( carDto ).isNotNull(); + assertThat( carDto.getMake() ).isNull(); + assertThat( carDto.getSeatCount() ).isEqualTo( 0 ); + assertThat( carDto.getModel() ).isEqualTo( "ModelT" ); + assertThat( carDto.getCatalogId() ).isNotEmpty(); + } + + @ProcessorTest + public void shouldMapIterableRegardlessOfMapNullArgOnMapper() { + + //when + List carDtos = CarMapperMapSettingOnMapper.INSTANCE.carsToCarDtos( null ); + + //then + assertThat( carDtos ).isEmpty(); + } + + @ProcessorTest + public void shouldMapMapToWithMapNullArgOnMapper() { + + //when + Map carDtoMap = CarMapperMapSettingOnMapper.INSTANCE.carsToCarDtoMap( null ); + + //then + assertThat( carDtoMap ).isNull(); + } + @ProcessorTest public void shouldMapExpressionAndConstantRegardlessNullArgOnConfig() { @@ -196,8 +269,7 @@ public void shouldMapIterableWithNullArgOnConfig() { List carDtos = CarMapperSettingOnConfig.INSTANCE.carsToCarDtos( null ); //then - assertThat( carDtos ).isNotNull(); - assertThat( carDtos.isEmpty() ).isTrue(); + assertThat( carDtos ).isEmpty(); } @ProcessorTest @@ -210,6 +282,74 @@ public void shouldMapMapWithNullArgOnConfig() { assertThat( carDtoMap ).isNull(); } + @ProcessorTest + public void shouldMapExpressionAndConstantRegardlessOfIterableNullArgOnConfig() { + + //when + CarDto carDto = CarMapperIterableSettingOnConfig.INSTANCE.carToCarDto( null ); + + //then + assertThat( carDto ).isNotNull(); + assertThat( carDto.getMake() ).isNull(); + assertThat( carDto.getSeatCount() ).isEqualTo( 0 ); + assertThat( carDto.getModel() ).isEqualTo( "ModelT" ); + assertThat( carDto.getCatalogId() ).isNotEmpty(); + } + + @ProcessorTest + public void shouldMapIterableToNullWithIterableNullArgOnConfig() { + + //when + List carDtos = CarMapperIterableSettingOnConfig.INSTANCE.carsToCarDtos( null ); + + //then + assertThat( carDtos ).isNull(); + } + + @ProcessorTest + public void shouldMapMapRegardlessOfIterableNullArgOnConfig() { + + //when + Map carDtoMap = CarMapperIterableSettingOnConfig.INSTANCE.carsToCarDtoMap( null ); + + //then + assertThat( carDtoMap ).isEmpty(); + } + + @ProcessorTest + public void shouldMapExpressionAndConstantRegardlessOfMapNullArgOnConfig() { + + //when + CarDto carDto = CarMapperMapSettingOnConfig.INSTANCE.carToCarDto( null ); + + //then + assertThat( carDto ).isNotNull(); + assertThat( carDto.getMake() ).isNull(); + assertThat( carDto.getSeatCount() ).isEqualTo( 0 ); + assertThat( carDto.getModel() ).isEqualTo( "ModelT" ); + assertThat( carDto.getCatalogId() ).isNotEmpty(); + } + + @ProcessorTest + public void shouldMapIterableRegardlessOfMapNullArgOnConfig() { + + //when + List carDtos = CarMapperMapSettingOnConfig.INSTANCE.carsToCarDtos( null ); + + //then + assertThat( carDtos ).isEmpty(); + } + + @ProcessorTest + public void shouldMapMapToNullWithMapNullArgOnConfig() { + + //when + Map carDtoMap = CarMapperMapSettingOnConfig.INSTANCE.carsToCarDtoMap( null ); + + //then + assertThat( carDtoMap ).isNull(); + } + @ProcessorTest public void shouldApplyConfiguredStrategyForMethodWithSeveralSourceParams() { //when From ca2529f862c2e439a1341d2f3b85189513c140ad Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sun, 31 Oct 2021 16:46:35 +0100 Subject: [PATCH 209/597] #598: Errors/Warnings now end up in the @Mapper annotated class. (#2634) #598: Errors/Warnings now end up in the @Mapper annotated class. Co-authored-by: Ben Zegveld --- .../MapperAnnotatedFormattingMessenger.java | 136 ++++++++++++++++++ .../processor/MapperCreationProcessor.java | 3 +- .../mapstruct/ap/internal/util/Message.java | 2 + .../AbstractMapper.java | 11 ++ .../ErroneousMapper1.java | 13 ++ .../ErroneousMapper2.java | 14 ++ .../ErroneousPropertyMappingTest.java | 51 +++++++ .../Source.java | 19 +++ .../Target.java | 20 +++ .../UnmappableClass.java | 9 ++ .../nestedbeans/DottedErrorMessageTest.java | 84 ++++++----- 11 files changed, 325 insertions(+), 37 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/processor/MapperAnnotatedFormattingMessenger.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/AbstractMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousMapper1.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousMapper2.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousPropertyMappingTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/UnmappableClass.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperAnnotatedFormattingMessenger.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperAnnotatedFormattingMessenger.java new file mode 100644 index 0000000000..eeb84570b0 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperAnnotatedFormattingMessenger.java @@ -0,0 +1,136 @@ +/* + * 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.internal.processor; + +import java.util.stream.Collectors; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.tools.Diagnostic.Kind; + +import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.TypeUtils; + +/** + * Handles redirection of errors/warnings so that they're shown on the mapper instead of hidden on a superclass. + * + * @author Ben Zegveld + */ +public class MapperAnnotatedFormattingMessenger implements FormattingMessager { + + private FormattingMessager delegateMessager; + private TypeElement mapperTypeElement; + private TypeUtils typeUtils; + + public MapperAnnotatedFormattingMessenger(FormattingMessager delegateMessager, TypeElement mapperTypeElement, + TypeUtils typeUtils) { + this.delegateMessager = delegateMessager; + this.mapperTypeElement = mapperTypeElement; + this.typeUtils = typeUtils; + } + + @Override + public void printMessage(Message msg, Object... args) { + delegateMessager.printMessage( msg, args ); + } + + @Override + public void printMessage(Element e, Message msg, Object... args) { + delegateMessager + .printMessage( + determineDelegationElement( e ), + determineDelegationMessage( e, msg ), + determineDelegationArguments( e, msg, args ) ); + } + + @Override + public void printMessage(Element e, AnnotationMirror a, Message msg, Object... args) { + delegateMessager + .printMessage( + determineDelegationElement( e ), + a, + determineDelegationMessage( e, msg ), + determineDelegationArguments( e, msg, args ) ); + } + + @Override + public void printMessage(Element e, AnnotationMirror a, AnnotationValue v, Message msg, Object... args) { + delegateMessager + .printMessage( + determineDelegationElement( e ), + a, + v, + determineDelegationMessage( e, msg ), + determineDelegationArguments( e, msg, args ) ); + } + + @Override + public void note(int level, Message log, Object... args) { + delegateMessager.note( level, log, args ); + } + + @Override + public boolean isErroneous() { + return delegateMessager.isErroneous(); + } + + private Object[] determineDelegationArguments(Element e, Message msg, Object[] args) { + if ( methodInMapperClass( e ) ) { + return args; + } + String originalMessage = String.format( msg.getDescription(), args ); + return new Object[] { originalMessage, constructMethod( e ), e.getEnclosingElement().getSimpleName() }; + } + + /** + * ExecutableElement.toString() has different values depending on the compiler. Constructing the method itself + * manually will ensure that the message is always traceable to it's source. + */ + private String constructMethod(Element e) { + if ( e instanceof ExecutableElement ) { + ExecutableElement ee = (ExecutableElement) e; + StringBuilder method = new StringBuilder(); + method.append( typeUtils.asElement( ee.getReturnType() ).getSimpleName() ); + method.append( " " ); + method.append( ee.getSimpleName() ); + method.append( "(" ); + method.append( ee.getParameters() + .stream() + .map( this::parameterToString ) + .collect( Collectors.joining( ", " ) ) ); + method.append( ")" ); + return method.toString(); + } + return e.toString(); + } + + private String parameterToString(VariableElement element) { + return typeUtils.asElement( element.asType() ).getSimpleName() + " " + element.getSimpleName(); + } + + private Message determineDelegationMessage(Element e, Message msg) { + if ( methodInMapperClass( e ) ) { + return msg; + } + if ( msg.getDiagnosticKind() == Kind.ERROR ) { + return Message.MESSAGE_MOVED_TO_MAPPER_ERROR; + } + return Message.MESSAGE_MOVED_TO_MAPPER_WARNING; + } + + private Element determineDelegationElement(Element e) { + return methodInMapperClass( e ) ? e : mapperTypeElement; + } + + private boolean methodInMapperClass(Element e) { + return mapperTypeElement == null || e.equals( mapperTypeElement ) + || e.getEnclosingElement().equals( mapperTypeElement ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 13e0010171..539e40daf5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -96,7 +96,8 @@ public class MapperCreationProcessor implements ModelElementProcessor sourceModel) { this.elementUtils = context.getElementUtils(); this.typeUtils = context.getTypeUtils(); - this.messager = context.getMessager(); + this.messager = + new MapperAnnotatedFormattingMessenger( context.getMessager(), mapperTypeElement, context.getTypeUtils() ); this.options = context.getOptions(); this.versionInformation = context.getVersionInformation(); this.typeFactory = context.getTypeFactory(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 66b37de7a1..9b21e4090f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -20,6 +20,8 @@ public enum Message { // CHECKSTYLE:OFF PROCESSING_NOTE( "processing: %s.", Diagnostic.Kind.NOTE ), CONFIG_NOTE( "applying mapper configuration: %s.", Diagnostic.Kind.NOTE ), + MESSAGE_MOVED_TO_MAPPER_ERROR( "%s Occured at '%s' in '%s'." ), + MESSAGE_MOVED_TO_MAPPER_WARNING( "%s Occured at '%s' in '%s'.", Diagnostic.Kind.WARNING ), BEANMAPPING_CREATE_NOTE( "creating bean mapping method implementation for %s.", Diagnostic.Kind.NOTE ), BEANMAPPING_NO_ELEMENTS( "'nullValueMappingStrategy', 'nullValuePropertyMappingStrategy', 'resultType' and 'qualifiedBy' are undefined in @BeanMapping, define at least one of them." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/AbstractMapper.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/AbstractMapper.java new file mode 100644 index 0000000000..5155ac386c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/AbstractMapper.java @@ -0,0 +1,11 @@ +/* + * 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.erroneous.supermappingwithsubclassmapper; + +public interface AbstractMapper { + + TARGET map(SOURCE source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousMapper1.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousMapper1.java new file mode 100644 index 0000000000..95a0784934 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousMapper1.java @@ -0,0 +1,13 @@ +/* + * 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.erroneous.supermappingwithsubclassmapper; + +import org.mapstruct.Mapper; + +@Mapper +public interface ErroneousMapper1 extends AbstractMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousMapper2.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousMapper2.java new file mode 100644 index 0000000000..b75f1a0e73 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousMapper2.java @@ -0,0 +1,14 @@ +/* + * 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.erroneous.supermappingwithsubclassmapper; + +import org.mapstruct.Mapper; + +@Mapper +public interface ErroneousMapper2 extends AbstractMapper { + @Override + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousPropertyMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousPropertyMappingTest.java new file mode 100644 index 0000000000..147f1d6929 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/ErroneousPropertyMappingTest.java @@ -0,0 +1,51 @@ +/* + * 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.erroneous.supermappingwithsubclassmapper; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +@IssueKey( "598" ) +@WithClasses( { Source.class, Target.class, UnmappableClass.class, AbstractMapper.class } ) +public class ErroneousPropertyMappingTest { + + @ProcessorTest + @WithClasses( ErroneousMapper1.class ) + @IssueKey( "598" ) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapper1.class, + line = 11, + messageRegExp = "Can't map property \"UnmappableClass property\" to \"String property\"\\. " + + "Consider to declare/implement a mapping method: \"String map\\(UnmappableClass value\\)\"\\. " + + "Occured at 'TARGET map\\(SOURCE source\\)' in 'AbstractMapper'\\.") + } + ) + public void testUnmappableSourcePropertyInSuperclass() { + } + + @ProcessorTest + @WithClasses( ErroneousMapper2.class ) + @IssueKey( "598" ) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapper2.class, + line = 13, + message = "Can't map property \"UnmappableClass property\" to \"String property\". " + + "Consider to declare/implement a mapping method: \"String map(UnmappableClass value)\".") } ) + public void testMethodOverride() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/Source.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/Source.java new file mode 100644 index 0000000000..383eb1dbe6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/Source.java @@ -0,0 +1,19 @@ +/* + * 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.erroneous.supermappingwithsubclassmapper; + +public class Source { + + private UnmappableClass property; + + public UnmappableClass getProperty() { + return property; + } + + public void setProperty(UnmappableClass property) { + this.property = property; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/Target.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/Target.java new file mode 100644 index 0000000000..f647ad7afa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/Target.java @@ -0,0 +1,20 @@ +/* + * 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.erroneous.supermappingwithsubclassmapper; + +public class Target { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/UnmappableClass.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/UnmappableClass.java new file mode 100644 index 0000000000..af239fb046 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/supermappingwithsubclassmapper/UnmappableClass.java @@ -0,0 +1,9 @@ +/* + * 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.erroneous.supermappingwithsubclassmapper; + +public class UnmappableClass { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java index fb10616577..f98da19a75 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/DottedErrorMessageTest.java @@ -93,11 +93,12 @@ public class DottedErrorMessageTest { @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { - @Diagnostic(type = BaseDeepNestingMapper.class, + @Diagnostic(type = UnmappableDeepNestingMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 10, + line = 14, message = "Unmapped target property: \"rgb\". Mapping from " + PROPERTY + - " \"Color house.roof.color\" to \"ColorDto house.roof.color\".") + " \"Color house.roof.color\" to \"ColorDto house.roof.color\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepNestingMapper'.") } ) public void testDeepNestedBeans() { @@ -110,11 +111,12 @@ public void testDeepNestedBeans() { @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { - @Diagnostic(type = BaseDeepListMapper.class, + @Diagnostic(type = UnmappableDeepListMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 10, + line = 14, message = "Unmapped target property: \"left\". Mapping from " + COLLECTION_ELEMENT + - " \"Wheel car.wheels\" to \"WheelDto car.wheels\".") + " \"Wheel car.wheels\" to \"WheelDto car.wheels\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepListMapper'.") } ) public void testIterables() { @@ -127,11 +129,12 @@ public void testIterables() { @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { - @Diagnostic(type = BaseDeepMapKeyMapper.class, + @Diagnostic(type = UnmappableDeepMapKeyMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 10, + line = 14, message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_KEY + - " \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\".") + " \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepMapKeyMapper'.") } ) public void testMapKeys() { @@ -144,11 +147,12 @@ public void testMapKeys() { @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { - @Diagnostic(type = BaseDeepMapValueMapper.class, + @Diagnostic(type = UnmappableDeepMapValueMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 10, + line = 14, message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_VALUE + - " \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\".") + " \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepMapValueMapper'.") } ) public void testMapValues() { @@ -161,11 +165,12 @@ public void testMapValues() { @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { - @Diagnostic(type = BaseCollectionElementPropertyMapper.class, + @Diagnostic(type = UnmappableCollectionElementPropertyMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 10, + line = 14, message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + - " \"Info computers[].info\" to \"InfoDto computers[].info\".") + " \"Info computers[].info\" to \"InfoDto computers[].info\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseCollectionElementPropertyMapper'.") } ) public void testCollectionElementProperty() { @@ -178,11 +183,12 @@ public void testCollectionElementProperty() { @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { - @Diagnostic(type = BaseValuePropertyMapper.class, + @Diagnostic(type = UnmappableValuePropertyMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 10, + line = 14, message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + - " \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\".") + " \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseValuePropertyMapper'.") } ) public void testMapValueProperty() { @@ -220,36 +226,42 @@ public void testMapEnumProperty() { @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, diagnostics = { - @Diagnostic(type = BaseDeepNestingMapper.class, + @Diagnostic(type = UnmappableWarnDeepNestingMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, - line = 10, + line = 13, message = "Unmapped target property: \"rgb\". Mapping from " + PROPERTY + - " \"Color house.roof.color\" to \"ColorDto house.roof.color\"."), - @Diagnostic(type = BaseDeepListMapper.class, + " \"Color house.roof.color\" to \"ColorDto house.roof.color\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepNestingMapper'."), + @Diagnostic(type = UnmappableWarnDeepListMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, - line = 10, + line = 13, message = "Unmapped target property: \"left\". Mapping from " + COLLECTION_ELEMENT + - " \"Wheel car.wheels\" to \"WheelDto car.wheels\"."), - @Diagnostic(type = BaseDeepMapKeyMapper.class, + " \"Wheel car.wheels\" to \"WheelDto car.wheels\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepListMapper'."), + @Diagnostic(type = UnmappableWarnDeepMapKeyMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, - line = 10, + line = 13, message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_KEY + - " \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\"."), - @Diagnostic(type = BaseDeepMapValueMapper.class, + " \"Word dictionary.wordMap{:key}\" to \"WordDto dictionary.wordMap{:key}\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepMapKeyMapper'."), + @Diagnostic(type = UnmappableWarnDeepMapValueMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, - line = 10, + line = 13, message = "Unmapped target property: \"pronunciation\". Mapping from " + MAP_VALUE + - " \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\"."), - @Diagnostic(type = BaseCollectionElementPropertyMapper.class, + " \"ForeignWord dictionary.wordMap{:value}\" to \"ForeignWordDto dictionary.wordMap{:value}\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseDeepMapValueMapper'."), + @Diagnostic(type = UnmappableWarnCollectionElementPropertyMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, - line = 10, + line = 13, message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + - " \"Info computers[].info\" to \"InfoDto computers[].info\"."), - @Diagnostic(type = BaseValuePropertyMapper.class, + " \"Info computers[].info\" to \"InfoDto computers[].info\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseCollectionElementPropertyMapper'."), + @Diagnostic(type = UnmappableWarnValuePropertyMapper.class, kind = javax.tools.Diagnostic.Kind.WARNING, - line = 10, + line = 13, message = "Unmapped target property: \"color\". Mapping from " + PROPERTY + - " \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\".") + " \"Info catNameMap{:value}.info\" to \"InfoDto catNameMap{:value}.info\"." + + " Occured at 'UserDto userToUserDto(User user)' in 'BaseValuePropertyMapper'.") } ) public void testWarnUnmappedTargetProperties() { From 907d605160386d818e8df0a577fef32da30b147e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 24 Oct 2021 17:20:56 +0200 Subject: [PATCH 210/597] #2624 Nested target methods should be inherited for forged Map to Bean methods --- .../ap/internal/model/PropertyMapping.java | 19 ++--- .../ap/test/bugs/_2624/Issue2624Mapper.java | 76 +++++++++++++++++++ .../ap/test/bugs/_2624/Issue2624Test.java | 43 +++++++++++ 3 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2624/Issue2624Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2624/Issue2624Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 82d2dd53fa..1d902e54e7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -309,7 +309,7 @@ else if ( sourceType.isMapType() && targetType.isMapType() ) { assignment = forgeMapMapping( sourceType, targetType, rightHandSide ); } else if ( sourceType.isMapType() && !targetType.isMapType()) { - assignment = forgeMapToBeanMapping( sourceType, targetType, rightHandSide ); + assignment = forgeMapping( sourceType, targetType.withoutBounds(), rightHandSide ); } else if ( ( sourceType.isIterableType() && targetType.isStreamType() ) || ( sourceType.isStreamType() && targetType.isStreamType() ) @@ -753,19 +753,6 @@ private Assignment forgeMapMapping(Type sourceType, Type targetType, SourceRHS s return createForgedAssignment( source, methodRef, mapMappingMethod ); } - private Assignment forgeMapToBeanMapping(Type sourceType, Type targetType, SourceRHS source) { - - targetType = targetType.withoutBounds(); - ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, "{}" ); - - BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder(); - final BeanMappingMethod mapToBeanMappingMethod = builder.mappingContext( ctx ) - .forgedMethod( methodRef ) - .build(); - - return createForgedAssignment( source, methodRef, mapToBeanMappingMethod ); - } - private Assignment forgeMapping(SourceRHS sourceRHS) { Type sourceType; if ( targetWriteAccessorType == AccessorType.ADDER ) { @@ -778,6 +765,10 @@ private Assignment forgeMapping(SourceRHS sourceRHS) { return null; } + return forgeMapping( sourceType, targetType, sourceRHS ); + } + + private Assignment forgeMapping(Type sourceType, Type targetType, SourceRHS sourceRHS) { //Fail fast. If we could not find the method by now, no need to try if ( sourceType.isPrimitive() || targetType.isPrimitive() ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2624/Issue2624Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2624/Issue2624Mapper.java new file mode 100644 index 0000000000..f4d3c3d22d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2624/Issue2624Mapper.java @@ -0,0 +1,76 @@ +/* + * 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._2624; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2624Mapper { + + Issue2624Mapper INSTANCE = Mappers.getMapper( Issue2624Mapper.class ); + + @Mapping( target = "department.id", source = "did") + @Mapping( target = "department.name", source = "dname") + Employee fromMap(Map source); + + class Employee { + private String id; + private String name; + private Department department; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Department getDepartment() { + return department; + } + + public void setDepartment(Department department) { + this.department = department; + } + } + + class Department { + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2624/Issue2624Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2624/Issue2624Test.java new file mode 100644 index 0000000000..2fb04ef3a0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2624/Issue2624Test.java @@ -0,0 +1,43 @@ +/* + * 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._2624; + +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Issue2624Mapper.class +}) +class Issue2624Test { + + @ProcessorTest + void shouldCorrectlyMapNestedTargetFromMap() { + Map map = new HashMap<>(); + map.put( "id", "1234" ); + map.put( "name", "Tester" ); + map.put( "did", "4321" ); //Department Id + map.put( "dname", "Test" ); // Department name + + Issue2624Mapper.Employee employee = Issue2624Mapper.INSTANCE.fromMap( map ); + + assertThat( employee ).isNotNull(); + assertThat( employee.getId() ).isEqualTo( "1234" ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + + Issue2624Mapper.Department department = employee.getDepartment(); + assertThat( department ).isNotNull(); + assertThat( department.getId() ).isEqualTo( "4321" ); + assertThat( department.getName() ).isEqualTo( "Test" ); + } +} From 166eb699c75b60979c3713c1492f98150ccfcac4 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 30 Oct 2021 14:27:24 +0200 Subject: [PATCH 211/597] #1752 Always return mapping target when using update methods with return --- .../ap/internal/model/BeanMappingMethod.ftl | 2 +- .../internal/model/IterableMappingMethod.ftl | 3 +-- .../ap/internal/model/MapMappingMethod.ftl | 2 +- .../ap/internal/model/StreamMappingMethod.ftl | 3 +-- .../ap/test/array/ArrayMappingTest.java | 13 ++++++--- .../ap/test/bugs/_374/Issue374Test.java | 3 ++- .../simple/BuilderInfoTargetTest.java | 13 +++++++++ .../DefaultCollectionImplementationTest.java | 16 ++++++++++- .../test/collection/map/MapMappingTest.java | 14 ++++++++++ .../ap/test/inheritance/InheritanceTest.java | 14 ++++++++++ .../DefaultStreamImplementationTest.java | 27 +++++++++++++++++++ .../ap/test/array/ScienceMapperImpl.java | 2 +- .../DomainDtoWithNcvsAlwaysMapperImpl.java | 2 +- .../_913/DomainDtoWithNvmsNullMapperImpl.java | 2 +- .../DomainDtoWithPresenceCheckMapperImpl.java | 2 +- .../SourceTargetMapperImpl.java | 2 +- 16 files changed, 104 insertions(+), 16 deletions(-) diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 12a03c06ca..81e3b61fd7 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -20,7 +20,7 @@ <#if !mapNullToDefault> if ( <#list sourceParametersExcludingPrimitives as sourceParam>${sourceParam.name} == null<#if sourceParam_has_next> && ) { - return<#if returnType.name != "void"> null; + return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#if finalizerMethod??>.<@includeModel object=finalizerMethod /><#else>null; } 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 3af75079b9..495111b7a1 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 @@ -16,8 +16,7 @@ if ( ${sourceParameter.name} == null ) { <#if !mapNullToDefault> - <#-- returned target type starts to miss-align here with target handed via param, TODO is this right? --> - return<#if returnType.name != "void"> null; + return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#else>null; <#else> <#if resultType.arrayType> <#if existingInstanceMapping> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl index 1c6e6bad35..443d87cba7 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl @@ -16,7 +16,7 @@ if ( ${sourceParameter.name} == null ) { <#if !mapNullToDefault> - return<#if returnType.name != "void"> null; + return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#else>null; <#else> <#if existingInstanceMapping> ${resultName}.clear(); diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl index ae48a3477a..860859fc3a 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl @@ -17,8 +17,7 @@ if ( ${sourceParameter.name} == null ) { <#if !mapNullToDefault> - <#-- returned target type starts to miss-align here with target handed via param, TODO is this right? --> - return<#if returnType.name != "void"> null; + return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#else>null; <#else> <#if resultType.arrayType> <#if existingInstanceMapping> diff --git a/processor/src/test/java/org/mapstruct/ap/test/array/ArrayMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/array/ArrayMappingTest.java index 07113f7cf4..67ef2216d8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/array/ArrayMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/array/ArrayMappingTest.java @@ -123,15 +123,15 @@ public void shouldMapArrayToArrayExistingLargerSizedTarget() { } @ProcessorTest - public void shouldMapTargetToNullWhenNullSource() { - // TODO: What about existing target? + public void shouldReturnMapTargetWhenNullSource() { ScientistDto[] existingTarget = new ScientistDto[]{ new ScientistDto( "Jim" ) }; ScientistDto[] target = ScienceMapper.INSTANCE.scientistsToDtos( null, existingTarget ); - assertThat( target ).isNull(); + assertThat( target ).isNotNull(); + assertThat( target ).isEqualTo( existingTarget ); assertThat( existingTarget ).extracting( "name" ).containsOnly( "Jim" ); } @@ -143,6 +143,7 @@ public void shouldMapBooleanWhenReturnDefault() { boolean[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); assertThat( target ).containsOnly( false ); + assertThat( target ).isEqualTo( existingTarget ); assertThat( existingTarget ).containsOnly( false ); assertThat( ScienceMapper.INSTANCE.nvmMapping( null ) ).isEmpty(); @@ -154,6 +155,7 @@ public void shouldMapShortWhenReturnDefault() { short[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); assertThat( target ).containsOnly( new short[] { 0 } ); + assertThat( target ).isEqualTo( existingTarget ); assertThat( existingTarget ).containsOnly( new short[] { 0 } ); } @@ -163,6 +165,7 @@ public void shouldMapCharWhenReturnDefault() { char[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); assertThat( target ).containsOnly( new char[] { 0 } ); + assertThat( target ).isEqualTo( existingTarget ); assertThat( existingTarget ).containsOnly( new char[] { 0 } ); } @@ -172,6 +175,7 @@ public void shouldMapIntWhenReturnDefault() { int[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); assertThat( target ).containsOnly( 0 ); + assertThat( target ).isEqualTo( existingTarget ); assertThat( existingTarget ).containsOnly( 0 ); } @@ -181,6 +185,7 @@ public void shouldMapLongWhenReturnDefault() { long[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); assertThat( target ).containsOnly( 0L ); + assertThat( target ).isEqualTo( existingTarget ); assertThat( existingTarget ).containsOnly( 0L ); } @@ -190,6 +195,7 @@ public void shouldMapFloatWhenReturnDefault() { float[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); assertThat( target ).containsOnly( 0.0f ); + assertThat( target ).isEqualTo( existingTarget ); assertThat( existingTarget ).containsOnly( 0.0f ); } @@ -199,6 +205,7 @@ public void shouldMapDoubleWhenReturnDefault() { double[] target = ScienceMapper.INSTANCE.nvmMapping( null, existingTarget ); assertThat( target ).containsOnly( 0.0d ); + assertThat( target ).isEqualTo( existingTarget ); assertThat( existingTarget ).containsOnly( 0.0d ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_374/Issue374Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_374/Issue374Test.java index 7f174732ff..2bc6d6a170 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_374/Issue374Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_374/Issue374Test.java @@ -41,7 +41,8 @@ public void shouldMapExistingTargetWithConstantToDefault() { Target target2 = new Target(); Target result2 = Issue374Mapper.INSTANCE.map2( null, target2 ); - assertThat( result2 ).isNull(); + assertThat( result2 ).isNotNull(); + assertThat( result2 ).isEqualTo( target2 ); assertThat( target2.getTest() ).isNull(); assertThat( target2.getConstant() ).isNull(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java index 3682f2ccc5..4f3d8b85f4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.builder.mappingTarget.simple; import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.runner.GeneratedSource; @@ -36,6 +37,18 @@ public void testSimpleImmutableBuilderHappyPath() { assertThat( targetObject.getName() ).isEqualTo( "Bob" ); } + @ProcessorTest + @IssueKey("1752") + public void testSimpleImmutableBuilderFromNullSource() { + SimpleImmutableTarget targetObject = SimpleBuilderMapper.INSTANCE.toImmutable( + null, + SimpleImmutableTarget.builder().age( 3 ).name( "Bob" ) + ); + assertThat( targetObject ).isNotNull(); + assertThat( targetObject.getAge() ).isEqualTo( 3 ); + assertThat( targetObject.getName() ).isEqualTo( "Bob" ); + } + @ProcessorTest public void testMutableTargetWithBuilder() { SimpleMutableSource source = new SimpleMutableSource(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java index 9d360656a0..dccfcf01ca 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java @@ -159,7 +159,21 @@ public void shouldUseAndReturnTargetParameterForMapping() { SourceTargetMapper.INSTANCE .sourceFoosToTargetFoosUsingTargetParameterAndReturn( createSourceFooList(), target ); - assertThat( target == result ).isTrue(); + assertThat( result ).isSameAs( target ); + assertResultList( target ); + } + + @ProcessorTest + @IssueKey("1752") + public void shouldUseAndReturnTargetParameterForNullMapping() { + List target = new ArrayList<>(); + target.add( new TargetFoo( "Bob" ) ); + target.add( new TargetFoo( "Alice" ) ); + Iterable result = + SourceTargetMapper.INSTANCE + .sourceFoosToTargetFoosUsingTargetParameterAndReturn( null, target ); + + assertThat( result ).isSameAs( target ); assertResultList( target ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java index ddba51f826..d0dd66d22b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java @@ -82,6 +82,20 @@ public void shouldCreateMapMethodImplementationWithReturnedTargetParameter() { assertResult( target ); } + @ProcessorTest + @IssueKey("1752") + public void shouldCreateMapMethodImplementationWithReturnedTargetParameterAndNullSource() { + Map target = new HashMap<>(); + target.put( 42L, new GregorianCalendar( 1980, Calendar.JANUARY, 1 ).getTime() ); + target.put( 121L, new GregorianCalendar( 2013, Calendar.JULY, 20 ).getTime() ); + + Map returnedTarget = SourceTargetMapper.INSTANCE + .stringStringMapToLongDateMapUsingTargetParameterAndReturn( null, target ); + + assertThat( target ).isSameAs( returnedTarget ); + assertResult( target ); + } + private void assertResult(Map target) { assertThat( target ).isNotNull(); assertThat( target ).hasSize( 2 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java index d127a9ac11..aaf299635e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java @@ -53,6 +53,20 @@ public void shouldMapAttributeFromSuperTypeUsingReturnedTargetParameter() { assertResult( target ); } + @ProcessorTest + @IssueKey("1752") + public void shouldMapAttributeFromSuperTypeUsingReturnedTargetParameterAndNullSource() { + + TargetExt target = new TargetExt(); + target.setFoo( 42L ); + target.publicFoo = 52L; + target.setBar( 23 ); + TargetBase result = SourceTargetMapper.INSTANCE.sourceToTargetWithTargetParameterAndReturn( null, target ); + + assertThat( target ).isSameAs( result ); + assertResult( target ); + } + private void assertResult(TargetExt target) { assertThat( target ).isNotNull(); assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/DefaultStreamImplementationTest.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/DefaultStreamImplementationTest.java index 91be53fe64..8e69bafce2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/DefaultStreamImplementationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/DefaultStreamImplementationTest.java @@ -141,6 +141,19 @@ public void shouldUseAndReturnTargetParameterForArrayMappingAndSmallerArray() { assertThat( target ).containsOnly( new TargetFoo( "Bob" ) ); } + @ProcessorTest + @IssueKey("1752") + public void shouldUseAndReturnTargetParameterArrayForNullSource() { + TargetFoo[] target = new TargetFoo[1]; + target[0] = new TargetFoo( "Bob" ); + TargetFoo[] result = + SourceTargetMapper.INSTANCE.streamToArrayUsingTargetParameterAndReturn( null, target ); + + assertThat( result ).isSameAs( target ); + assertThat( target ).isNotNull(); + assertThat( target ).containsOnly( new TargetFoo( "Bob" ) ); + } + @ProcessorTest public void shouldUseAndReturnTargetParameterForMapping() { List target = new ArrayList<>(); @@ -152,6 +165,20 @@ public void shouldUseAndReturnTargetParameterForMapping() { assertResultList( target ); } + @ProcessorTest + @IssueKey("1752") + public void shouldUseAndReturnTargetParameterForNullMapping() { + List target = new ArrayList<>(); + target.add( new TargetFoo( "Bob" ) ); + target.add( new TargetFoo( "Alice" ) ); + Iterable result = + SourceTargetMapper.INSTANCE + .sourceFoosToTargetFoosUsingTargetParameterAndReturn( null, target ); + + assertThat( result ).isSameAs( target ); + assertResultList( target ); + } + @ProcessorTest public void shouldUseDefaultImplementationForListWithoutSetter() { Source source = new Source(); diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/array/ScienceMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/array/ScienceMapperImpl.java index 066901bc45..c1f4557fbf 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/array/ScienceMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/array/ScienceMapperImpl.java @@ -92,7 +92,7 @@ public List scientistsToDtosAsList(Scientist[] scientists) { @Override public ScientistDto[] scientistsToDtos(Scientist[] scientists, ScientistDto[] target) { if ( scientists == null ) { - return null; + return target; } int i = 0; diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java index b3a8c89c60..22fed55509 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java @@ -128,7 +128,7 @@ public void update(DtoWithPresenceCheck source, Domain target) { @Override public Domain updateWithReturn(DtoWithPresenceCheck source, Domain target) { if ( source == null ) { - return null; + return target; } if ( target.getStrings() != null ) { diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java index 59a770d049..e257ac8443 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java @@ -143,7 +143,7 @@ public void update(Dto source, Domain target) { @Override public Domain updateWithReturn(Dto source, Domain target) { if ( source == null ) { - return null; + return target; } if ( target.getStrings() != null ) { diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java index 8cc41c47d5..ddba54dbd1 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java @@ -128,7 +128,7 @@ public void update(DtoWithPresenceCheck source, Domain target) { @Override public Domain updateWithReturn(DtoWithPresenceCheck source, Domain target) { if ( source == null ) { - return null; + return target; } if ( target.getStrings() != null ) { diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java index 853ad59560..d9e60ad1e1 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java @@ -133,7 +133,7 @@ public void sourceFoosToTargetFoosUsingTargetParameter(List targetFoo @Override public Iterable sourceFoosToTargetFoosUsingTargetParameterAndReturn(Iterable sourceFoos, List targetFoos) { if ( sourceFoos == null ) { - return null; + return targetFoos; } targetFoos.clear(); From 735a5bef6a36644a48bfc5bebde31e217574551c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 6 Nov 2021 09:21:16 +0100 Subject: [PATCH 212/597] #2225 Add support for suppressing the generation of the timestamp through Mapper and MapperConfig --- core/src/main/java/org/mapstruct/Mapper.java | 13 +++++++++++ .../main/java/org/mapstruct/MapperConfig.java | 11 ++++++++++ .../ap/internal/model/Decorator.java | 9 ++++++++ .../ap/internal/model/GeneratedType.java | 3 ++- .../mapstruct/ap/internal/model/Mapper.java | 9 ++++++++ .../internal/model/source/DefaultOptions.java | 7 ++++++ .../model/source/DelegatingOptions.java | 4 ++++ .../model/source/MapperConfigOptions.java | 7 ++++++ .../internal/model/source/MapperOptions.java | 7 ++++++ .../processor/MapperCreationProcessor.java | 14 ++++++------ .../SuppressTimestampViaMapper.java | 16 ++++++++++++++ .../SuppressTimestampViaMapperConfig.java | 22 +++++++++++++++++++ .../ap/test/versioninfo/VersionInfoTest.java | 16 ++++++++++++++ 13 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapperConfig.java diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 26442e4ff0..c5aed661f3 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -357,4 +357,17 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default * @since 1.4 */ Class unexpectedValueMappingException() default IllegalArgumentException.class; + + /** + * Flag indicating whether the addition of a time stamp in the {@code @Generated} annotation should be suppressed. + * i.e. not be added. + * + * The method overrides the flag set in a central configuration set by {@link #config()} + * or through an annotation processor option. + * + * @return whether the addition of a timestamp should be suppressed + * + * @since 1.5 + */ + boolean suppressTimestampInGenerated() default false; } diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java index ce239322a2..757a4ab0af 100644 --- a/core/src/main/java/org/mapstruct/MapperConfig.java +++ b/core/src/main/java/org/mapstruct/MapperConfig.java @@ -329,5 +329,16 @@ MappingInheritanceStrategy mappingInheritanceStrategy() */ Class unexpectedValueMappingException() default IllegalArgumentException.class; + /** + * Flag indicating whether the addition of a time stamp in the {@code @Generated} annotation should be suppressed. + * i.e. not be added. + * + * The method overrides the flag set through an annotation processor option. + * + * @return whether the addition of a timestamp should be suppressed + * + * @since 1.5 + */ + boolean suppressTimestampInGenerated() default false; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java index a54045dca0..b7dc0effcb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java @@ -32,6 +32,7 @@ public static class Builder extends GeneratedTypeBuilder { private boolean hasDelegateConstructor; private String implName; private String implPackage; + private boolean suppressGeneratorTimestamp; public Builder() { super( Builder.class ); @@ -62,6 +63,11 @@ public Builder implPackage(String implPackage) { return this; } + public Builder suppressGeneratorTimestamp(boolean suppressGeneratorTimestamp) { + this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; + return this; + } + public Decorator build() { String implementationName = implName.replace( Mapper.CLASS_NAME_PLACEHOLDER, Mapper.getFlatName( mapperElement ) ); @@ -86,6 +92,7 @@ public Decorator build() { methods, options, versionInformation, + suppressGeneratorTimestamp, Accessibility.fromModifiers( mapperElement.getModifiers() ), extraImportedTypes, decoratorConstructor @@ -101,6 +108,7 @@ private Decorator(TypeFactory typeFactory, String packageName, String name, Type Type mapperType, List methods, Options options, VersionInformation versionInformation, + boolean suppressGeneratorTimestamp, Accessibility accessibility, SortedSet extraImports, DecoratorConstructor decoratorConstructor) { super( @@ -112,6 +120,7 @@ private Decorator(TypeFactory typeFactory, String packageName, String name, Type Arrays.asList( new Field( mapperType, "delegate", true ) ), options, versionInformation, + suppressGeneratorTimestamp, accessibility, extraImports, decoratorConstructor diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java index 620559ea24..134ab081dc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java @@ -103,6 +103,7 @@ public T methods(List methods) { protected GeneratedType(TypeFactory typeFactory, String packageName, String name, Type mapperDefinitionType, List methods, List fields, Options options, VersionInformation versionInformation, + boolean suppressGeneratorTimestamp, Accessibility accessibility, SortedSet extraImportedTypes, Constructor constructor) { this.packageName = packageName; this.name = name; @@ -113,7 +114,7 @@ protected GeneratedType(TypeFactory typeFactory, String packageName, String name this.methods = methods; this.fields = fields; - this.suppressGeneratorTimestamp = options.isSuppressGeneratorTimestamp(); + this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; this.suppressGeneratorVersionComment = options.isSuppressGeneratorVersionComment(); this.versionInformation = versionInformation; this.accessibility = accessibility; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java index a066b123c9..fa15cd8ed0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java @@ -42,6 +42,7 @@ public static class Builder extends GeneratedTypeBuilder { private boolean customName; private String implPackage; private boolean customPackage; + private boolean suppressGeneratorTimestamp; public Builder() { super( Builder.class ); @@ -79,6 +80,11 @@ public Builder implPackage(String implPackage) { return this; } + public Builder suppressGeneratorTimestamp(boolean suppressGeneratorTimestamp) { + this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; + return this; + } + public Mapper build() { String implementationName = implName.replace( CLASS_NAME_PLACEHOLDER, getFlatName( element ) ) + ( decorator == null ? "" : "_" ); @@ -102,6 +108,7 @@ public Mapper build() { methods, options, versionInformation, + suppressGeneratorTimestamp, Accessibility.fromModifiers( element.getModifiers() ), fields, constructor, @@ -121,6 +128,7 @@ private Mapper(TypeFactory typeFactory, String packageName, String name, Type mapperDefinitionType, boolean customPackage, boolean customImplName, List methods, Options options, VersionInformation versionInformation, + boolean suppressGeneratorTimestamp, Accessibility accessibility, List fields, Constructor constructor, Decorator decorator, SortedSet extraImportedTypes ) { @@ -133,6 +141,7 @@ private Mapper(TypeFactory typeFactory, String packageName, String name, fields, options, versionInformation, + suppressGeneratorTimestamp, accessibility, extraImportedTypes, constructor diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index 1258707e43..5c76e7face 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -83,6 +83,13 @@ public String componentModel() { return mapper.componentModel().getDefaultValue(); } + public boolean suppressTimestampInGenerated() { + if ( mapper.suppressTimestampInGenerated().hasValue() ) { + return mapper.suppressTimestampInGenerated().getValue(); + } + return options.isSuppressGeneratorTimestamp(); + } + @Override public MappingInheritanceStrategyGem getMappingInheritanceStrategy() { return MappingInheritanceStrategyGem.valueOf( mapper.mappingInheritanceStrategy().getDefaultValue() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java index 12f610f1ac..50c1d84541 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -68,6 +68,10 @@ public String componentModel() { return next.componentModel(); } + public boolean suppressTimestampInGenerated() { + return next.suppressTimestampInGenerated(); + } + public MappingInheritanceStrategyGem getMappingInheritanceStrategy() { return next.getMappingInheritanceStrategy(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java index 623b97bc78..e3ca6162a5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -76,6 +76,13 @@ public String componentModel() { return mapperConfig.componentModel().hasValue() ? mapperConfig.componentModel().get() : next().componentModel(); } + @Override + public boolean suppressTimestampInGenerated() { + return mapperConfig.suppressTimestampInGenerated().hasValue() ? + mapperConfig.suppressTimestampInGenerated().get() : + next().suppressTimestampInGenerated(); + } + @Override public MappingInheritanceStrategyGem getMappingInheritanceStrategy() { return mapperConfig.mappingInheritanceStrategy().hasValue() ? diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index 3261205116..ed1af34f79 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -105,6 +105,13 @@ public String componentModel() { return mapper.componentModel().hasValue() ? mapper.componentModel().get() : next().componentModel(); } + @Override + public boolean suppressTimestampInGenerated() { + return mapper.suppressTimestampInGenerated().hasValue() ? + mapper.suppressTimestampInGenerated().get() : + next().suppressTimestampInGenerated(); + } + @Override public MappingInheritanceStrategyGem getMappingInheritanceStrategy() { return mapper.mappingInheritanceStrategy().hasValue() ? diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 539e40daf5..bae0cda1ae 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -198,13 +198,13 @@ private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List< .constructorFragments( constructorFragments ) .options( options ) .versionInformation( versionInformation ) - .decorator( getDecorator( element, methods, mapperOptions.implementationName(), - mapperOptions.implementationPackage(), getExtraImports( element, mapperOptions ) ) ) + .decorator( getDecorator( element, methods, mapperOptions ) ) .typeFactory( typeFactory ) .elementUtils( elementUtils ) .extraImports( getExtraImports( element, mapperOptions ) ) .implName( mapperOptions.implementationName() ) .implPackage( mapperOptions.implementationPackage() ) + .suppressGeneratorTimestamp( mapperOptions.suppressTimestampInGenerated() ) .build(); if ( !mappingContext.getForgedMethodsUnderCreation().isEmpty() ) { @@ -226,8 +226,7 @@ private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List< return mapper; } - private Decorator getDecorator(TypeElement element, List methods, String implName, - String implPackage, SortedSet extraImports) { + private Decorator getDecorator(TypeElement element, List methods, MapperOptions mapperOptions) { DecoratedWithGem decoratedWith = DecoratedWithGem.instanceOn( element ); if ( decoratedWith == null ) { @@ -287,9 +286,10 @@ else if ( constructor.getParameters().size() == 1 ) { .hasDelegateConstructor( hasDelegateConstructor ) .options( options ) .versionInformation( versionInformation ) - .implName( implName ) - .implPackage( implPackage ) - .extraImports( extraImports ) + .implName( mapperOptions.implementationName() ) + .implPackage( mapperOptions.implementationPackage() ) + .extraImports( getExtraImports( element, mapperOptions ) ) + .suppressGeneratorTimestamp( mapperOptions.suppressTimestampInGenerated() ) .build(); return decorator; diff --git a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapper.java b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapper.java new file mode 100644 index 0000000000..02a7b467b1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapper.java @@ -0,0 +1,16 @@ +/* + * 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.versioninfo; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper(suppressTimestampInGenerated = true) +public interface SuppressTimestampViaMapper { + Object toObject(Object object); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapperConfig.java b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapperConfig.java new file mode 100644 index 0000000000..a471cad028 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapperConfig.java @@ -0,0 +1,22 @@ +/* + * 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.versioninfo; + +import org.mapstruct.Mapper; +import org.mapstruct.MapperConfig; + +/** + * @author Filip Hrisafov + */ +@Mapper(config = SuppressTimestampViaMapperConfig.Config.class) +public interface SuppressTimestampViaMapperConfig { + Object toObject(Object object); + + @MapperConfig(suppressTimestampInGenerated = true) + interface Config { + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java index 1eec91b951..f3192b85ad 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java @@ -49,4 +49,20 @@ public void includesCommentAndTimestamp() { .contains( "comments = \"version: " ); } + @ProcessorTest + @WithClasses(SuppressTimestampViaMapper.class) + @IssueKey("2225") + void includesNoTimestampViaMapper() { + generatedSource.forMapper( SuppressTimestampViaMapper.class ).content() + .doesNotContain( "date = \"" ); + } + + @ProcessorTest + @WithClasses(SuppressTimestampViaMapperConfig.class) + @IssueKey("2225") + void includesNoTimestampViaMapperConfig() { + generatedSource.forMapper( SuppressTimestampViaMapperConfig.class ).content() + .doesNotContain( "date = \"" ); + } + } From 72e6b1feb56c228eae5dfb91ee0a6b6c7acc9b42 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sun, 14 Nov 2021 20:11:05 +0100 Subject: [PATCH 213/597] #2636: defaultValue combined with qualified should not convert if not needed (#2637) --- core/src/main/java/org/mapstruct/Mapping.java | 8 +++ .../chapter-5-data-type-conversions.asciidoc | 70 +++++++++++++++++++ .../DefaultExpressionUsageMapper.java | 27 +++++++ .../defaults/DefaultValueUsageMapper.java | 27 +++++++ .../qualifier/defaults/DirectoryNode.java | 21 ++++++ .../FaultyDefaultValueUsageMapper.java | 26 +++++++ .../selection/qualifier/defaults/Folder.java | 29 ++++++++ .../defaults/QualifierWithDefaultsTest.java | 51 ++++++++++++++ 8 files changed, 259 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DefaultExpressionUsageMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DefaultValueUsageMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DirectoryNode.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/FaultyDefaultValueUsageMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/Folder.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/QualifierWithDefaultsTest.java diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 7ca637f0ca..92ae29eb54 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -218,6 +218,10 @@ * *

    *

    + * You can use {@link #qualifiedBy()} or {@link #qualifiedByName()} to force the use of a conversion method + * even when one would not apply. (e.g. {@code String} to {@code String}) + *

    + *

    * This attribute can not be used together with {@link #source()}, {@link #defaultValue()}, * {@link #defaultExpression()} or {@link #expression()}. * @@ -295,6 +299,8 @@ * A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple * mapping methods (hand written or generated) qualify and thus would result in an 'Ambiguous mapping methods found' * error. A qualifier is a custom annotation and can be placed on a hand written mapper class or a method. + *

    + * Note that {@link #defaultValue()} usage will also be converted using this qualifier. * * @return the qualifiers * @see Qualifier @@ -309,6 +315,8 @@ * Note that annotation-based qualifiers are generally preferable as they allow more easily to find references and * are safe for refactorings, but name-based qualifiers can be a less verbose alternative when requiring a large * number of qualifiers as no custom annotation types are needed. + *

    + * Note that {@link #defaultValue()} usage will also be converted using this qualifier. * * @return One or more qualifier name(s) * @see #qualifiedBy() diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 91f4afb1cf..e456af01ba 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -703,3 +703,73 @@ public interface MovieMapper { ==== Although the used mechanism is the same, the user has to be a bit more careful. Refactoring the name of a defined qualifier in an IDE will neatly refactor all other occurrences as well. This is obviously not the case for changing a name. ==== + +=== Combining qualifiers with defaults +Please note that the `Mapping#defaultValue` is in essence a `String`, which needs to be converted to the `Mapping#target`. Providing a `Mapping#qualifiedByName` or `Mapping#qualifiedBy` will force MapStruct to use that method. If you want different behavior for the `Mapping#defaultValue`, then please provide an appropriate mapping method. This mapping method needs to transforms a `String` into the desired type of `Mapping#target` and also be annotated so that it can be found by the `Mapping#qualifiedByName` or `Mapping#qualifiedBy`. + +.Mapper using defaultValue +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface MovieMapper { + + @Mapping( target = "category", qualifiedByName = "CategoryToString", defaultValue = "DEFAULT" ) + GermanRelease toGerman( OriginalRelease movies ); + + @Named("CategoryToString") + default String defaultValueForQualifier(Category cat) { + // some mapping logic + } +} +---- +==== + +In the above example in case that category is null, the method `CategoryToString( Enum.valueOf( Category.class, "DEFAULT" ) )` will be called and the result will be set to the category field. + +.Mapper using defaultValue and default method. +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface MovieMapper { + + @Mapping( target = "category", qualifiedByName = "CategoryToString", defaultValue = "Unknown" ) + GermanRelease toGerman( OriginalRelease movies ); + + @Named("CategoryToString") + default String defaultValueForQualifier(Category cat) { + // some mapping logic + } + + @Named("CategoryToString") + default String defaultValueForQualifier(String value) { + return value; + } +} +---- +==== +In the above example in case that category is null, the method `defaultValueForQualifier( "Unknown" )` will be called and the result will be set to the category field. + +If the above mentioned methods do not work there is the option to use `defaultExpression` to set the default value. + +.Mapper using defaultExpression +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface MovieMapper { + + @Mapping( target = "category", qualifiedByName = "CategoryToString", defaultExpression = "java(\"Unknown\")" ) + GermanRelease toGerman( OriginalRelease movies ); + + @Named("CategoryToString") + default String defaultValueForQualifier(Category cat) { + // some mapping logic + } +} +---- +==== diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DefaultExpressionUsageMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DefaultExpressionUsageMapper.java new file mode 100644 index 0000000000..69c025983e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DefaultExpressionUsageMapper.java @@ -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.selection.qualifier.defaults; + +import java.util.UUID; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface DefaultExpressionUsageMapper { + @Mapping( source = "folder.ancestor.id", target = "parent", + defaultExpression = "java(\"#\")", qualifiedByName = "uuidToString" ) + DirectoryNode convert(Folder folder); + + @Named( "uuidToString" ) + default String uuidToString(UUID id) { + return id.toString(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DefaultValueUsageMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DefaultValueUsageMapper.java new file mode 100644 index 0000000000..622cd9193a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DefaultValueUsageMapper.java @@ -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.selection.qualifier.defaults; + +import java.util.UUID; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface DefaultValueUsageMapper { + @Mapping( source = "folder.ancestor.id", target = "parent", + defaultValue = "00000000-0000-4000-0000-000000000000", qualifiedByName = "uuidToString" ) + DirectoryNode convert(Folder folder); + + @Named( "uuidToString" ) + default String uuidToString(UUID id) { + return id.toString(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DirectoryNode.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DirectoryNode.java new file mode 100644 index 0000000000..5f52f07275 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/DirectoryNode.java @@ -0,0 +1,21 @@ +/* + * 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.selection.qualifier.defaults; + +/** + * @author Ben Zegveld + */ +class DirectoryNode { + private String parent; + + public void setParent(String parent) { + this.parent = parent; + } + + public String getParent() { + return parent; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/FaultyDefaultValueUsageMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/FaultyDefaultValueUsageMapper.java new file mode 100644 index 0000000000..e394536daa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/FaultyDefaultValueUsageMapper.java @@ -0,0 +1,26 @@ +/* + * 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.selection.qualifier.defaults; + +import java.util.UUID; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface FaultyDefaultValueUsageMapper { + @Mapping( source = "folder.ancestor.id", target = "parent", defaultValue = "#", qualifiedByName = "uuidToString" ) + DirectoryNode convert(Folder folder); + + @Named( "uuidToString" ) + default String uuidToString(UUID id) { + return id.toString(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/Folder.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/Folder.java new file mode 100644 index 0000000000..a6e7b5a9b2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/Folder.java @@ -0,0 +1,29 @@ +/* + * 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.selection.qualifier.defaults; + +import java.util.UUID; + +/** + * @author Ben Zegveld + */ +class Folder { + private UUID id; + private Folder ancestor; + + Folder(UUID id, Folder ancestor) { + this.id = id; + this.ancestor = ancestor; + } + + public Folder getAncestor() { + return ancestor; + } + + public UUID getId() { + return id; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/QualifierWithDefaultsTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/QualifierWithDefaultsTest.java new file mode 100644 index 0000000000..63af9911f6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/defaults/QualifierWithDefaultsTest.java @@ -0,0 +1,51 @@ +/* + * 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.selection.qualifier.defaults; + +import java.util.UUID; +import org.assertj.core.api.Assertions; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@WithClasses( { DirectoryNode.class, Folder.class } ) +public class QualifierWithDefaultsTest { + + @ProcessorTest + @WithClasses( FaultyDefaultValueUsageMapper.class ) + void defaultValueHasInvalidValue() { + Folder rootFolder = new Folder( UUID.randomUUID(), null ); + FaultyDefaultValueUsageMapper faultyMapper = Mappers.getMapper( FaultyDefaultValueUsageMapper.class ); + + Assertions + .assertThatThrownBy( () -> faultyMapper.convert( rootFolder ) ) + .isInstanceOf( IllegalArgumentException.class ); // UUID.valueOf should throw this. + } + + @ProcessorTest + @WithClasses( DefaultValueUsageMapper.class ) + void defaultValuehasUsableValue() { + Folder rootFolder = new Folder( UUID.randomUUID(), null ); + + DirectoryNode node = Mappers.getMapper( DefaultValueUsageMapper.class ).convert( rootFolder ); + + Assertions.assertThat( node.getParent() ).isEqualTo( "00000000-0000-4000-0000-000000000000" ); + } + + @ProcessorTest + @WithClasses( DefaultExpressionUsageMapper.class ) + void defaultExpressionDoesNotGetConverted() { + Folder rootFolder = new Folder( UUID.randomUUID(), null ); + + DirectoryNode node = Mappers.getMapper( DefaultExpressionUsageMapper.class ).convert( rootFolder ); + + Assertions.assertThat( node.getParent() ).isEqualTo( "#" ); + } +} From 29008e12bf4d8f5c2136f8ab3cb6aec8547d3325 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 7 Nov 2021 09:14:13 +0100 Subject: [PATCH 214/597] #2005 Parameter type should only be checked if we are mapping from a single argument source --- .../processor/MethodRetrievalProcessor.java | 54 ++++++++++--------- .../test/multisource/MultiSourceMapper.java | 46 ++++++++++++++++ .../multisource/MultiSourceMapperTest.java | 45 ++++++++++++++++ 3 files changed, 119 insertions(+), 26 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/multisource/MultiSourceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/multisource/MultiSourceMapperTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 9bf55e731f..b226c1a050 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -512,25 +512,39 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List elements); + + Target mapFromCollectionAndPrimitive(Collection elements, int value); + + class Target { + private int value; + private Collection elements; + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + public Collection getElements() { + return elements; + } + + public void setElements(Collection elements) { + this.elements = elements; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/multisource/MultiSourceMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/multisource/MultiSourceMapperTest.java new file mode 100644 index 0000000000..c4e1eaaee4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/multisource/MultiSourceMapperTest.java @@ -0,0 +1,45 @@ +/* + * 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.multisource; + +import java.util.Collections; + +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 Filip Hrisafov + */ +public class MultiSourceMapperTest { + + @IssueKey("2005") + @ProcessorTest + @WithClasses({ + MultiSourceMapper.class + }) + void shouldBeAbleToMapFromPrimitiveAndCollectionAsMultiSource() { + MultiSourceMapper.Target target = MultiSourceMapper.INSTANCE.mapFromPrimitiveAndCollection( + 10, + Collections.singleton( "test" ) + ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( 10 ); + assertThat( target.getElements() ).containsExactly( "test" ); + + target = MultiSourceMapper.INSTANCE.mapFromCollectionAndPrimitive( + Collections.singleton( "otherTest" ), + 20 + ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( 20 ); + assertThat( target.getElements() ).containsExactly( "otherTest" ); + } +} From 13bc0c023c82e40f6cb14b41f5fba462859bd308 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 20 Nov 2021 08:48:08 +0100 Subject: [PATCH 215/597] #2553 Support source property paths for maps --- .../ap/internal/model/BeanMappingMethod.java | 23 ++--- .../model/NestedPropertyMappingMethod.java | 22 +++-- .../NestedTargetPropertyMappingHolder.java | 2 +- .../model/beanmapping/SourceReference.java | 84 ++++--------------- .../ap/internal/model/common/Type.java | 50 +++++++++++ .../ap/test/frommap/FromMapMappingTest.java | 14 ++++ ...FromMapAndNestedMapWithDefinedMapping.java | 57 +++++++++++++ 7 files changed, 162 insertions(+), 90 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedMapWithDefinedMapping.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 0b299067f5..37dec16410 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -504,7 +504,7 @@ private void handleUnprocessedDefinedTargets() { .build(); Accessor targetPropertyReadAccessor = - method.getResultType().getPropertyReadAccessors().get( propertyName ); + method.getResultType().getReadAccessor( propertyName ); MappingReferences mappingRefs = extractMappingReferences( propertyName, true ); PropertyMapping propertyMapping = new PropertyMappingBuilder() .mappingContext( ctx ) @@ -1047,7 +1047,7 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTyp } Accessor targetWriteAccessor = unprocessedTargetProperties.get( targetPropertyName ); - Accessor targetReadAccessor = resultTypeToMap.getPropertyReadAccessors().get( targetPropertyName ); + Accessor targetReadAccessor = resultTypeToMap.getReadAccessor( targetPropertyName ); if ( targetWriteAccessor == null ) { if ( targetReadAccessor == null ) { @@ -1389,7 +1389,7 @@ private void applyPropertyNameBasedMapping(List sourceReference } Accessor targetPropertyReadAccessor = - method.getResultType().getPropertyReadAccessors().get( targetPropertyName ); + method.getResultType().getReadAccessor( targetPropertyName ); MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false ); PropertyMapping propertyMapping = new PropertyMappingBuilder().mappingContext( ctx ) .sourceMethod( method ) @@ -1432,7 +1432,7 @@ private void applyParameterNameBasedMapping() { .build(); Accessor targetPropertyReadAccessor = - method.getResultType().getPropertyReadAccessors().get( targetProperty.getKey() ); + method.getResultType().getReadAccessor( targetProperty.getKey() ); MappingReferences mappingRefs = extractMappingReferences( targetProperty.getKey(), false ); PropertyMapping propertyMapping = new PropertyMappingBuilder() .mappingContext( ctx ) @@ -1473,22 +1473,11 @@ private SourceReference getSourceRefByTargetName(Parameter sourceParameter, Stri return sourceRef; } - if ( sourceParameter.getType().isMapType() ) { - List typeParameters = sourceParameter.getType().getTypeParameters(); - if ( typeParameters.size() == 2 && typeParameters.get( 0 ).isString() ) { - return SourceReference.fromMapSource( - new String[] { targetPropertyName }, - sourceParameter - ); - } - } - - Accessor sourceReadAccessor = - sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName ); + Accessor sourceReadAccessor = sourceParameter.getType().getReadAccessor( targetPropertyName ); if ( sourceReadAccessor != null ) { // property mapping Accessor sourcePresenceChecker = - sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName ); + sourceParameter.getType().getPresenceChecker( targetPropertyName ); DeclaredType declaredSourceType = (DeclaredType) sourceParameter.getType().getTypeMirror(); Type returnType = ctx.getTypeFactory().getReturnType( declaredSourceType, sourceReadAccessor ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java index 82c02f5385..dd87f8e005 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java @@ -14,9 +14,12 @@ import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry; +import org.mapstruct.ap.internal.model.presence.SourceReferenceContainsKeyPresenceCheck; import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.ValueProvider; +import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.AccessorType; /** * This method is used to convert the nested properties as listed in propertyEntries into a method @@ -165,11 +168,20 @@ public static class SafePropertyEntry { public SafePropertyEntry(PropertyEntry entry, String safeName, String previousPropertyName) { this.safeName = safeName; this.readAccessorName = ValueProvider.of( entry.getReadAccessor() ).getValue(); - if ( entry.getPresenceChecker() != null ) { - this.presenceChecker = new SourceReferenceMethodPresenceCheck( - previousPropertyName, - entry.getPresenceChecker().getSimpleName() - ); + Accessor presenceChecker = entry.getPresenceChecker(); + if ( presenceChecker != null ) { + if ( presenceChecker.getAccessorType() == AccessorType.MAP_CONTAINS ) { + this.presenceChecker = new SourceReferenceContainsKeyPresenceCheck( + previousPropertyName, + presenceChecker.getSimpleName() + ); + } + else { + this.presenceChecker = new SourceReferenceMethodPresenceCheck( + previousPropertyName, + presenceChecker.getSimpleName() + ); + } } else { this.presenceChecker = null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index 254ba62871..402c2447b2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -642,7 +642,7 @@ private PropertyMapping createPropertyMappingForNestedTarget(MappingReferences m boolean forceUpdateMethod) { Accessor targetWriteAccessor = targetPropertiesWriteAccessors.get( targetPropertyName ); - Accessor targetReadAccessor = targetType.getPropertyReadAccessors().get( targetPropertyName ); + Accessor targetReadAccessor = targetType.getReadAccessor( targetPropertyName ); if ( targetWriteAccessor == null ) { Set readAccessors = targetType.getPropertyReadAccessors().keySet(); String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index 15b10b5782..47f4830838 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -7,16 +7,13 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; @@ -27,8 +24,6 @@ import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.MapValueAccessor; -import org.mapstruct.ap.internal.util.accessor.MapValuePresenceChecker; import static org.mapstruct.ap.internal.model.beanmapping.PropertyEntry.forSourceReference; import static org.mapstruct.ap.internal.util.Collections.last; @@ -51,32 +46,13 @@ *

  • {@code propertyEntries[1]} will describe {@code propB}
  • * * - * After building, {@link #isValid()} will return true when when no problems are detected during building. + * After building, {@link #isValid()} will return true when no problems are detected during building. * * @author Sjaak Derksen + * @author Filip Hrisafov */ public class SourceReference extends AbstractReference { - public static SourceReference fromMapSource(String[] segments, Parameter parameter) { - Type parameterType = parameter.getType(); - Type valueType = parameterType.getTypeParameters().get( 1 ); - - TypeElement typeElement = parameterType.getTypeElement(); - TypeMirror typeMirror = valueType.getTypeMirror(); - String simpleName = String.join( ".", segments ); - - MapValueAccessor mapValueAccessor = new MapValueAccessor( typeElement, typeMirror, simpleName ); - MapValuePresenceChecker mapValuePresenceChecker = new MapValuePresenceChecker( - typeElement, - typeMirror, - simpleName - ); - List entries = Collections.singletonList( - PropertyEntry.forSourceReference( segments, mapValueAccessor, mapValuePresenceChecker, valueType ) - ); - return new SourceReference( parameter, entries, true ); - } - /** * Builds a {@link SourceReference} from an {@code @Mappping}. */ @@ -173,11 +149,6 @@ public SourceReference build() { * @return the source reference */ private SourceReference buildFromSingleSourceParameters(String[] segments, Parameter parameter) { - - if ( canBeTreatedAsMapSourceType( parameter.getType() ) ) { - return fromMapSource( segments, parameter ); - } - boolean foundEntryMatch; String[] propertyNames = segments; @@ -214,14 +185,6 @@ private SourceReference buildFromSingleSourceParameters(String[] segments, Param */ private SourceReference buildFromMultipleSourceParameters(String[] segments, Parameter parameter) { - if (parameter != null && canBeTreatedAsMapSourceType( parameter.getType() )) { - String[] propertyNames = new String[0]; - if ( segments.length > 1 ) { - propertyNames = Arrays.copyOfRange( segments, 1, segments.length ); - } - return fromMapSource( propertyNames, parameter ); - } - boolean foundEntryMatch; String[] propertyNames = new String[0]; @@ -244,17 +207,8 @@ private SourceReference buildFromMultipleSourceParameters(String[] segments, Par return new SourceReference( parameter, entries, foundEntryMatch ); } - private boolean canBeTreatedAsMapSourceType(Type type) { - if ( !type.isMapType() ) { - return false; - } - - List typeParameters = type.getTypeParameters(); - return typeParameters.size() == 2 && typeParameters.get( 0 ).isString(); - } - /** - * When there are more than one source parameters, the first segment name of the propery + * When there are more than one source parameters, the first segment name of the property * needs to match the parameter name to avoid ambiguity * * consider: {@code Target map( Source1 source1, Source2 source2 )} @@ -356,24 +310,20 @@ private List matchWithSourceAccessorTypes(Type type, String[] ent Type newType = type; for ( int i = 0; i < entryNames.length; i++ ) { boolean matchFound = false; - Map sourceReadAccessors = newType.getPropertyReadAccessors(); - Map sourcePresenceCheckers = newType.getPropertyPresenceCheckers(); - - for ( Map.Entry getter : sourceReadAccessors.entrySet() ) { - if ( getter.getKey().equals( entryNames[i] ) ) { - newType = typeFactory.getReturnType( - (DeclaredType) newType.getTypeMirror(), - getter.getValue() - ); - sourceEntries.add( forSourceReference( - Arrays.copyOf( entryNames, i + 1 ), - getter.getValue(), - sourcePresenceCheckers.get( entryNames[i] ), - newType - ) ); - matchFound = true; - break; - } + Accessor readAccessor = newType.getReadAccessor( entryNames[i] ); + if ( readAccessor != null ) { + Accessor presenceChecker = newType.getPresenceChecker( entryNames[i] ); + newType = typeFactory.getReturnType( + (DeclaredType) newType.getTypeMirror(), + readAccessor + ); + sourceEntries.add( forSourceReference( + Arrays.copyOf( entryNames, i + 1 ), + readAccessor, + presenceChecker, + newType + ) ); + matchFound = true; } if ( !matchFound ) { break; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 00bcae371c..4abec925cf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -48,6 +48,8 @@ import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; +import org.mapstruct.ap.internal.util.accessor.MapValueAccessor; +import org.mapstruct.ap.internal.util.accessor.MapValuePresenceChecker; import static org.mapstruct.ap.internal.util.Collections.first; @@ -60,6 +62,7 @@ * through {@link TypeFactory}. * * @author Gunnar Morling + * @author Filip Hrisafov */ public class Type extends ModelElement implements Comparable { @@ -308,6 +311,17 @@ public boolean isMapType() { return isMapType; } + private boolean hasStringMapSignature() { + if ( isMapType() ) { + List typeParameters = getTypeParameters(); + if ( typeParameters.size() == 2 && typeParameters.get( 0 ).isString() ) { + return true; + } + } + + return false; + } + public boolean isCollectionOrMapType() { return isCollectionType || isMapType; } @@ -597,6 +611,42 @@ public Type asRawType() { } } + public Accessor getReadAccessor(String propertyName) { + if ( hasStringMapSignature() ) { + ExecutableElement getMethod = getAllMethods() + .stream() + .filter( m -> m.getSimpleName().contentEquals( "get" ) ) + .filter( m -> m.getParameters().size() == 1 ) + .findAny() + .orElse( null ); + return new MapValueAccessor( getMethod, typeParameters.get( 1 ).getTypeMirror(), propertyName ); + } + + Map readAccessors = getPropertyReadAccessors(); + + return readAccessors.get( propertyName ); + } + + public Accessor getPresenceChecker(String propertyName) { + if ( hasStringMapSignature() ) { + ExecutableElement containsKeyMethod = getAllMethods() + .stream() + .filter( m -> m.getSimpleName().contentEquals( "containsKey" ) ) + .filter( m -> m.getParameters().size() == 1 ) + .findAny() + .orElse( null ); + + return new MapValuePresenceChecker( + containsKeyMethod, + typeParameters.get( 1 ).getTypeMirror(), + propertyName + ); + } + + Map presenceCheckers = getPropertyPresenceCheckers(); + return presenceCheckers.get( propertyName ); + } + /** * getPropertyReadAccessors * diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/FromMapMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/FromMapMappingTest.java index 548aecc1ec..bcb78d8dcc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/frommap/FromMapMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/FromMapMappingTest.java @@ -261,6 +261,20 @@ void shouldMapFromNestedMap() { assertThat( target.getNestedTarget().getStringFromNestedMap() ).isEqualTo( "valueFromNestedMap" ); } + @IssueKey("2553") + @ProcessorTest + @WithClasses(MapToBeanFromMapAndNestedMapWithDefinedMapping.class) + void shouldMapFromNestedMapWithDefinedMapping() { + + MapToBeanFromMapAndNestedMapWithDefinedMapping.Source source = + new MapToBeanFromMapAndNestedMapWithDefinedMapping.Source(); + MapToBeanFromMapAndNestedMapWithDefinedMapping.Target target = + MapToBeanFromMapAndNestedMapWithDefinedMapping.INSTANCE.toTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getNested() ).isEqualTo( "valueFromNestedMap" ); + } + @ProcessorTest @WithClasses(ObjectMapToBeanWithQualifierMapper.class) void shouldUseObjectQualifiedMethod() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedMapWithDefinedMapping.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedMapWithDefinedMapping.java new file mode 100644 index 0000000000..2203836ea6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanFromMapAndNestedMapWithDefinedMapping.java @@ -0,0 +1,57 @@ +/* + * 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.frommap; + +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface MapToBeanFromMapAndNestedMapWithDefinedMapping { + + MapToBeanFromMapAndNestedMapWithDefinedMapping INSTANCE = Mappers.getMapper( + MapToBeanFromMapAndNestedMapWithDefinedMapping.class ); + + @Mapping(target = "nested", source = "nestedTarget.nested") + Target toTarget(Source source); + + class Source { + + private Map nestedTarget = new HashMap<>(); + + public Map getNestedTarget() { + return nestedTarget; + } + + public void setNestedTarget(Map nestedTarget) { + this.nestedTarget = nestedTarget; + } + + public Source() { + nestedTarget.put( "nested", "valueFromNestedMap" ); + } + } + + class Target { + + private String nested; + + public String getNested() { + return nested; + } + + public void setNested(String nested) { + this.nested = nested; + } + } + +} From 754aaf2ef4a15e7956abb4178c4e79c3d821882d Mon Sep 17 00:00:00 2001 From: dersvenhesse Date: Sun, 21 Nov 2021 22:01:09 +0100 Subject: [PATCH 216/597] [DOCS] Fixed reference variable --- .../src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 2d2b930114..370c09e249 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -696,7 +696,7 @@ public class CustomerMapperImpl implements CustomerMapper { customer.setId( Integer.parseInt( map.get( "id" ) ) ); } if ( map.containsKey( "customerName" ) ) { - customer.setName( source.get( "customerName" ) ); + customer.setName( map.get( "customerName" ) ); } // ... } From 00df0bc3d0ab347fb79d726c3b19417b04b503c2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 3 Dec 2021 19:13:41 +0100 Subject: [PATCH 217/597] #2680 Refactor accessors Split the `PresenceCheck`s accessor from the current `Accessor`. Introduce a `ReadAccessor` that would allow us to more easily implement certain things. Remove `MAP_GET` and `MAP_CONTAINS` from the AccessorType and use the new refactored mechanism --- .../ap/internal/model/BeanMappingMethod.java | 21 +++--- .../model/NestedPropertyMappingMethod.java | 29 +++------ .../NestedTargetPropertyMappingHolder.java | 3 +- .../ap/internal/model/PropertyMapping.java | 42 +++++------- .../model/beanmapping/PropertyEntry.java | 18 +++--- .../model/beanmapping/SourceReference.java | 19 +++--- .../ap/internal/model/common/Type.java | 64 +++++++++---------- ...urceReferenceContainsKeyPresenceCheck.java | 59 ----------------- ...nceCheck.java => SuffixPresenceCheck.java} | 20 +++--- .../mapstruct/ap/internal/util/Filters.java | 25 +++----- .../ap/internal/util/ValueProvider.java | 58 ----------------- .../internal/util/accessor/AccessorType.java | 5 +- .../util/accessor/DelegateAccessor.java | 48 ++++++++++++++ .../util/accessor/MapValueAccessor.java | 9 ++- .../accessor/MapValuePresenceChecker.java | 55 ---------------- .../util/accessor/PresenceCheckAccessor.java | 29 +++++++++ .../internal/util/accessor/ReadAccessor.java | 36 +++++++++++ .../util/accessor/ReadDelegateAccessor.java | 17 +++++ ...ourceReferenceContainsKeyPresenceCheck.ftl | 9 --- ...senceCheck.ftl => SuffixPresenceCheck.ftl} | 4 +- 20 files changed, 251 insertions(+), 319 deletions(-) delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.java rename processor/src/main/java/org/mapstruct/ap/internal/model/presence/{SourceReferenceMethodPresenceCheck.java => SuffixPresenceCheck.java} (66%) delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/DelegateAccessor.java delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValuePresenceChecker.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/PresenceCheckAccessor.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadDelegateAccessor.java delete mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.ftl rename processor/src/main/resources/org/mapstruct/ap/internal/model/presence/{SourceReferenceMethodPresenceCheck.ftl => SuffixPresenceCheck.ftl} (72%) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 37dec16410..bdfd8cf54a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -64,6 +64,8 @@ import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; import org.mapstruct.ap.internal.util.accessor.ParameterElementAccessor; +import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; +import org.mapstruct.ap.internal.util.accessor.ReadAccessor; import static org.mapstruct.ap.internal.model.beanmapping.MappingReferences.forSourceMethod; import static org.mapstruct.ap.internal.util.Collections.first; @@ -257,9 +259,9 @@ else if ( !method.isUpdateMethod() ) { continue; } - Map readAccessors = sourceParameter.getType().getPropertyReadAccessors(); + Map readAccessors = sourceParameter.getType().getPropertyReadAccessors(); - for ( Entry entry : readAccessors.entrySet() ) { + for ( Entry entry : readAccessors.entrySet() ) { unprocessedSourceProperties.put( entry.getKey(), entry.getValue() ); } } @@ -503,7 +505,7 @@ private void handleUnprocessedDefinedTargets() { .name( propertyName ) .build(); - Accessor targetPropertyReadAccessor = + ReadAccessor targetPropertyReadAccessor = method.getResultType().getReadAccessor( propertyName ); MappingReferences mappingRefs = extractMappingReferences( propertyName, true ); PropertyMapping propertyMapping = new PropertyMappingBuilder() @@ -1047,7 +1049,7 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTyp } Accessor targetWriteAccessor = unprocessedTargetProperties.get( targetPropertyName ); - Accessor targetReadAccessor = resultTypeToMap.getReadAccessor( targetPropertyName ); + ReadAccessor targetReadAccessor = resultTypeToMap.getReadAccessor( targetPropertyName ); if ( targetWriteAccessor == null ) { if ( targetReadAccessor == null ) { @@ -1388,7 +1390,7 @@ private void applyPropertyNameBasedMapping(List sourceReference continue; } - Accessor targetPropertyReadAccessor = + ReadAccessor targetPropertyReadAccessor = method.getResultType().getReadAccessor( targetPropertyName ); MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false ); PropertyMapping propertyMapping = new PropertyMappingBuilder().mappingContext( ctx ) @@ -1431,7 +1433,7 @@ private void applyParameterNameBasedMapping() { .name( targetProperty.getKey() ) .build(); - Accessor targetPropertyReadAccessor = + ReadAccessor targetPropertyReadAccessor = method.getResultType().getReadAccessor( targetProperty.getKey() ); MappingReferences mappingRefs = extractMappingReferences( targetProperty.getKey(), false ); PropertyMapping propertyMapping = new PropertyMappingBuilder() @@ -1453,7 +1455,8 @@ private void applyParameterNameBasedMapping() { // The source parameter was directly mapped so ignore all of its source properties completely if ( !sourceParameter.getType().isPrimitive() && !sourceParameter.getType().isArrayType() ) { // We explicitly ignore source properties from primitives or array types - Map readAccessors = sourceParameter.getType().getPropertyReadAccessors(); + Map readAccessors = sourceParameter.getType() + .getPropertyReadAccessors(); for ( String sourceProperty : readAccessors.keySet() ) { unprocessedSourceProperties.remove( sourceProperty ); } @@ -1473,10 +1476,10 @@ private SourceReference getSourceRefByTargetName(Parameter sourceParameter, Stri return sourceRef; } - Accessor sourceReadAccessor = sourceParameter.getType().getReadAccessor( targetPropertyName ); + ReadAccessor sourceReadAccessor = sourceParameter.getType().getReadAccessor( targetPropertyName ); if ( sourceReadAccessor != null ) { // property mapping - Accessor sourcePresenceChecker = + PresenceCheckAccessor sourcePresenceChecker = sourceParameter.getType().getPresenceChecker( targetPropertyName ); DeclaredType declaredSourceType = (DeclaredType) sourceParameter.getType().getTypeMirror(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java index dd87f8e005..96004cb474 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java @@ -10,16 +10,13 @@ import java.util.Objects; import java.util.Set; +import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.beanmapping.PropertyEntry; -import org.mapstruct.ap.internal.model.presence.SourceReferenceContainsKeyPresenceCheck; -import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck; +import org.mapstruct.ap.internal.model.presence.SuffixPresenceCheck; import org.mapstruct.ap.internal.util.Strings; -import org.mapstruct.ap.internal.util.ValueProvider; -import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.AccessorType; +import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; /** * This method is used to convert the nested properties as listed in propertyEntries into a method @@ -167,21 +164,13 @@ public static class SafePropertyEntry { public SafePropertyEntry(PropertyEntry entry, String safeName, String previousPropertyName) { this.safeName = safeName; - this.readAccessorName = ValueProvider.of( entry.getReadAccessor() ).getValue(); - Accessor presenceChecker = entry.getPresenceChecker(); + this.readAccessorName = entry.getReadAccessor().getReadValueSource(); + PresenceCheckAccessor presenceChecker = entry.getPresenceChecker(); if ( presenceChecker != null ) { - if ( presenceChecker.getAccessorType() == AccessorType.MAP_CONTAINS ) { - this.presenceChecker = new SourceReferenceContainsKeyPresenceCheck( - previousPropertyName, - presenceChecker.getSimpleName() - ); - } - else { - this.presenceChecker = new SourceReferenceMethodPresenceCheck( - previousPropertyName, - presenceChecker.getSimpleName() - ); - } + this.presenceChecker = new SuffixPresenceCheck( + previousPropertyName, + presenceChecker.getPresenceCheckSuffix() + ); } else { this.presenceChecker = null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index 402c2447b2..61e3926047 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -21,6 +21,7 @@ import org.mapstruct.ap.internal.model.beanmapping.SourceReference; import org.mapstruct.ap.internal.model.beanmapping.TargetReference; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.util.accessor.ReadAccessor; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; @@ -642,7 +643,7 @@ private PropertyMapping createPropertyMappingForNestedTarget(MappingReferences m boolean forceUpdateMethod) { Accessor targetWriteAccessor = targetPropertiesWriteAccessors.get( targetPropertyName ); - Accessor targetReadAccessor = targetType.getReadAccessor( targetPropertyName ); + ReadAccessor targetReadAccessor = targetType.getReadAccessor( targetPropertyName ); if ( targetWriteAccessor == null ) { Set readAccessors = targetType.getPropertyReadAccessors().keySet(); String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 1d902e54e7..c83f856587 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -37,8 +37,7 @@ import org.mapstruct.ap.internal.model.presence.AllPresenceChecksPresenceCheck; import org.mapstruct.ap.internal.model.presence.JavaExpressionPresenceCheck; import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; -import org.mapstruct.ap.internal.model.presence.SourceReferenceContainsKeyPresenceCheck; -import org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck; +import org.mapstruct.ap.internal.model.presence.SuffixPresenceCheck; import org.mapstruct.ap.internal.model.source.DelegatingOptions; import org.mapstruct.ap.internal.model.source.MappingControl; import org.mapstruct.ap.internal.model.source.MappingOptions; @@ -48,9 +47,9 @@ import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.NativeTypes; import org.mapstruct.ap.internal.util.Strings; -import org.mapstruct.ap.internal.util.ValueProvider; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; +import org.mapstruct.ap.internal.util.accessor.ReadAccessor; import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; @@ -73,7 +72,7 @@ public class PropertyMapping extends ModelElement { private final String name; private final String sourceBeanName; private final String targetWriteAccessorName; - private final ValueProvider targetReadAccessorProvider; + private final ReadAccessor targetReadAccessorProvider; private final Type targetType; private final Assignment assignment; private final Set dependsOn; @@ -87,7 +86,7 @@ private static class MappingBuilderBase> extends protected AccessorType targetWriteAccessorType; protected Type targetType; protected BuilderType targetBuilderType; - protected Accessor targetReadAccessor; + protected ReadAccessor targetReadAccessor; protected String targetPropertyName; protected String sourcePropertyName; @@ -103,7 +102,7 @@ public T sourceMethod(Method sourceMethod) { return super.method( sourceMethod ); } - public T target(String targetPropertyName, Accessor targetReadAccessor, Accessor targetWriteAccessor) { + public T target(String targetPropertyName, ReadAccessor targetReadAccessor, Accessor targetWriteAccessor) { this.targetPropertyName = targetPropertyName; this.targetReadAccessor = targetReadAccessor; this.targetWriteAccessor = targetWriteAccessor; @@ -290,7 +289,7 @@ else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.get targetPropertyName, rightHandSide.getSourceParameterName(), targetWriteAccessor.getSimpleName(), - ValueProvider.of( targetReadAccessor ), + targetReadAccessor, targetType, assignment, dependsOn, @@ -567,7 +566,7 @@ private SourceRHS getSourceRHS( SourceReference sourceReference ) { } // simple property else if ( !sourceReference.isNested() ) { - String sourceRef = sourceParam.getName() + "." + ValueProvider.of( propertyEntry.getReadAccessor() ); + String sourceRef = sourceParam.getName() + "." + propertyEntry.getReadAccessor().getReadValueSource(); SourceRHS sourceRHS = new SourceRHS( sourceParam.getName(), sourceRef, @@ -660,28 +659,21 @@ private PresenceCheck getSourcePresenceCheckerRef(SourceReference sourceReferenc // in the forged method? PropertyEntry propertyEntry = sourceReference.getShallowestProperty(); if ( propertyEntry.getPresenceChecker() != null ) { - if (propertyEntry.getPresenceChecker().getAccessorType() == AccessorType.MAP_CONTAINS ) { - return new SourceReferenceContainsKeyPresenceCheck( - sourceParam.getName(), - propertyEntry.getPresenceChecker().getSimpleName() - ); - } - List presenceChecks = new ArrayList<>(); - presenceChecks.add( new SourceReferenceMethodPresenceCheck( + presenceChecks.add( new SuffixPresenceCheck( sourceParam.getName(), - propertyEntry.getPresenceChecker().getSimpleName() + propertyEntry.getPresenceChecker().getPresenceCheckSuffix() ) ); String variableName = sourceParam.getName() + "." - + propertyEntry.getReadAccessor().getSimpleName() + "()"; + + propertyEntry.getReadAccessor().getReadValueSource(); for (int i = 1; i < sourceReference.getPropertyEntries().size(); i++) { PropertyEntry entry = sourceReference.getPropertyEntries().get( i ); if (entry.getPresenceChecker() != null && entry.getReadAccessor() != null) { presenceChecks.add( new NullPresenceCheck( variableName ) ); - presenceChecks.add( new SourceReferenceMethodPresenceCheck( + presenceChecks.add( new SuffixPresenceCheck( variableName, - entry.getPresenceChecker().getSimpleName() + entry.getPresenceChecker().getPresenceCheckSuffix() ) ); variableName = variableName + "." + entry.getReadAccessor().getSimpleName() + "()"; } @@ -992,7 +984,7 @@ else if ( errorMessageDetails == null ) { return new PropertyMapping( targetPropertyName, targetWriteAccessor.getSimpleName(), - ValueProvider.of( targetReadAccessor ), + targetReadAccessor, targetType, assignment, dependsOn, @@ -1058,7 +1050,7 @@ public PropertyMapping build() { return new PropertyMapping( targetPropertyName, targetWriteAccessor.getSimpleName(), - ValueProvider.of( targetReadAccessor ), + targetReadAccessor, targetType, assignment, dependsOn, @@ -1071,7 +1063,7 @@ public PropertyMapping build() { // Constructor for creating mappings of constant expressions. private PropertyMapping(String name, String targetWriteAccessorName, - ValueProvider targetReadAccessorProvider, + ReadAccessor targetReadAccessorProvider, Type targetType, Assignment propertyAssignment, Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) { this( name, null, targetWriteAccessorName, targetReadAccessorProvider, @@ -1081,7 +1073,7 @@ private PropertyMapping(String name, String targetWriteAccessorName, } private PropertyMapping(String name, String sourceBeanName, String targetWriteAccessorName, - ValueProvider targetReadAccessorProvider, Type targetType, + ReadAccessor targetReadAccessorProvider, Type targetType, Assignment assignment, Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) { this.name = name; @@ -1112,7 +1104,7 @@ public String getTargetWriteAccessorName() { } public String getTargetReadAccessorName() { - return targetReadAccessorProvider == null ? null : targetReadAccessorProvider.getValue(); + return targetReadAccessorProvider == null ? null : targetReadAccessorProvider.getReadValueSource(); } public Type getTargetType() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java index 8ac5e46671..cb6cd8af79 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java @@ -9,7 +9,8 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.util.Strings; -import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; +import org.mapstruct.ap.internal.util.accessor.ReadAccessor; /** * A PropertyEntry contains information on the name, readAccessor and presenceCheck (for source) @@ -18,8 +19,8 @@ public class PropertyEntry { private final String[] fullName; - private final Accessor readAccessor; - private final Accessor presenceChecker; + private final ReadAccessor readAccessor; + private final PresenceCheckAccessor presenceChecker; private final Type type; /** @@ -29,7 +30,8 @@ public class PropertyEntry { * @param readAccessor * @param type */ - private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor presenceChecker, Type type) { + private PropertyEntry(String[] fullName, ReadAccessor readAccessor, PresenceCheckAccessor presenceChecker, + Type type) { this.fullName = fullName; this.readAccessor = readAccessor; this.presenceChecker = presenceChecker; @@ -45,8 +47,8 @@ private PropertyEntry(String[] fullName, Accessor readAccessor, Accessor presenc * @param type type of the property * @return the property entry for given parameters. */ - public static PropertyEntry forSourceReference(String[] name, Accessor readAccessor, - Accessor presenceChecker, Type type) { + public static PropertyEntry forSourceReference(String[] name, ReadAccessor readAccessor, + PresenceCheckAccessor presenceChecker, Type type) { return new PropertyEntry( name, readAccessor, presenceChecker, type ); } @@ -54,11 +56,11 @@ public String getName() { return fullName[fullName.length - 1]; } - public Accessor getReadAccessor() { + public ReadAccessor getReadAccessor() { return readAccessor; } - public Accessor getPresenceChecker() { + public PresenceCheckAccessor getPresenceChecker() { return presenceChecker; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index 47f4830838..d5c18a99f1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -23,7 +23,8 @@ import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; -import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; +import org.mapstruct.ap.internal.util.accessor.ReadAccessor; import static org.mapstruct.ap.internal.model.beanmapping.PropertyEntry.forSourceReference; import static org.mapstruct.ap.internal.util.Collections.last; @@ -310,9 +311,9 @@ private List matchWithSourceAccessorTypes(Type type, String[] ent Type newType = type; for ( int i = 0; i < entryNames.length; i++ ) { boolean matchFound = false; - Accessor readAccessor = newType.getReadAccessor( entryNames[i] ); + ReadAccessor readAccessor = newType.getReadAccessor( entryNames[i] ); if ( readAccessor != null ) { - Accessor presenceChecker = newType.getPresenceChecker( entryNames[i] ); + PresenceCheckAccessor presenceChecker = newType.getPresenceChecker( entryNames[i] ); newType = typeFactory.getReturnType( (DeclaredType) newType.getTypeMirror(), readAccessor @@ -343,8 +344,8 @@ private void reportMappingError(Message msg, Object... objects) { public static class BuilderFromProperty { private String name; - private Accessor readAccessor; - private Accessor presenceChecker; + private ReadAccessor readAccessor; + private PresenceCheckAccessor presenceChecker; private Type type; private Parameter sourceParameter; @@ -353,12 +354,12 @@ public BuilderFromProperty name(String name) { return this; } - public BuilderFromProperty readAccessor(Accessor readAccessor) { + public BuilderFromProperty readAccessor(ReadAccessor readAccessor) { this.readAccessor = readAccessor; return this; } - public BuilderFromProperty presenceChecker(Accessor presenceChecker) { + public BuilderFromProperty presenceChecker(PresenceCheckAccessor presenceChecker) { this.presenceChecker = presenceChecker; return this; } @@ -430,10 +431,10 @@ public List push(TypeFactory typeFactory, FormattingMessager me PropertyEntry deepestProperty = getDeepestProperty(); if ( deepestProperty != null ) { Type type = deepestProperty.getType(); - Map newDeepestReadAccessors = type.getPropertyReadAccessors(); + Map newDeepestReadAccessors = type.getPropertyReadAccessors(); String parameterName = getParameter().getName(); String deepestPropertyFullName = deepestProperty.getFullName(); - for ( Map.Entry newDeepestReadAccessorEntry : newDeepestReadAccessors.entrySet() ) { + for ( Map.Entry newDeepestReadAccessorEntry : newDeepestReadAccessors.entrySet() ) { // Always include the parameter name in the new full name. // Otherwise multi source parameters might be reported incorrectly String newFullName = diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 4abec925cf..ab2871c69d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -48,8 +48,10 @@ import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; +import org.mapstruct.ap.internal.util.accessor.FieldElementAccessor; import org.mapstruct.ap.internal.util.accessor.MapValueAccessor; -import org.mapstruct.ap.internal.util.accessor.MapValuePresenceChecker; +import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; +import org.mapstruct.ap.internal.util.accessor.ReadAccessor; import static org.mapstruct.ap.internal.util.Collections.first; @@ -101,8 +103,8 @@ public class Type extends ModelElement implements Comparable { private final Map notToBeImportedTypes; private Boolean isToBeImported; - private Map readAccessors = null; - private Map presenceCheckers = null; + private Map readAccessors = null; + private Map presenceCheckers = null; private List allMethods = null; private List allFields = null; @@ -611,7 +613,7 @@ public Type asRawType() { } } - public Accessor getReadAccessor(String propertyName) { + public ReadAccessor getReadAccessor(String propertyName) { if ( hasStringMapSignature() ) { ExecutableElement getMethod = getAllMethods() .stream() @@ -622,28 +624,17 @@ public Accessor getReadAccessor(String propertyName) { return new MapValueAccessor( getMethod, typeParameters.get( 1 ).getTypeMirror(), propertyName ); } - Map readAccessors = getPropertyReadAccessors(); + Map readAccessors = getPropertyReadAccessors(); return readAccessors.get( propertyName ); } - public Accessor getPresenceChecker(String propertyName) { + public PresenceCheckAccessor getPresenceChecker(String propertyName) { if ( hasStringMapSignature() ) { - ExecutableElement containsKeyMethod = getAllMethods() - .stream() - .filter( m -> m.getSimpleName().contentEquals( "containsKey" ) ) - .filter( m -> m.getParameters().size() == 1 ) - .findAny() - .orElse( null ); - - return new MapValuePresenceChecker( - containsKeyMethod, - typeParameters.get( 1 ).getTypeMirror(), - propertyName - ); + return PresenceCheckAccessor.mapContainsKey( propertyName ); } - Map presenceCheckers = getPropertyPresenceCheckers(); + Map presenceCheckers = getPropertyPresenceCheckers(); return presenceCheckers.get( propertyName ); } @@ -652,15 +643,15 @@ public Accessor getPresenceChecker(String propertyName) { * * @return an unmodifiable map of all read accessors (including 'is' for booleans), indexed by property name */ - public Map getPropertyReadAccessors() { + public Map getPropertyReadAccessors() { if ( readAccessors == null ) { - Map modifiableGetters = new LinkedHashMap<>(); + Map modifiableGetters = new LinkedHashMap<>(); - Map recordAccessors = filters.recordAccessorsIn( getRecordComponents() ); + Map recordAccessors = filters.recordAccessorsIn( getRecordComponents() ); modifiableGetters.putAll( recordAccessors ); - List getterList = filters.getterMethodsIn( getAllMethods() ); - for ( Accessor getter : getterList ) { + List getterList = filters.getterMethodsIn( getAllMethods() ); + for ( ReadAccessor getter : getterList ) { String simpleName = getter.getSimpleName(); if ( recordAccessors.containsKey( simpleName ) ) { // If there is already a record accessor that contains the simple name @@ -690,8 +681,8 @@ public Map getPropertyReadAccessors() { } } - List fieldsList = filters.fieldsIn( getAllFields() ); - for ( Accessor field : fieldsList ) { + List fieldsList = filters.fieldsIn( getAllFields(), ReadAccessor::fromField ); + for ( ReadAccessor field : fieldsList ) { String propertyName = getPropertyName( field ); // If there was no getter or is method for booleans, then resort to the field. // If a field was already added do not add it again. @@ -707,12 +698,15 @@ public Map getPropertyReadAccessors() { * * @return an unmodifiable map of all presence checkers, indexed by property name */ - public Map getPropertyPresenceCheckers() { + public Map getPropertyPresenceCheckers() { if ( presenceCheckers == null ) { - List checkerList = filters.presenceCheckMethodsIn( getAllMethods() ); - Map modifiableCheckers = new LinkedHashMap<>(); - for ( Accessor checker : checkerList ) { - modifiableCheckers.put( getPropertyName( checker ), checker ); + List checkerList = filters.presenceCheckMethodsIn( getAllMethods() ); + Map modifiableCheckers = new LinkedHashMap<>(); + for ( ExecutableElement checker : checkerList ) { + modifiableCheckers.put( + getPropertyName( checker ), + PresenceCheckAccessor.methodInvocation( checker ) + ); } presenceCheckers = Collections.unmodifiableMap( modifiableCheckers ); } @@ -841,13 +835,17 @@ private List nullSafeTypeElementListConversion(Function getAlternativeTargetAccessors() { List setterMethods = getSetters(); List readAccessors = new ArrayList<>( getPropertyReadAccessors().values() ); // All the fields are also alternative accessors - readAccessors.addAll( filters.fieldsIn( getAllFields() ) ); + readAccessors.addAll( filters.fieldsIn( getAllFields(), FieldElementAccessor::new ) ); // there could be a read accessor (field or method) for a list/map that is not present as setter. // an accessor could substitute the setter in that case and act as setter. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.java deleted file mode 100644 index d4c011a051..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.internal.model.presence; - -import java.util.Collections; -import java.util.Objects; -import java.util.Set; - -import org.mapstruct.ap.internal.model.common.ModelElement; -import org.mapstruct.ap.internal.model.common.PresenceCheck; -import org.mapstruct.ap.internal.model.common.Type; - -/** - * @author Filip Hrisafov - */ -public class SourceReferenceContainsKeyPresenceCheck extends ModelElement implements PresenceCheck { - - private final String sourceReference; - private final String propertyName; - - public SourceReferenceContainsKeyPresenceCheck(String sourceReference, String propertyName) { - this.sourceReference = sourceReference; - this.propertyName = propertyName; - } - - public String getSourceReference() { - return sourceReference; - } - - public String getPropertyName() { - return propertyName; - } - - @Override - public Set getImportTypes() { - return Collections.emptySet(); - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - if ( o == null || getClass() != o.getClass() ) { - return false; - } - SourceReferenceContainsKeyPresenceCheck that = (SourceReferenceContainsKeyPresenceCheck) o; - return Objects.equals( sourceReference, that.sourceReference ) && - Objects.equals( propertyName, that.propertyName ); - } - - @Override - public int hashCode() { - return Objects.hash( sourceReference, propertyName ); - } -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java similarity index 66% rename from processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java index 374eac0c8e..3710e97173 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java @@ -16,22 +16,22 @@ /** * @author Filip Hrisafov */ -public class SourceReferenceMethodPresenceCheck extends ModelElement implements PresenceCheck { +public class SuffixPresenceCheck extends ModelElement implements PresenceCheck { private final String sourceReference; - private final String methodName; + private final String suffix; - public SourceReferenceMethodPresenceCheck(String sourceReference, String methodName) { + public SuffixPresenceCheck(String sourceReference, String suffix) { this.sourceReference = sourceReference; - this.methodName = methodName; + this.suffix = suffix; } public String getSourceReference() { return sourceReference; } - public String getMethodName() { - return methodName; + public String getSuffix() { + return suffix; } @Override @@ -47,13 +47,13 @@ public boolean equals(Object o) { if ( o == null || getClass() != o.getClass() ) { return false; } - SourceReferenceMethodPresenceCheck that = (SourceReferenceMethodPresenceCheck) o; - return Objects.equals( sourceReference, that.sourceReference ) && - Objects.equals( methodName, that.methodName ); + SuffixPresenceCheck that = (SuffixPresenceCheck) o; + return Objects.equals( sourceReference, that.sourceReference ) + && Objects.equals( suffix, that.suffix ); } @Override public int hashCode() { - return Objects.hash( sourceReference, methodName ); + return Objects.hash( sourceReference, suffix ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java index 9cb923b3c8..123f72832b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java @@ -12,6 +12,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -23,12 +24,10 @@ import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; -import org.mapstruct.ap.internal.util.accessor.FieldElementAccessor; +import org.mapstruct.ap.internal.util.accessor.ReadAccessor; import static org.mapstruct.ap.internal.util.Collections.first; import static org.mapstruct.ap.internal.util.accessor.AccessorType.ADDER; -import static org.mapstruct.ap.internal.util.accessor.AccessorType.GETTER; -import static org.mapstruct.ap.internal.util.accessor.AccessorType.PRESENCE_CHECKER; import static org.mapstruct.ap.internal.util.accessor.AccessorType.SETTER; /** @@ -68,10 +67,10 @@ public Filters(AccessorNamingUtils accessorNaming, TypeUtils typeUtils, TypeMirr this.typeMirror = typeMirror; } - public List getterMethodsIn(List elements) { + public List getterMethodsIn(List elements) { return elements.stream() .filter( accessorNaming::isGetterMethod ) - .map( method -> new ExecutableElementAccessor( method, getReturnType( method ), GETTER ) ) + .map( method -> ReadAccessor.fromGetter( method, getReturnType( method ) ) ) .collect( Collectors.toCollection( LinkedList::new ) ); } @@ -89,21 +88,18 @@ public List recordComponentsIn(TypeElement typeElement) { } } - public Map recordAccessorsIn(Collection recordComponents) { + public Map recordAccessorsIn(Collection recordComponents) { if ( RECORD_COMPONENT_ACCESSOR_METHOD == null ) { return java.util.Collections.emptyMap(); } try { - Map recordAccessors = new LinkedHashMap<>(); + Map recordAccessors = new LinkedHashMap<>(); for ( Element recordComponent : recordComponents ) { ExecutableElement recordExecutableElement = (ExecutableElement) RECORD_COMPONENT_ACCESSOR_METHOD.invoke( recordComponent ); recordAccessors.put( recordComponent.getSimpleName().toString(), - new ExecutableElementAccessor( recordExecutableElement, - getReturnType( recordExecutableElement ), - GETTER - ) + ReadAccessor.fromGetter( recordExecutableElement, getReturnType( recordExecutableElement ) ) ); } @@ -118,17 +114,16 @@ private TypeMirror getReturnType(ExecutableElement executableElement) { return getWithinContext( executableElement ).getReturnType(); } - public List fieldsIn(List accessors) { + public List fieldsIn(List accessors, Function creator) { return accessors.stream() .filter( Fields::isFieldAccessor ) - .map( FieldElementAccessor::new ) + .map( creator ) .collect( Collectors.toCollection( LinkedList::new ) ); } - public List presenceCheckMethodsIn(List elements) { + public List presenceCheckMethodsIn(List elements) { return elements.stream() .filter( accessorNaming::isPresenceCheckMethod ) - .map( method -> new ExecutableElementAccessor( method, getReturnType( method ), PRESENCE_CHECKER ) ) .collect( Collectors.toCollection( LinkedList::new ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java deleted file mode 100644 index 16abd074dd..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/ValueProvider.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.internal.util; - -import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.AccessorType; - -/** - * This a wrapper class which provides the value that needs to be used in the models. - * - * It is used to provide the read value for a difference kind of {@link Accessor}. - * - * @author Filip Hrisafov - */ -public class ValueProvider { - - private final String value; - - private ValueProvider(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - @Override - public String toString() { - return value; - } - - /** - * Creates a {@link ValueProvider} from the provided {@code accessor}. The base value is - * {@link Accessor#getSimpleName()}. If the {@code accessor} is for an executable, then {@code ()} is - * appended. - * - * @param accessor that provides the value - * - * @return a {@link ValueProvider} tha provides a read value for the {@code accessor} - */ - public static ValueProvider of(Accessor accessor) { - if ( accessor == null ) { - return null; - } - String value = accessor.getSimpleName(); - if (accessor.getAccessorType() == AccessorType.MAP_GET ) { - value = "get( \"" + value + "\" )"; - return new ValueProvider( value ); - } - if ( !accessor.getAccessorType().isFieldAssignment() ) { - value += "()"; - } - return new ValueProvider( value ); - } -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java index 4e3b7a4776..112c1c512d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AccessorType.java @@ -11,10 +11,7 @@ public enum AccessorType { FIELD, GETTER, SETTER, - ADDER, - MAP_GET, - MAP_CONTAINS, - PRESENCE_CHECKER; + ADDER; public boolean isFieldAssignment() { return this == FIELD || this == PARAMETER; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/DelegateAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/DelegateAccessor.java new file mode 100644 index 0000000000..9b2981d7c5 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/DelegateAccessor.java @@ -0,0 +1,48 @@ +/* + * 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.internal.util.accessor; + +import java.util.Set; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import javax.lang.model.type.TypeMirror; + +/** + * @author Filip Hrisafov + */ +public abstract class DelegateAccessor implements Accessor { + + protected final Accessor delegate; + + protected DelegateAccessor(Accessor delegate) { + this.delegate = delegate; + } + + @Override + public TypeMirror getAccessedType() { + return delegate.getAccessedType(); + } + + @Override + public String getSimpleName() { + return delegate.getSimpleName(); + } + + @Override + public Set getModifiers() { + return delegate.getModifiers(); + } + + @Override + public Element getElement() { + return delegate.getElement(); + } + + @Override + public AccessorType getAccessorType() { + return delegate.getAccessorType(); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValueAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValueAccessor.java index ab086d9b0e..7a708995cd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValueAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValueAccessor.java @@ -16,7 +16,7 @@ * * @author Christian Kosmowski */ -public class MapValueAccessor implements Accessor { +public class MapValueAccessor implements ReadAccessor { private final TypeMirror valueTypeMirror; private final String simpleName; @@ -50,6 +50,11 @@ public Element getElement() { @Override public AccessorType getAccessorType() { - return AccessorType.MAP_GET; + return AccessorType.GETTER; + } + + @Override + public String getReadValueSource() { + return "get( \"" + getSimpleName() + "\" )"; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValuePresenceChecker.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValuePresenceChecker.java deleted file mode 100644 index 3f72ed86c1..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/MapValuePresenceChecker.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.internal.util.accessor; - -import java.util.Collections; -import java.util.Set; -import javax.lang.model.element.Element; -import javax.lang.model.element.Modifier; -import javax.lang.model.type.TypeMirror; - -/** - * An {@link Accessor} that wraps a Map value. - * - * @author Christian Kosmowski - */ -public class MapValuePresenceChecker implements Accessor { - - private final Element element; - private final TypeMirror valueTypeMirror; - private final String simpleName; - - public MapValuePresenceChecker(Element element, TypeMirror valueTypeMirror, String simpleName) { - this.element = element; - this.valueTypeMirror = valueTypeMirror; - this.simpleName = simpleName; - } - - @Override - public TypeMirror getAccessedType() { - return valueTypeMirror; - } - - @Override - public String getSimpleName() { - return this.simpleName; - } - - @Override - public Set getModifiers() { - return Collections.emptySet(); - } - - @Override - public Element getElement() { - return this.element; - } - - @Override - public AccessorType getAccessorType() { - return AccessorType.MAP_CONTAINS; - } -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/PresenceCheckAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/PresenceCheckAccessor.java new file mode 100644 index 0000000000..cc974b939f --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/PresenceCheckAccessor.java @@ -0,0 +1,29 @@ +/* + * 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.internal.util.accessor; + +import javax.lang.model.element.ExecutableElement; + +/** + * @author Filip Hrisafov + */ +public interface PresenceCheckAccessor { + + String getPresenceCheckSuffix(); + + static PresenceCheckAccessor methodInvocation(ExecutableElement element) { + return suffix( "." + element.getSimpleName() + "()" ); + } + + static PresenceCheckAccessor mapContainsKey(String propertyName) { + return suffix( ".containsKey( \"" + propertyName + "\" )" ); + } + + static PresenceCheckAccessor suffix(String suffix) { + return () -> suffix; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java new file mode 100644 index 0000000000..a790a3361b --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java @@ -0,0 +1,36 @@ +/* + * 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.internal.util.accessor; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; + +/** + * @author Filip Hrisafov + */ +public interface ReadAccessor extends Accessor { + + String getReadValueSource(); + + static ReadAccessor fromField(VariableElement variableElement) { + return new ReadDelegateAccessor( new FieldElementAccessor( variableElement ) ) { + @Override + public String getReadValueSource() { + return getSimpleName(); + } + }; + } + + static ReadAccessor fromGetter(ExecutableElement element, TypeMirror accessedType) { + return new ReadDelegateAccessor( new ExecutableElementAccessor( element, accessedType, AccessorType.GETTER ) ) { + @Override + public String getReadValueSource() { + return getSimpleName() + "()"; + } + }; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadDelegateAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadDelegateAccessor.java new file mode 100644 index 0000000000..608c1b21cf --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadDelegateAccessor.java @@ -0,0 +1,17 @@ +/* + * 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.internal.util.accessor; + +/** + * @author Filip Hrisafov + */ +public abstract class ReadDelegateAccessor extends DelegateAccessor implements ReadAccessor { + + protected ReadDelegateAccessor(Accessor delegate) { + super( delegate ); + } + +} diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.ftl deleted file mode 100644 index 5cffc481a1..0000000000 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceContainsKeyPresenceCheck.ftl +++ /dev/null @@ -1,9 +0,0 @@ -<#-- - - Copyright MapStruct Authors. - - Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - ---> -<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.SourceReferenceContainsKeyPresenceCheck" --> -${sourceReference}.containsKey( "${propertyName}" ) \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.ftl similarity index 72% rename from processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.ftl rename to processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.ftl index 930d5e24bc..103b4e7f9c 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SourceReferenceMethodPresenceCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.ftl @@ -5,5 +5,5 @@ Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 --> -<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.SourceReferenceMethodPresenceCheck" --> -${sourceReference}.${methodName}() \ No newline at end of file +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.SuffixPresenceCheck" --> +${sourceReference}${suffix} \ No newline at end of file From 5de813c16f19050e6cd0a8cd08dc18799a37271b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Nov 2021 13:48:24 +0100 Subject: [PATCH 218/597] #2666 Presence Check should be applied to source parameters when used in `@Mapping` --- .../ap/internal/model/PropertyMapping.java | 14 +- .../expression/ConditionalExpressionTest.java | 37 +++++ ...nalWithSourceToTargetExpressionMapper.java | 132 ++++++++++++++++++ ...itionalMethodWithSourceToTargetMapper.java | 132 ++++++++++++++++++ .../qualifier/ConditionalQualifierTest.java | 37 +++++ 5 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalWithSourceToTargetExpressionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceToTargetMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index c83f856587..f17ab9ddb1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -558,11 +558,17 @@ private SourceRHS getSourceRHS( SourceReference sourceReference ) { // parameter reference if ( propertyEntry == null ) { - return new SourceRHS( sourceParam.getName(), - sourceParam.getType(), - existingVariableNames, - sourceReference.toString() + SourceRHS sourceRHS = new SourceRHS( + sourceParam.getName(), + sourceParam.getType(), + existingVariableNames, + sourceReference.toString() ); + sourceRHS.setSourcePresenceCheckerReference( getSourcePresenceCheckerRef( + sourceReference, + sourceRHS + ) ); + return sourceRHS; } // simple property else if ( !sourceReference.isNested() ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java index 60179cdeac..42219cfb6a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java @@ -74,6 +74,43 @@ public void conditionalSimpleExpression() { assertThat( target.getValue() ).isEqualTo( 0 ); } + @ProcessorTest + @WithClasses({ + ConditionalWithSourceToTargetExpressionMapper.class + }) + @IssueKey("2666") + public void conditionalExpressionForSourceToTarget() { + ConditionalWithSourceToTargetExpressionMapper mapper = ConditionalWithSourceToTargetExpressionMapper.INSTANCE; + + ConditionalWithSourceToTargetExpressionMapper.OrderDTO orderDto = + new ConditionalWithSourceToTargetExpressionMapper.OrderDTO(); + + ConditionalWithSourceToTargetExpressionMapper.Order order = mapper.convertToOrder( orderDto ); + assertThat( order ).isNotNull(); + assertThat( order.getCustomer() ).isNull(); + + orderDto = new ConditionalWithSourceToTargetExpressionMapper.OrderDTO(); + orderDto.setCustomerName( "Tester" ); + order = mapper.convertToOrder( orderDto ); + + assertThat( order ).isNotNull(); + assertThat( order.getCustomer() ).isNotNull(); + assertThat( order.getCustomer().getName() ).isEqualTo( "Tester" ); + assertThat( order.getCustomer().getAddress() ).isNull(); + + orderDto = new ConditionalWithSourceToTargetExpressionMapper.OrderDTO(); + orderDto.setLine1( "Line 1" ); + order = mapper.convertToOrder( orderDto ); + + assertThat( order ).isNotNull(); + assertThat( order.getCustomer() ).isNotNull(); + assertThat( order.getCustomer().getName() ).isNull(); + assertThat( order.getCustomer().getAddress() ).isNotNull(); + assertThat( order.getCustomer().getAddress().getLine1() ).isEqualTo( "Line 1" ); + assertThat( order.getCustomer().getAddress().getLine2() ).isNull(); + + } + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalWithSourceToTargetExpressionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalWithSourceToTargetExpressionMapper.java new file mode 100644 index 0000000000..c32fe99e03 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalWithSourceToTargetExpressionMapper.java @@ -0,0 +1,132 @@ +/* + * 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.expression; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(imports = ConditionalWithSourceToTargetExpressionMapper.Util.class) +public interface ConditionalWithSourceToTargetExpressionMapper { + + ConditionalWithSourceToTargetExpressionMapper INSTANCE = + Mappers.getMapper( ConditionalWithSourceToTargetExpressionMapper.class ); + + @Mapping(source = "orderDTO", target = "customer", + conditionExpression = "java(Util.mapCustomerFromOrder( orderDTO ))") + Order convertToOrder(OrderDTO orderDTO); + + @Mapping(source = "customerName", target = "name") + @Mapping(source = "orderDTO", target = "address", + conditionExpression = "java(Util.mapAddressFromOrder( orderDTO ))") + Customer convertToCustomer(OrderDTO orderDTO); + + Address convertToAddress(OrderDTO orderDTO); + + class OrderDTO { + + private String customerName; + private String line1; + private String line2; + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public String getLine1() { + return line1; + } + + public void setLine1(String line1) { + this.line1 = line1; + } + + public String getLine2() { + return line2; + } + + public void setLine2(String line2) { + this.line2 = line2; + } + + } + + class Order { + + private Customer customer; + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + } + + class Customer { + private String name; + private Address address; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + } + + class Address { + + private String line1; + private String line2; + + public String getLine1() { + return line1; + } + + public void setLine1(String line1) { + this.line1 = line1; + } + + public String getLine2() { + return line2; + } + + public void setLine2(String line2) { + this.line2 = line2; + } + } + + interface Util { + + static boolean mapCustomerFromOrder(OrderDTO orderDTO) { + return orderDTO != null && ( orderDTO.getCustomerName() != null || mapAddressFromOrder( orderDTO ) ); + } + + static boolean mapAddressFromOrder(OrderDTO orderDTO) { + return orderDTO != null && ( orderDTO.getLine1() != null || orderDTO.getLine2() != null ); + } + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceToTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceToTargetMapper.java new file mode 100644 index 0000000000..35864fe62c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceToTargetMapper.java @@ -0,0 +1,132 @@ +/* + * 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.qualifier; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConditionalMethodWithSourceToTargetMapper { + + ConditionalMethodWithSourceToTargetMapper INSTANCE = + Mappers.getMapper( ConditionalMethodWithSourceToTargetMapper.class ); + + @Mapping(source = "orderDTO", target = "customer", conditionQualifiedByName = "mapCustomerFromOrder") + Order convertToOrder(OrderDTO orderDTO); + + @Mapping(source = "customerName", target = "name") + @Mapping(source = "orderDTO", target = "address", conditionQualifiedByName = "mapAddressFromOrder") + Customer convertToCustomer(OrderDTO orderDTO); + + Address convertToAddress(OrderDTO orderDTO); + + @Condition + @Named("mapCustomerFromOrder") + default boolean mapCustomerFromOrder(OrderDTO orderDTO) { + return orderDTO != null && ( orderDTO.getCustomerName() != null || mapAddressFromOrder( orderDTO ) ); + } + + @Condition + @Named("mapAddressFromOrder") + default boolean mapAddressFromOrder(OrderDTO orderDTO) { + return orderDTO != null && ( orderDTO.getLine1() != null || orderDTO.getLine2() != null ); + } + + class OrderDTO { + + private String customerName; + private String line1; + private String line2; + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public String getLine1() { + return line1; + } + + public void setLine1(String line1) { + this.line1 = line1; + } + + public String getLine2() { + return line2; + } + + public void setLine2(String line2) { + this.line2 = line2; + } + + } + + class Order { + + private Customer customer; + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + } + + class Customer { + private String name; + private Address address; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + } + + class Address { + + private String line1; + private String line2; + + public String getLine1() { + return line1; + } + + public void setLine1(String line1) { + this.line1 = line1; + } + + public String getLine2() { + return line2; + } + + public void setLine2(String line2) { + this.line2 = line2; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java index 5d13787e65..69c9a048cd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalQualifierTest.java @@ -86,4 +86,41 @@ public void conditionalClassQualifiers() { assertThat( employee.getNin() ).isNull(); assertThat( employee.getSsid() ).isNull(); } + + @ProcessorTest + @WithClasses({ + ConditionalMethodWithSourceToTargetMapper.class + }) + @IssueKey("2666") + public void conditionalQualifiersForSourceToTarget() { + ConditionalMethodWithSourceToTargetMapper mapper = ConditionalMethodWithSourceToTargetMapper.INSTANCE; + + ConditionalMethodWithSourceToTargetMapper.OrderDTO orderDto = + new ConditionalMethodWithSourceToTargetMapper.OrderDTO(); + + ConditionalMethodWithSourceToTargetMapper.Order order = mapper.convertToOrder( orderDto ); + assertThat( order ).isNotNull(); + assertThat( order.getCustomer() ).isNull(); + + orderDto = new ConditionalMethodWithSourceToTargetMapper.OrderDTO(); + orderDto.setCustomerName( "Tester" ); + order = mapper.convertToOrder( orderDto ); + + assertThat( order ).isNotNull(); + assertThat( order.getCustomer() ).isNotNull(); + assertThat( order.getCustomer().getName() ).isEqualTo( "Tester" ); + assertThat( order.getCustomer().getAddress() ).isNull(); + + orderDto = new ConditionalMethodWithSourceToTargetMapper.OrderDTO(); + orderDto.setLine1( "Line 1" ); + order = mapper.convertToOrder( orderDto ); + + assertThat( order ).isNotNull(); + assertThat( order.getCustomer() ).isNotNull(); + assertThat( order.getCustomer().getName() ).isNull(); + assertThat( order.getCustomer().getAddress() ).isNotNull(); + assertThat( order.getCustomer().getAddress().getLine1() ).isEqualTo( "Line 1" ); + assertThat( order.getCustomer().getAddress().getLine2() ).isNull(); + + } } From ea45666d665c51c02a79bb9a670855b9fab06059 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sat, 11 Dec 2021 14:16:19 +0100 Subject: [PATCH 219/597] #2673: Fix optional wrapping pattern throwing exception when primitive types are present MethodMatcher incorrectly reported that a primitive type matches a candidate for a type var --- .../internal/model/source/MethodMatcher.java | 7 +- .../ap/test/bugs/_2673/Issue2673Mapper.java | 66 +++++++++++++++++++ .../ap/test/bugs/_2673/Issue2673Test.java | 24 +++++++ 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2673/Issue2673Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2673/Issue2673Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index f08e4a9152..f0d9536799 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; - import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; @@ -239,6 +238,8 @@ boolean getCandidates(Type aCandidateMethodType, Type matchingType, Map String method( T par ); @@ -256,6 +257,8 @@ boolean getCandidates(Type aCandidateMethodType, Type matchingType, Map T map(Optional opt) { + return opt.orElse( null ); + } + + default Optional map(T t) { + return Optional.ofNullable( t ); + } + + class Target { + private final int primitive; + private final String nonPrimitive; + + public Target(int primitive, String nonPrimitive) { + this.primitive = primitive; + this.nonPrimitive = nonPrimitive; + } + + public int getPrimitive() { + return primitive; + } + + public String getNonPrimitive() { + return nonPrimitive; + } + } + + class Source { + private final int primitive; + private final Optional nonPrimitive; + + public Source(int primitive, Optional nonPrimitive) { + this.primitive = primitive; + this.nonPrimitive = nonPrimitive; + } + + public int getPrimitive() { + return primitive; + } + + public Optional getNonPrimitive() { + return nonPrimitive; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2673/Issue2673Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2673/Issue2673Test.java new file mode 100644 index 0000000000..40a5e79276 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2673/Issue2673Test.java @@ -0,0 +1,24 @@ +/* + * 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._2673; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Ben Zegveld + */ +@WithClasses({ + Issue2673Mapper.class +}) +class Issue2673Test { + + @ProcessorTest + @IssueKey( "2673" ) + void shouldCompile() { + } +} From 0de10ca83c682303f4082c27b3a22eec5fbcb258 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 12 Dec 2021 12:48:55 +0100 Subject: [PATCH 220/597] [maven-release-plugin] prepare release 1.5.0.Beta2 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index aaae7b5404..6eaf876961 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta2 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 293043e6f9..ef963caadd 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta2 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 10c95cf3b5..8012b3be5e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta2 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 337ad35e51..d2bf1509d5 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta2 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 8850da641c..3341ffb5cc 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta2 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 4b48c19496..69b81e4321 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta2 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index f4edccfc85..f5f241762a 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta2 pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.0.Beta2
    diff --git a/pom.xml b/pom.xml index 28c35aea74..2179424cbd 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta2 parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.0.Beta2 diff --git a/processor/pom.xml b/processor/pom.xml index 4f2476ceb0..c1751f395f 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Beta2 ../parent/pom.xml From 930b07aab82d9270a871f89b957717825a693385 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 12 Dec 2021 12:48:56 +0100 Subject: [PATCH 221/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 6eaf876961..aaae7b5404 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta2 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index ef963caadd..293043e6f9 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta2 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 8012b3be5e..10c95cf3b5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta2 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index d2bf1509d5..337ad35e51 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta2 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 3341ffb5cc..8850da641c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta2 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 69b81e4321..4b48c19496 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta2 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index f5f241762a..f4edccfc85 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta2 + 1.5.0-SNAPSHOT pom MapStruct Parent @@ -64,7 +64,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.0.Beta2 + HEAD diff --git a/pom.xml b/pom.xml index 2179424cbd..28c35aea74 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta2 + 1.5.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.0.Beta2 + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index c1751f395f..4f2476ceb0 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Beta2 + 1.5.0-SNAPSHOT ../parent/pom.xml From 0a7b8134d4eaf5430e392403f33881129eaeba98 Mon Sep 17 00:00:00 2001 From: Ben Zegveld Date: Fri, 24 Dec 2021 14:44:59 +0100 Subject: [PATCH 222/597] #2689: documentation: fix example title. --- .../main/asciidoc/chapter-10-advanced-mapping-options.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 5d7da371a1..fdbe5f8059 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -303,7 +303,7 @@ public interface CarMapper { The generated mapper will look like: -.try-catch block in generated implementation +.Custom condition check in generated implementation ==== [source, java, linenums] [subs="verbatim,attributes"] From 42cfa05c40d1cd8d68397ed5581de147929c1250 Mon Sep 17 00:00:00 2001 From: Valentin Kulesh Date: Sun, 26 Dec 2021 13:22:05 +0300 Subject: [PATCH 223/597] #2704 Fix broken reference to constants within nested enums --- .../model/assignment/EnumConstantWrapper.ftl | 2 +- .../ap/test/bugs/_2704/Issue2704Test.java | 21 +++++++++++++++++++ .../ap/test/bugs/_2704/TestMapper.java | 19 +++++++++++++++++ .../ap/test/bugs/_2704/TopLevel.java | 21 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/Issue2704Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/TestMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/TopLevel.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.ftl index 7364f7922e..449a273614 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.ftl @@ -6,4 +6,4 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.assignment.EnumConstantWrapper" --> -${ext.targetType.name}.${assignment} \ No newline at end of file +${ext.targetType.createReferenceName()}.${assignment} \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/Issue2704Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/Issue2704Test.java new file mode 100644 index 0000000000..5920a6022b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/Issue2704Test.java @@ -0,0 +1,21 @@ +/* + * 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._2704; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Valentin Kulesh + */ +@IssueKey("2704") +@WithClasses({ TestMapper.class, TopLevel.class }) +public class Issue2704Test { + @ProcessorTest + public void shouldCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/TestMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/TestMapper.java new file mode 100644 index 0000000000..846a783437 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/TestMapper.java @@ -0,0 +1,19 @@ +/* + * 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._2704; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.bugs._2704.TopLevel.Target; + +/** + * @author Valentin Kulesh + */ +@Mapper(implementationPackage = "") +public interface TestMapper { + @Mapping(target = "e", constant = "VALUE1") + Target test(Object unused); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/TopLevel.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/TopLevel.java new file mode 100644 index 0000000000..ef30c2cd6e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2704/TopLevel.java @@ -0,0 +1,21 @@ +/* + * 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._2704; + +/** + * @author Valentin Kulesh + */ +public interface TopLevel { + enum InnerEnum { + VALUE1, + VALUE2, + } + + class Target { + public void setE(@SuppressWarnings("unused") InnerEnum e) { + } + } +} From f7f65ac1dee0db41daf702382fba0284bff5419a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 25 Dec 2021 13:12:29 +0100 Subject: [PATCH 224/597] #2687 Add documentation for NullValueMappingStrategy for collections and maps --- ...apter-10-advanced-mapping-options.asciidoc | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index fdbe5f8059..463ebcf7c4 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -212,6 +212,29 @@ However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETU The strategy works in a hierarchical fashion. Setting `nullValueMappingStrategy` on mapping method level will override `@Mapper#nullValueMappingStrategy`, and `@Mapper#nullValueMappingStrategy` will override `@MapperConfig#nullValueMappingStrategy`. +[[mapping-result-for-null-collection-or-map-arguments]] +=== Controlling mapping result for 'null' collection or map arguments + +With <> it is possible to control how the return type should be constructed when the source argument of the mapping method is `null`. +That is applied for all mapping methods (bean, iterable or map mapping methods). + +However, MapStruct also offers a more dedicated way to control how collections / maps should be mapped. +e.g. return default (empty) collections / maps, but return `null` for beans. + +For collections (iterables) this can be controlled through: + +* `MapperConfig#nullValueIterableMappingStrategy` +* `Mapper#nullValueIterableMappingStrategy` +* `IterableMapping#nullValueMappingStrategy` + +For maps this can be controlled through: + +* `MapperConfig#nullValueMapMappingStrategy` +* `Mapper#nullValueMapMappingStrategy` +* `MapMapping#nullValueMappingStrategy` + +How the value of the `NullValueMappingStrategy` is applied is the same as in <> + [[mapping-result-for-null-properties]] === Controlling mapping result for 'null' properties in bean mappings (update mapping methods only). From 59c5f40ac3c6f83e0e4c333a9ed67ae23b6ae664 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Tue, 18 Jan 2022 18:22:47 +0100 Subject: [PATCH 225/597] #2686 Add documentation about when mappers are injected --- .../src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc index 724b42fbb7..67db28c7c0 100644 --- a/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc @@ -117,7 +117,7 @@ public interface CarMapper { ---- ==== -The generated mapper will inject all classes defined in the **uses** attribute. +The generated mapper will inject classes defined in the **uses** attribute if MapStruct has detected that it needs to use an instance of it for a mapping. When `InjectionStrategy#CONSTRUCTOR` is used, the constructor will have the appropriate annotation and the fields won't. When `InjectionStrategy#FIELD` is used, the annotation is on the field itself. For now, the default injection strategy is field injection, but it can be configured with <>. From 277b6f5d2b357097a968d1201d29b3012928b72d Mon Sep 17 00:00:00 2001 From: Ben Zegveld Date: Thu, 20 Jan 2022 23:02:12 +0100 Subject: [PATCH 226/597] #2719: added a note at the builder documentation to point towards the Before-/AfterMapping documentation. --- .../src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 370c09e249..9d5124ee97 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -445,6 +445,11 @@ The <> are also considered for the builder type. E.g. If an object factory exists for our `PersonBuilder` then this factory would be used instead of the builder creation method. ====== +[NOTE] +====== +Detected builders influence `@BeforeMapping` and `@AfterMapping` behavior. See chapter `Mapping customization with before-mapping and after-mapping methods` for more information. +====== + .Person with Builder example ==== [source, java, linenums] From 0f297ae60f2a839578576b46c38b08832f167bc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Jan 2022 22:33:51 +0000 Subject: [PATCH 227/597] Bump protobuf-java from 3.6.0 to 3.16.1 in /parent Bumps [protobuf-java](https://github.com/protocolbuffers/protobuf) from 3.6.0 to 3.16.1. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/master/generate_changelog.py) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.6.0...v3.16.1) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index f4edccfc85..263aa73503 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -223,7 +223,7 @@ com.google.protobuf protobuf-java - 3.6.0 + 3.16.1 org.inferred From 0a8e9b738cb2868628d2d8d12833c92b8ede1d04 Mon Sep 17 00:00:00 2001 From: Goni-Dev <56092853+Goni-Dev@users.noreply.github.com> Date: Sun, 23 Jan 2022 17:26:21 +0100 Subject: [PATCH 228/597] #2709 Corrected description for example demonstrating default expression --- .../main/asciidoc/chapter-10-advanced-mapping-options.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 463ebcf7c4..0bbec1fe57 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -88,7 +88,7 @@ Default expressions are a combination of default values and expressions. They wi The same warnings and restrictions apply to default expressions that apply to expressions. Only Java is supported, and MapStruct will not validate the expression at generation-time. -The example below demonstrates how two source properties can be mapped to one target: +The example below demonstrates how a default expression can be used to set a value when the source attribute is not present (e.g. is `null`): .Mapping method using a default expression ==== From b22efd9ad7d8878c45d8f5c906b39dbbfe3aeaec Mon Sep 17 00:00:00 2001 From: Justyna <98126210+JKLedzion@users.noreply.github.com> Date: Mon, 24 Jan 2022 14:17:24 +0100 Subject: [PATCH 229/597] =?UTF-8?q?#2674:=20Add=20check=20if=20method=20wi?= =?UTF-8?q?thout=20implementation=20doesn=E2=80=99t=20have=20`@AfterMappin?= =?UTF-8?q?g`=20/=20`@BeforeMapping`=20annotation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../processor/MethodRetrievalProcessor.java | 10 +++++ .../mapstruct/ap/internal/util/Message.java | 2 + .../_2674/ErroneousSourceTargetMapping.java | 28 +++++++++++++ .../ap/test/bugs/_2674/Issue2674Test.java | 40 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2674/Source.java | 28 +++++++++++++ .../mapstruct/ap/test/bugs/_2674/Target.java | 28 +++++++++++++ 6 files changed, 136 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2674/ErroneousSourceTargetMapping.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2674/Issue2674Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2674/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2674/Target.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index b226c1a050..a31fb41a7a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -564,6 +564,16 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List Date: Sat, 29 Jan 2022 00:37:24 +0100 Subject: [PATCH 230/597] #2668: Added support for collections and maps with a no-args constructor (#2671) #2668: Added support for collections and maps with a no-args constructor. Added a compiler error in case of a collection or map without either a no-arg constructor or a copy constructor. --- .../model/CollectionAssignmentBuilder.java | 84 +++++++++++- ...nceSetterWrapperForCollectionsAndMaps.java | 43 +++++++ ...perForCollectionsAndMapsWithNullCheck.java | 2 +- .../ap/internal/model/common/Type.java | 6 +- .../mapstruct/ap/internal/util/Message.java | 1 + ...anceSetterWrapperForCollectionsAndMaps.ftl | 23 ++++ .../bugs/_2668/Erroneous2668ListMapper.java | 54 ++++++++ .../bugs/_2668/Erroneous2668MapMapper.java | 54 ++++++++ .../ap/test/bugs/_2668/Issue2668Mapper.java | 120 ++++++++++++++++++ .../ap/test/bugs/_2668/Issue2668Test.java | 59 +++++++++ 10 files changed, 438 insertions(+), 8 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/assignment/NewInstanceSetterWrapperForCollectionsAndMaps.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/NewInstanceSetterWrapperForCollectionsAndMaps.ftl create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Erroneous2668ListMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Erroneous2668MapMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Issue2668Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Issue2668Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index dd6db1b0ee..e3bc85342a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -5,19 +5,27 @@ */ package org.mapstruct.ap.internal.model; +import java.util.function.Predicate; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; + +import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.model.assignment.ExistingInstanceSetterWrapperForCollectionsAndMaps; import org.mapstruct.ap.internal.model.assignment.GetterWrapperForCollectionsAndMaps; +import org.mapstruct.ap.internal.model.assignment.NewInstanceSetterWrapperForCollectionsAndMaps; import org.mapstruct.ap.internal.model.assignment.SetterWrapperForCollectionsAndMaps; import org.mapstruct.ap.internal.model.assignment.SetterWrapperForCollectionsAndMapsWithNullCheck; import org.mapstruct.ap.internal.model.assignment.UpdateWrapper; import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment.AssignmentType; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; -import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; -import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; -import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; @@ -169,7 +177,8 @@ else if ( method.isUpdateMethod() && !targetImmutable ) { targetAccessorType.isFieldAssignment() ); } - else if ( setterWrapperNeedsSourceNullCheck( result ) ) { + else if ( setterWrapperNeedsSourceNullCheck( result ) + && canBeMappedOrDirectlyAssigned( result ) ) { result = new SetterWrapperForCollectionsAndMapsWithNullCheck( result, @@ -179,7 +188,7 @@ else if ( setterWrapperNeedsSourceNullCheck( result ) ) { targetAccessorType.isFieldAssignment() ); } - else { + else if ( canBeMappedOrDirectlyAssigned( result ) ) { //TODO init default value // target accessor is setter, so wrap the setter in setter map/ collection handling @@ -190,6 +199,21 @@ else if ( setterWrapperNeedsSourceNullCheck( result ) ) { targetAccessorType.isFieldAssignment() ); } + else if ( hasNoArgsConstructor() ) { + result = new NewInstanceSetterWrapperForCollectionsAndMaps( + result, + method.getThrownTypes(), + targetType, + ctx.getTypeFactory(), + targetAccessorType.isFieldAssignment() ); + } + else { + ctx.getMessager().printMessage( + method.getExecutable(), + Message.PROPERTYMAPPING_NO_SUITABLE_COLLECTION_OR_MAP_CONSTRUCTOR, + targetType + ); + } } else { if ( targetImmutable ) { @@ -212,6 +236,12 @@ else if ( setterWrapperNeedsSourceNullCheck( result ) ) { return result; } + private boolean canBeMappedOrDirectlyAssigned(Assignment result) { + return result.getType() != AssignmentType.DIRECT + || hasCopyConstructor() + || targetType.isEnumSet(); + } + /** * Checks whether the setter wrapper should include a null / presence check or not * @@ -236,4 +266,48 @@ private boolean setterWrapperNeedsSourceNullCheck(Assignment rhs) { return false; } + private boolean hasCopyConstructor() { + return checkConstructorForPredicate( this::hasCopyConstructor ); + } + + private boolean hasNoArgsConstructor() { + return checkConstructorForPredicate( this::hasNoArgsConstructor ); + } + + private boolean checkConstructorForPredicate(Predicate predicate) { + if ( targetType.isCollectionOrMapType() ) { + if ( "java.util".equals( targetType.getPackageName() ) ) { + return true; + } + else { + Element sourceElement = targetType.getImplementationType() != null + ? targetType.getImplementationType().getTypeElement() + : targetType.getTypeElement(); + if ( sourceElement != null ) { + for ( Element element : sourceElement.getEnclosedElements() ) { + if ( element.getKind() == ElementKind.CONSTRUCTOR + && element.getModifiers().contains( Modifier.PUBLIC ) ) { + if ( predicate.test( element ) ) { + return true; + } + } + } + } + } + } + return false; + } + + private boolean hasNoArgsConstructor(Element element) { + return ( (ExecutableElement) element ).getParameters().isEmpty(); + } + + private boolean hasCopyConstructor(Element element) { + if ( element instanceof ExecutableElement ) { + ExecutableElement ee = (ExecutableElement) element; + return ee.getParameters().size() == 1 + && ctx.getTypeUtils().isAssignable( targetType.getTypeMirror(), ee.getParameters().get( 0 ).asType() ); + } + return false; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/NewInstanceSetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/NewInstanceSetterWrapperForCollectionsAndMaps.java new file mode 100644 index 0000000000..f0e23470a7 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/NewInstanceSetterWrapperForCollectionsAndMaps.java @@ -0,0 +1,43 @@ +/* + * 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.internal.model.assignment; + +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.model.common.TypeFactory; + +/** + * This wrapper handles the situation where an assignment is done via the setter, while creating the collection or map + * using a no-args constructor. + * + * @author Ben Zegveld + */ +public class NewInstanceSetterWrapperForCollectionsAndMaps extends SetterWrapperForCollectionsAndMapsWithNullCheck { + + private String instanceVar; + + public NewInstanceSetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, + List thrownTypesToExclude, + Type targetType, + TypeFactory typeFactory, + boolean fieldAssignment) { + + super( + decoratedAssignment, + thrownTypesToExclude, + targetType, + typeFactory, + fieldAssignment + ); + this.instanceVar = decoratedAssignment.createUniqueVarName( targetType.getName() ); + } + + public String getInstanceVar() { + return instanceVar; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java index 5e3f4d63fe..a0c9f799e9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java @@ -68,7 +68,7 @@ public boolean isDirectAssignment() { } public boolean isEnumSet() { - return "java.util.EnumSet".equals( targetType.getFullyQualifiedName() ); + return targetType.isEnumSet(); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index ab2871c69d..ba5f62254d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; - import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -113,7 +112,6 @@ public class Type extends ModelElement implements Comparable { private List setters = null; private List adders = null; private List alternativeTargetAccessors = null; - private Map constructorAccessors = null; private Type boundingBase = null; @@ -1603,4 +1601,8 @@ private static Type topLevelType(TypeElement typeElement, TypeFactory typeFactor return parent == null ? null : typeFactory.getType( parent.asType() ); } + public boolean isEnumSet() { + return "java.util.EnumSet".equals( getFullyQualifiedName() ); + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 26f4a33c5d..8588ec183a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -80,6 +80,7 @@ public enum Message { PROPERTYMAPPING_WHITESPACE_TRIMMED( "The property named \"%s\" has whitespaces, using trimmed property \"%s\" instead.", Diagnostic.Kind.WARNING ), PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PROPERTY_FROM_TARGET("The type of parameter \"%s\" has no property named \"%s\". Please define the source property explicitly."), PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET("No property named \"%s\" exists in source parameter(s). Please define the source explicitly."), + PROPERTYMAPPING_NO_SUITABLE_COLLECTION_OR_MAP_CONSTRUCTOR( "%s does not have an accessible copy or no-args constructor." ), CONVERSION_LOSSY_WARNING( "%s has a possibly lossy conversion from %s to %s.", Diagnostic.Kind.WARNING ), CONVERSION_LOSSY_ERROR( "Can't map %s. It has a possibly lossy conversion from %s to %s." ), diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/NewInstanceSetterWrapperForCollectionsAndMaps.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/NewInstanceSetterWrapperForCollectionsAndMaps.ftl new file mode 100644 index 0000000000..4582e68a1e --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/NewInstanceSetterWrapperForCollectionsAndMaps.ftl @@ -0,0 +1,23 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.assignment.SetterWrapperForCollectionsAndMapsWithNullCheck" --> +<#import "../macro/CommonMacros.ftl" as lib> +<@lib.sourceLocalVarAssignment/> +<@lib.handleExceptions> + <@callTargetWriteAccessor/> + +<#-- + assigns the target via the regular target write accessor (usually the setter) +--> +<#macro callTargetWriteAccessor> + <@lib.handleLocalVarNullCheck needs_explicit_local_var=directAssignment> + <#if ext.targetType.implementationType??><@includeModel object=ext.targetType.implementationType/><#else><@includeModel object=ext.targetType/> ${instanceVar} = new <#if ext.targetType.implementationType??><@includeModel object=ext.targetType.implementationType/><#else><@includeModel object=ext.targetType/>(); + ${instanceVar}.<#if ext.targetType.collectionType>addAll<#else>putAll( ${nullCheckLocalVarName} ); + <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite>${instanceVar}; + + diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Erroneous2668ListMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Erroneous2668ListMapper.java new file mode 100644 index 0000000000..beb1d3283a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Erroneous2668ListMapper.java @@ -0,0 +1,54 @@ +/* + * 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._2668; + +import java.util.ArrayList; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface Erroneous2668ListMapper { + + Erroneous2668ListMapper INSTANCE = Mappers.getMapper( Erroneous2668ListMapper.class ); + + CollectionTarget map(CollectionSource source); + + class CollectionTarget { + MyArrayList list; + + public MyArrayList getList() { + return list; + } + + public void setList(MyArrayList list) { + this.list = list; + } + } + + class CollectionSource { + MyArrayList list; + + public MyArrayList getList() { + return list; + } + + public void setList(MyArrayList list) { + this.list = list; + } + } + + class MyArrayList extends ArrayList { + private String unusable; + + public MyArrayList(String unusable) { + this.unusable = unusable; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Erroneous2668MapMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Erroneous2668MapMapper.java new file mode 100644 index 0000000000..3c9bcd3926 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Erroneous2668MapMapper.java @@ -0,0 +1,54 @@ +/* + * 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._2668; + +import java.util.HashMap; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface Erroneous2668MapMapper { + + Erroneous2668MapMapper INSTANCE = Mappers.getMapper( Erroneous2668MapMapper.class ); + + CollectionTarget map(CollectionSource source); + + class CollectionTarget { + MyHashMap map; + + public MyHashMap getMap() { + return map; + } + + public void setMap(MyHashMap map) { + this.map = map; + } + } + + class CollectionSource { + MyHashMap map; + + public MyHashMap getMap() { + return map; + } + + public void setMap(MyHashMap map) { + this.map = map; + } + } + + class MyHashMap extends HashMap { + private String unusable; + + public MyHashMap(String unusable) { + this.unusable = unusable; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Issue2668Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Issue2668Mapper.java new file mode 100644 index 0000000000..1b72e2f636 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Issue2668Mapper.java @@ -0,0 +1,120 @@ +/* + * 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._2668; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface Issue2668Mapper { + + Issue2668Mapper INSTANCE = Mappers.getMapper( Issue2668Mapper.class ); + + CollectionTarget map(CollectionSource source); + + class CollectionTarget { + MyArrayList list; + MyHashMap map; + MyCopyArrayList copyList; + MyCopyHashMap copyMap; + + public MyArrayList getList() { + return list; + } + + public MyHashMap getMap() { + return map; + } + + public void setList(MyArrayList list) { + this.list = list; + } + + public void setMap(MyHashMap map) { + this.map = map; + } + + public MyCopyArrayList getCopyList() { + return copyList; + } + + public MyCopyHashMap getCopyMap() { + return copyMap; + } + + public void setCopyList(MyCopyArrayList copyList) { + this.copyList = copyList; + } + + public void setCopyMap(MyCopyHashMap copyMap) { + this.copyMap = copyMap; + } + } + + class CollectionSource { + MyArrayList list; + MyHashMap map; + MyCopyArrayList copyList; + MyCopyHashMap copyMap; + + public MyArrayList getList() { + return list; + } + + public MyHashMap getMap() { + return map; + } + + public void setList(MyArrayList list) { + this.list = list; + } + + public void setMap(MyHashMap map) { + this.map = map; + } + + public MyCopyArrayList getCopyList() { + return copyList; + } + + public MyCopyHashMap getCopyMap() { + return copyMap; + } + + public void setCopyList(MyCopyArrayList copyList) { + this.copyList = copyList; + } + + public void setCopyMap(MyCopyHashMap copyMap) { + this.copyMap = copyMap; + } + } + + class MyArrayList extends ArrayList { + } + + class MyHashMap extends HashMap { + } + + class MyCopyArrayList extends ArrayList { + public MyCopyArrayList(Collection copy) { + super( copy ); + } + } + + class MyCopyHashMap extends HashMap { + public MyCopyHashMap(HashMap copy) { + super( copy ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Issue2668Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Issue2668Test.java new file mode 100644 index 0000000000..20b4e445fd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2668/Issue2668Test.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.bugs._2668; + +import javax.tools.Diagnostic.Kind; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +/** + * @author Ben Zegveld + */ +@IssueKey( "2668" ) +class Issue2668Test { + + @ProcessorTest + @WithClasses( Issue2668Mapper.class ) + void shouldCompileCorrectlyWithAvailableConstructors() { + } + + @ProcessorTest + @WithClasses( Erroneous2668ListMapper.class ) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = Kind.ERROR, + line = 21, + message = "org.mapstruct.ap.test.bugs._2668.Erroneous2668ListMapper.MyArrayList" + + " does not have an accessible copy or no-args constructor." + ) + } + ) + void errorExpectedBecauseCollectionIsNotUsable() { + } + + @ProcessorTest + @WithClasses( Erroneous2668MapMapper.class ) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = Kind.ERROR, + line = 21, + message = "org.mapstruct.ap.test.bugs._2668.Erroneous2668MapMapper.MyHashMap" + + " does not have an accessible copy or no-args constructor." + ) + } + ) + void errorExpectedBecauseMapIsNotUsable() { + } +} From ec30f5d2797670f053e6b2cc5eb96f62dcc3541c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Jan 2022 11:03:44 +0100 Subject: [PATCH 231/597] #2725 Update tools-gem to 1.0.0.Alpha3 and run GitHub Action with Java 19-ea Also update Spring to 5.3.15 to be able to run on Java 19 --- .github/workflows/java-ea.yml | 2 +- parent/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index f2b2a99be4..76e24db3b1 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - java: [18-ea] + java: [18-ea, 19-ea] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: diff --git a/parent/pom.xml b/parent/pom.xml index 263aa73503..242d5215de 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -21,11 +21,11 @@ UTF-8 - 1.0.0.Alpha2 + 1.0.0.Alpha3 3.0.0-M3 3.0.0-M5 3.1.0 - 5.3.10 + 5.3.15 1.6.0 8.36.1 5.8.0-M1 From 20ff51ebb8782afe571e797b992e9660e3667e74 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Jan 2022 11:13:16 +0100 Subject: [PATCH 232/597] #2728 Add new WithTestDependency annotation for our processor testing Adding this dependency allows us to dynamically pick the dependencies that we want to have on the test compilation classpath. It would allow us to more granularly test things with different dependencies, such as javax inject and jakarta inject --- .../ap/test/bugs/_1395/Issue1395Test.java | 2 + .../ap/test/bugs/_1425/Issue1425Test.java | 2 + .../ap/test/bugs/_1460/Issue1460Test.java | 2 + .../ap/test/bugs/_2145/Issue2145Test.java | 2 + .../ap/test/bugs/_880/Issue880Test.java | 2 + .../ap/test/builtin/BuiltInTest.java | 28 +++++++--- .../test/builtin/jodatime/JodaTimeTest.java | 4 ++ .../collection/wildcard/WildCardTest.java | 2 + .../erroneous/InvalidDateFormatTest.java | 2 + .../jodatime/JodaConversionTest.java | 2 + .../decorator/jsr330/Jsr330DecoratorTest.java | 2 + .../constructor/SpringDecoratorTest.java | 2 + .../spring/field/SpringDecoratorTest.java | 2 + .../destination/DestinationClassNameTest.java | 2 + .../imports/ConflictingTypesNamesTest.java | 2 + ...30DefaultCompileOptionFieldMapperTest.java | 2 + ...330CompileOptionConstructorMapperTest.java | 2 + .../Jsr330ConstructorMapperTest.java | 2 + .../jsr330/field/Jsr330FieldMapperTest.java | 2 + .../_default/SpringDefaultMapperTest.java | 2 + ...ingCompileOptionConstructorMapperTest.java | 2 + .../SpringConstructorMapperTest.java | 2 + .../spring/field/SpringFieldMapperTest.java | 2 + .../NestedMappingMethodInvocationTest.java | 4 ++ ...ferencedMappersWithSameSimpleNameTest.java | 2 + .../jaxb/JaxbFactoryMethodSelectionTest.java | 2 + .../jaxb/UnderscoreSelectionTest.java | 2 + .../ap/testutil/WithJavaxInject.java | 27 ++++++++++ .../mapstruct/ap/testutil/WithJavaxJaxb.java | 27 ++++++++++ .../org/mapstruct/ap/testutil/WithJoda.java | 27 ++++++++++ .../org/mapstruct/ap/testutil/WithSpring.java | 28 ++++++++++ .../ap/testutil/WithTestDependencies.java | 22 ++++++++ .../ap/testutil/WithTestDependency.java | 28 ++++++++++ .../testutil/runner/CompilationRequest.java | 11 +++- .../testutil/runner/CompilingExtension.java | 52 ++++++++++++------- .../runner/EclipseCompilingExtension.java | 25 +++++++-- .../runner/JdkCompilingExtension.java | 20 ++++++- 37 files changed, 319 insertions(+), 32 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithJavaxInject.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithJavaxJaxb.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithJoda.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithSpring.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithTestDependencies.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithTestDependency.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java index 757436a0f4..042d0288cd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java @@ -8,6 +8,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; /** * @author Filip Hrisafov @@ -18,6 +19,7 @@ Source.class, Target.class } ) +@WithSpring @IssueKey( "1395" ) public class Issue1395Test { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1425/Issue1425Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1425/Issue1425Test.java index 8c5700772f..a71c33c396 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1425/Issue1425Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1425/Issue1425Test.java @@ -9,6 +9,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJoda; import static org.assertj.core.api.Assertions.assertThat; @@ -21,6 +22,7 @@ Target.class }) @IssueKey("1425") +@WithJoda public class Issue1425Test { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Test.java index 077e66df76..e9c97f39bc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Test.java @@ -12,6 +12,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJoda; import static org.assertj.core.api.Assertions.assertThat; @@ -24,6 +25,7 @@ Target.class }) @IssueKey("1460") +@WithJoda public class Issue1460Test { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java index ec8bb4603c..38aed76003 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2145/Issue2145Test.java @@ -8,11 +8,13 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxJaxb; import static org.assertj.core.api.Assertions.assertThat; @IssueKey("2145") @WithClasses(Issue2145Mapper.class) +@WithJavaxJaxb public class Issue2145Test { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java index d05f01dc91..9d8540b409 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java @@ -11,6 +11,7 @@ import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.MappingConstants; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; @@ -30,6 +31,7 @@ @ProcessorOptions({ @ProcessorOption(name = "mapstruct.defaultComponentModel", value = MappingConstants.ComponentModel.SPRING), @ProcessorOption(name = "mapstruct.unmappedTargetPolicy", value = "ignore") }) +@WithSpring public class Issue880Test { @RegisterExtension final GeneratedSource generatedSource = new GeneratedSource(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java index 0f1983a851..2dc41b822d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java @@ -58,6 +58,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxJaxb; import static org.assertj.core.api.Assertions.assertThat; @@ -71,8 +72,6 @@ MapTarget.class, CalendarProperty.class, DateProperty.class, - JaxbElementListProperty.class, - JaxbElementProperty.class, StringListProperty.class, StringProperty.class, BigDecimalProperty.class, @@ -81,13 +80,16 @@ XmlGregorianCalendarProperty.class, ZonedDateTimeProperty.class, IterableSource.class, - MapSource.class }) @DefaultTimeZone("Europe/Berlin") public class BuiltInTest { @ProcessorTest - @WithClasses( JaxbMapper.class ) + @WithClasses( { + JaxbMapper.class, + JaxbElementProperty.class, + } ) + @WithJavaxJaxb public void shouldApplyBuiltInOnJAXBElement() { JaxbElementProperty source = new JaxbElementProperty(); source.setProp( createJaxb( "TEST" ) ); @@ -100,7 +102,11 @@ public void shouldApplyBuiltInOnJAXBElement() { } @ProcessorTest - @WithClasses( JaxbMapper.class ) + @WithClasses( { + JaxbMapper.class, + JaxbElementProperty.class, + } ) + @WithJavaxJaxb @IssueKey( "1698" ) public void shouldApplyBuiltInOnJAXBElementExtra() { JaxbElementProperty source = new JaxbElementProperty(); @@ -123,7 +129,11 @@ public void shouldApplyBuiltInOnJAXBElementExtra() { } @ProcessorTest - @WithClasses( JaxbListMapper.class ) + @WithClasses( { + JaxbListMapper.class, + JaxbElementListProperty.class, + } ) + @WithJavaxJaxb @IssueKey( "141" ) public void shouldApplyBuiltInOnJAXBElementList() { @@ -347,7 +357,11 @@ public void shouldApplyBuiltInOnIterable() throws DatatypeConfigurationException } @ProcessorTest - @WithClasses( MapSourceTargetMapper.class ) + @WithClasses( { + MapSourceTargetMapper.class, + MapSource.class, + } ) + @WithJavaxJaxb public void shouldApplyBuiltInOnMap() throws DatatypeConfigurationException { MapSource source = new MapSource(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/jodatime/JodaTimeTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/jodatime/JodaTimeTest.java index 77c62c9cac..cb0c3d2378 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/jodatime/JodaTimeTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/jodatime/JodaTimeTest.java @@ -30,6 +30,8 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxJaxb; +import org.mapstruct.ap.testutil.WithJoda; import static org.assertj.core.api.Assertions.assertThat; @@ -45,6 +47,8 @@ XmlGregorianCalendarBean.class }) @IssueKey( "689" ) +@WithJoda +@WithJavaxJaxb public class JodaTimeTest { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java index 37ed4606f6..4da6f8648a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/WildCardTest.java @@ -13,6 +13,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxJaxb; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; @@ -133,6 +134,7 @@ public void shouldFailOnTypeVarTarget() { @ProcessorTest @WithClasses( { BeanMapper.class, GoodIdea.class, CunningPlan.class } ) + @WithJavaxJaxb public void shouldMapBean() { GoodIdea aGoodIdea = new GoodIdea(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java index 5eb67bee05..81bf3eb568 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/erroneous/InvalidDateFormatTest.java @@ -8,6 +8,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJoda; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; @@ -19,6 +20,7 @@ Source.class, Target.class }) +@WithJoda @IssueKey("725") public class InvalidDateFormatTest { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java index 001b6390e2..1ab73616d5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java @@ -20,6 +20,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJoda; import static org.assertj.core.api.Assertions.assertThat; @@ -31,6 +32,7 @@ @WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) @IssueKey("75") @DefaultLocale("de") +@WithJoda public class JodaConversionTest { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java index d00368dfd4..a3bad68b92 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java @@ -19,6 +19,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -44,6 +45,7 @@ @IssueKey("592") @ComponentScan(basePackageClasses = Jsr330DecoratorTest.class) @Configuration +@WithJavaxInject public class Jsr330DecoratorTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/SpringDecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/SpringDecoratorTest.java index dc834683e3..6d18555372 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/SpringDecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/constructor/SpringDecoratorTest.java @@ -16,6 +16,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -40,6 +41,7 @@ @IssueKey("592") @ComponentScan(basePackageClasses = SpringDecoratorTest.class) @Configuration +@WithSpring public class SpringDecoratorTest { @Autowired diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/SpringDecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/SpringDecoratorTest.java index 1b81ed8c48..ac0af86381 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/SpringDecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/field/SpringDecoratorTest.java @@ -16,6 +16,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -37,6 +38,7 @@ PersonMapper.class, PersonMapperDecorator.class }) +@WithSpring @IssueKey("592") @ComponentScan(basePackageClasses = SpringDecoratorTest.class) @Configuration diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java index e61bd1cb2c..30611a8d60 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java @@ -7,6 +7,7 @@ import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; import org.mapstruct.factory.Mappers; import static org.assertj.core.api.Assertions.assertThat; @@ -25,6 +26,7 @@ public void shouldGenerateRightName() { @ProcessorTest @WithClasses({ DestinationClassNameWithJsr330Mapper.class }) + @WithJavaxInject public void shouldNotGenerateSpi() throws Exception { Class clazz = DestinationClassNameWithJsr330Mapper.class; diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/ConflictingTypesNamesTest.java b/processor/src/test/java/org/mapstruct/ap/test/imports/ConflictingTypesNamesTest.java index d09ddf66a2..8d61eb8f8a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/imports/ConflictingTypesNamesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/ConflictingTypesNamesTest.java @@ -15,6 +15,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; @@ -41,6 +42,7 @@ org.mapstruct.ap.test.imports.to.FooWrapper.class, SecondSourceTargetMapper.class }) +@WithJavaxInject public class ConflictingTypesNamesTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java index 65d8acfb2c..dd639684b8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java @@ -17,6 +17,7 @@ import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -41,6 +42,7 @@ GenderJsr330DefaultCompileOptionFieldMapper.class }) @ComponentScan(basePackageClasses = CustomerJsr330DefaultCompileOptionFieldMapper.class) +@WithJavaxInject @Configuration public class Jsr330DefaultCompileOptionFieldMapperTest { diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java index c564648ecf..24bfba5511 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java @@ -14,6 +14,7 @@ import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; @@ -42,6 +43,7 @@ @ProcessorOption( name = "mapstruct.defaultInjectionStrategy", value = "constructor") @ComponentScan(basePackageClasses = CustomerJsr330CompileOptionConstructorMapper.class) @Configuration +@WithJavaxInject public class Jsr330CompileOptionConstructorMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java index 06f4180289..8543d57e38 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java @@ -15,6 +15,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -42,6 +43,7 @@ @IssueKey("571") @ComponentScan(basePackageClasses = CustomerJsr330ConstructorMapper.class) @Configuration +@WithJavaxInject public class Jsr330ConstructorMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java index fc3bd09561..d8cf0c3925 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java @@ -18,6 +18,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -44,6 +45,7 @@ @IssueKey("571") @ComponentScan(basePackageClasses = CustomerJsr330FieldMapper.class) @Configuration +@WithJavaxInject public class Jsr330FieldMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/SpringDefaultMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/SpringDefaultMapperTest.java index ef1b79b34b..2d9c374721 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/SpringDefaultMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/_default/SpringDefaultMapperTest.java @@ -15,6 +15,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -41,6 +42,7 @@ @IssueKey("571") @ComponentScan(basePackageClasses = CustomerSpringDefaultMapper.class) @Configuration +@WithSpring public class SpringDefaultMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java index b106a5d788..c3291a5f3b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/compileoptionconstructor/SpringCompileOptionConstructorMapperTest.java @@ -21,6 +21,7 @@ import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; @@ -52,6 +53,7 @@ @ProcessorOption( name = "mapstruct.defaultInjectionStrategy", value = "constructor") @ComponentScan(basePackageClasses = CustomerSpringCompileOptionConstructorMapper.class) @Configuration +@WithSpring @DefaultTimeZone("Europe/Berlin") public class SpringCompileOptionConstructorMapperTest { diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java index 1ff3fc8428..2594d5c23d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/constructor/SpringConstructorMapperTest.java @@ -23,6 +23,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -53,6 +54,7 @@ @IssueKey( "571" ) @ComponentScan(basePackageClasses = CustomerSpringConstructorMapper.class) @Configuration +@WithSpring @DefaultTimeZone("Europe/Berlin") public class SpringConstructorMapperTest { diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/SpringFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/SpringFieldMapperTest.java index 8ee03f4bfb..d464dd5b16 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/SpringFieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/field/SpringFieldMapperTest.java @@ -15,6 +15,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -42,6 +43,7 @@ @IssueKey("571") @ComponentScan(basePackageClasses = CustomerSpringFieldMapper.class) @Configuration +@WithSpring public class SpringFieldMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java index 1e9b52cf62..8d600d8604 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java @@ -20,6 +20,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxJaxb; import static org.assertj.core.api.Assertions.assertThat; @@ -42,6 +43,7 @@ public class NestedMappingMethodInvocationTest { OrderDetailsType.class, OrderType.class } ) + @WithJavaxJaxb public void shouldMapViaMethodAndMethod() throws DatatypeConfigurationException { OrderTypeToOrderDtoMapper instance = OrderTypeToOrderDtoMapper.INSTANCE; OrderDto target = instance.sourceToTarget( createOrderType() ); @@ -62,6 +64,7 @@ public void shouldMapViaMethodAndMethod() throws DatatypeConfigurationException ObjectFactory.class, TargetDto.class } ) + @WithJavaxJaxb public void shouldMapViaMethodAndConversion() { SourceTypeTargetDtoMapper instance = SourceTypeTargetDtoMapper.INSTANCE; @@ -78,6 +81,7 @@ public void shouldMapViaMethodAndConversion() { ObjectFactory.class, TargetDto.class } ) + @WithJavaxJaxb public void shouldMapViaConversionAndMethod() { SourceTypeTargetDtoMapper instance = SourceTypeTargetDtoMapper.INSTANCE; diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/samename/SeveralReferencedMappersWithSameSimpleNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/references/samename/SeveralReferencedMappersWithSameSimpleNameTest.java index 68ae703769..fe99685e9c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/references/samename/SeveralReferencedMappersWithSameSimpleNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/references/samename/SeveralReferencedMappersWithSameSimpleNameTest.java @@ -12,6 +12,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; import static org.assertj.core.api.Assertions.assertThat; @@ -30,6 +31,7 @@ Jsr330SourceTargetMapper.class, AnotherSourceTargetMapper.class }) +@WithJavaxInject public class SeveralReferencedMappersWithSameSimpleNameTest { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/JaxbFactoryMethodSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/JaxbFactoryMethodSelectionTest.java index 02705a097a..94fe8a1c10 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/JaxbFactoryMethodSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/JaxbFactoryMethodSelectionTest.java @@ -13,6 +13,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxJaxb; import static org.assertj.core.api.Assertions.assertThat; @@ -28,6 +29,7 @@ OrderDto.class, OrderShippingDetailsDto.class, OrderType.class, OrderShippingDetailsType.class, OrderMapper.class }) +@WithJavaxJaxb public class JaxbFactoryMethodSelectionTest { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/UnderscoreSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/UnderscoreSelectionTest.java index f94dd7c94c..caa60eeea3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/UnderscoreSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/jaxb/UnderscoreSelectionTest.java @@ -13,6 +13,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxJaxb; import static org.assertj.core.api.Assertions.assertThat; @@ -23,6 +24,7 @@ */ @IssueKey( "726" ) @WithClasses( { UnderscoreType.class, ObjectFactory.class, SuperType.class, SubType.class, UnderscoreMapper.class } ) +@WithJavaxJaxb public class UnderscoreSelectionTest { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithJavaxInject.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithJavaxInject.java new file mode 100644 index 0000000000..de2ae231e7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithJavaxInject.java @@ -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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Spring Dependencies + * + * @author Filip Hrisafov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithTestDependency({ + "javax.inject", +}) +public @interface WithJavaxInject { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithJavaxJaxb.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithJavaxJaxb.java new file mode 100644 index 0000000000..a404187b52 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithJavaxJaxb.java @@ -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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Spring Dependencies + * + * @author Filip Hrisafov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithTestDependency({ + "jaxb-api", +}) +public @interface WithJavaxJaxb { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithJoda.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithJoda.java new file mode 100644 index 0000000000..5b8bede20b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithJoda.java @@ -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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Spring Dependencies + * + * @author Filip Hrisafov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithTestDependency({ + "joda-time" +}) +public @interface WithJoda { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithSpring.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithSpring.java new file mode 100644 index 0000000000..40db5afd81 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithSpring.java @@ -0,0 +1,28 @@ +/* + * 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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Spring Dependencies + * + * @author Filip Hrisafov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithTestDependency({ + "spring-beans", + "spring-context" +}) +public @interface WithSpring { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithTestDependencies.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithTestDependencies.java new file mode 100644 index 0000000000..c9e8d8c523 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithTestDependencies.java @@ -0,0 +1,22 @@ +/* + * 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.testutil; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Filip Hrisafov + * @see WithTestDependency + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +public @interface WithTestDependencies { + + WithTestDependency[] value(); +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithTestDependency.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithTestDependency.java new file mode 100644 index 0000000000..26d478b666 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithTestDependency.java @@ -0,0 +1,28 @@ +/* + * 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.testutil; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the group id of the additional test dependencies that should be added to the test compile path + * + * @author Filip Hrisafov + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Repeatable( WithTestDependencies.class ) +public @interface WithTestDependency { + /** + * @return The group ids of the additional test dependencies for the test compile path + */ + String[] value(); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java index 3f1599aad0..60e9a30072 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.testutil.runner; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -17,13 +18,15 @@ public class CompilationRequest { private final Set> sourceClasses; private final Map, Class> services; private final List processorOptions; + private final Collection testDependencies; CompilationRequest(Compiler compiler, Set> sourceClasses, Map, Class> services, - List processorOptions) { + List processorOptions, Collection testDependencies) { this.compiler = compiler; this.sourceClasses = sourceClasses; this.services = services; this.processorOptions = processorOptions; + this.testDependencies = testDependencies; } @Override @@ -34,6 +37,7 @@ public int hashCode() { result = prime * result + ( ( processorOptions == null ) ? 0 : processorOptions.hashCode() ); result = prime * result + ( ( services == null ) ? 0 : services.hashCode() ); result = prime * result + ( ( sourceClasses == null ) ? 0 : sourceClasses.hashCode() ); + result = prime * result + ( ( testDependencies == null ) ? 0 : testDependencies.hashCode() ); return result; } @@ -53,6 +57,7 @@ public boolean equals(Object obj) { return compiler.equals( other.compiler ) && processorOptions.equals( other.processorOptions ) && services.equals( other.services ) + && testDependencies.equals( other.testDependencies ) && sourceClasses.equals( other.sourceClasses ); } @@ -67,4 +72,8 @@ public List getProcessorOptions() { public Map, Class> getServices() { return services; } + + public Collection getTestDependencies() { + return testDependencies; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java index b372974461..7c47d2a2e8 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -22,7 +23,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import java.util.stream.Stream; import com.puppycrawl.tools.checkstyle.Checker; import com.puppycrawl.tools.checkstyle.ConfigurationLoader; @@ -34,6 +34,7 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithServiceImplementation; +import org.mapstruct.ap.testutil.WithTestDependency; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.DisableCheckstyle; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; @@ -98,37 +99,38 @@ protected String getPathSuffix() { return "_" + compiler.name().toLowerCase(); } + /** + * Build the default test compilation classpath + * needed for compiling the generated sources once the processor has run. + */ private static List buildTestCompilationClasspath() { - String[] whitelist = - new String[] { + Collection whitelist = Arrays.asList( // MapStruct annotations in multi-module reactor build or IDE "core" + File.separator + "target", // MapStruct annotations in single module build "org" + File.separator + "mapstruct" + File.separator + "mapstruct" + File.separator, - "guava", - "javax.inject", - "spring-beans", - "spring-context", - "jaxb-api", - "joda-time" }; + "guava" + ); return filterBootClassPath( whitelist ); } + /** + * Build the annotation processor classpath. + * i.e. the classpath that is needed to run the annotation processor with its own dependencies only. + * The optional dependencies are not needed in this classpath. + */ private static List buildProcessorClasspath() { - String[] whitelist = - new String[] { + Collection whitelist = Arrays.asList( "processor" + File.separator + "target", // the processor itself, "freemarker", - "javax.inject", - "spring-context", - "gem-api", - "joda-time" }; + "gem-api" + ); return filterBootClassPath( whitelist ); } - protected static List filterBootClassPath(String[] whitelist) { + protected static List filterBootClassPath(Collection whitelist) { String[] bootClasspath = System.getProperty( "java.class.path" ).split( File.pathSeparator ); String testClasses = "target" + File.separator + "test-classes"; @@ -143,8 +145,8 @@ protected static List filterBootClassPath(String[] whitelist) { return classpath; } - private static boolean isWhitelisted(String path, String[] whitelist) { - return Stream.of( whitelist ).anyMatch( path::contains ); + private static boolean isWhitelisted(String path, Collection whitelist) { + return whitelist.stream().anyMatch( path::contains ); } @Override @@ -368,6 +370,17 @@ private Map, Class> getServices(Method testMethod, Class testClas return services; } + private Collection getAdditionalTestDependencies(Method testMethod, Class testClass) { + Collection testDependencies = new HashSet<>(); + findRepeatableAnnotations( testMethod, WithTestDependency.class ) + .forEach( annotation -> Collections.addAll( testDependencies, annotation.value() ) ); + + findRepeatableAnnotations( testClass, WithTestDependency.class ) + .forEach( annotation -> Collections.addAll( testDependencies, annotation.value() ) ); + + return testDependencies; + } + private void addServices(Map, Class> services, List withImplementations) { for ( WithServiceImplementation withImplementation : withImplementations ) { addService( services, withImplementation ); @@ -446,7 +459,8 @@ private CompilationOutcomeDescriptor compile(ExtensionContext context) { compiler, getTestClasses( testMethod, testClass ), getServices( testMethod, testClass ), - getProcessorOptions( testMethod, testClass ) + getProcessorOptions( testMethod, testClass ), + getAdditionalTestDependencies( testMethod, testClass ) ); ExtensionContext.Store rootStore = context.getRoot().getStore( NAMESPACE ); diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java index 6cab46cc7f..c356347664 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java @@ -6,6 +6,9 @@ package org.mapstruct.ap.testutil.runner; import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -65,13 +68,27 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe return clHelper.compileInOtherClassloader( compilationRequest, - TEST_COMPILATION_CLASSPATH, + getTestCompilationClasspath( compilationRequest ), getSourceFiles( compilationRequest.getSourceClasses() ), SOURCE_DIR, sourceOutputDir, classOutputDir ); } + private static List getTestCompilationClasspath(CompilationRequest request) { + Collection testDependencies = request.getTestDependencies(); + if ( testDependencies.isEmpty() ) { + return TEST_COMPILATION_CLASSPATH; + } + + List testCompilationPaths = new ArrayList<>( + TEST_COMPILATION_CLASSPATH.size() + testDependencies.size() ); + + testCompilationPaths.addAll( TEST_COMPILATION_CLASSPATH ); + testCompilationPaths.addAll( filterBootClassPath( testDependencies ) ); + return testCompilationPaths; + } + private static FilteringParentClassLoader newFilteringClassLoaderForEclipse() { return new FilteringParentClassLoader( // reload eclipse compiler classes @@ -143,12 +160,12 @@ private static String getSourceVersion() { } private static List buildEclipseCompilerClasspath() { - String[] whitelist = - new String[] { + Collection whitelist = Arrays.asList( "tycho-compiler", "ecj", "plexus-compiler-api", - "plexus-component-annotations" }; + "plexus-component-annotations" + ); return filterBootClassPath( whitelist ); } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java index 1761956678..2a0b6ba537 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Objects; import javax.annotation.processing.Processor; @@ -57,7 +58,7 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe fileManager.getJavaFileObjectsFromFiles( getSourceFiles( compilationRequest.getSourceClasses() ) ); try { - fileManager.setLocation( StandardLocation.CLASS_PATH, COMPILER_CLASSPATH_FILES ); + fileManager.setLocation( StandardLocation.CLASS_PATH, getCompilerClasspathFiles( compilationRequest ) ); fileManager.setLocation( StandardLocation.CLASS_OUTPUT, Arrays.asList( new File( classOutputDir ) ) ); fileManager.setLocation( StandardLocation.SOURCE_OUTPUT, Arrays.asList( new File( sourceOutputDir ) ) ); } @@ -97,6 +98,23 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe diagnostics.getDiagnostics() ); } + private static List getCompilerClasspathFiles(CompilationRequest request) { + Collection testDependencies = request.getTestDependencies(); + if ( testDependencies.isEmpty() ) { + return COMPILER_CLASSPATH_FILES; + } + + List compilerClasspathFiles = new ArrayList<>( + COMPILER_CLASSPATH_FILES.size() + testDependencies.size() ); + + compilerClasspathFiles.addAll( COMPILER_CLASSPATH_FILES ); + for ( String testDependencyPath : filterBootClassPath( testDependencies ) ) { + compilerClasspathFiles.add( new File( testDependencyPath ) ); + } + + return compilerClasspathFiles; + } + private static List asFiles(List paths) { List classpath = new ArrayList<>(); for ( String path : paths ) { From aade31f095f037a0ddf934de24f1638cb24aac6b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 23 Jan 2022 21:12:30 +0100 Subject: [PATCH 233/597] #2468 Update needed dependencies for running CDI tests on Java 16+ --- .../mapstruct/itest/tests/MavenIntegrationTest.java | 2 -- integrationtest/src/test/resources/cdiTest/pom.xml | 2 +- .../org/mapstruct/itest/cdi/CdiBasedMapperTest.java | 4 +++- parent/pom.xml | 11 ++++++----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 59aa48b053..9696542f58 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -5,7 +5,6 @@ */ package org.mapstruct.itest.tests; -import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.parallel.Execution; @@ -26,7 +25,6 @@ void autoValueBuilderTest() { } @ProcessorTest(baseDir = "cdiTest") - @DisabledForJreRange(min = JRE.JAVA_16) void cdiTest() { } diff --git a/integrationtest/src/test/resources/cdiTest/pom.xml b/integrationtest/src/test/resources/cdiTest/pom.xml index 30d35a421e..0b3c394e82 100644 --- a/integrationtest/src/test/resources/cdiTest/pom.xml +++ b/integrationtest/src/test/resources/cdiTest/pom.xml @@ -56,7 +56,7 @@ org.jboss.weld - weld-core + weld-core-impl test diff --git a/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java b/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java index 702ab6d255..0c343fce27 100644 --- a/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java +++ b/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java @@ -38,7 +38,9 @@ public static JavaArchive createDeployment() { .addPackage( SourceTargetMapper.class.getPackage() ) .addPackage( DateMapper.class.getPackage() ) .addAsManifestResource( - new StringAsset("org.mapstruct.itest.cdi.SourceTargetMapperDecorator"), + new StringAsset("" + + "org.mapstruct.itest.cdi.SourceTargetMapperDecorator" + + ""), "beans.xml" ); } diff --git a/parent/pom.xml b/parent/pom.xml index 242d5215de..21413dcef0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -152,7 +152,7 @@ javax.enterprise cdi-api - 1.2 + 2.0.SP1 javax.inject @@ -162,19 +162,20 @@ org.jboss.arquillian arquillian-bom - 1.0.2.Final + 1.6.0.Final import pom org.jboss.arquillian.container arquillian-weld-se-embedded-1.1 - 1.0.0.CR7 + 1.0.0.Final org.jboss.weld - weld-core - 2.3.2.Final + weld-core-impl + 3.1.8.Final + test org.glassfish From 5f4d355838adffe2691f4e2d3eb3ac7f6c4c7850 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Jan 2022 11:46:34 +0100 Subject: [PATCH 234/597] #1661 Add support for globally disabling builders --- .../src/main/asciidoc/chapter-2-set-up.asciidoc | 5 +++++ .../chapter-3-defining-a-mapper.asciidoc | 2 +- .../java/org/mapstruct/ap/MappingProcessor.java | 4 ++++ .../mapstruct/ap/internal/option/Options.java | 10 +++++++++- .../util/AnnotationProcessorContext.java | 10 ++++++++-- .../off/SimpleNotRealyImmutableBuilderTest.java | 17 ++++++++++++++++- .../builder/off/SimpleWithBuilderMapper.java | 17 +++++++++++++++++ 7 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleWithBuilderMapper.java diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 730433639d..54906f7893 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -260,6 +260,11 @@ Supported values are: If a policy is given for a specific mapper via `@Mapper#unmappedSourcePolicy()`, the value from the annotation takes precedence. If a policy is given for a specific bean mapping via `@BeanMapping#ignoreUnmappedSourceProperties()`, it takes precedence over both `@Mapper#unmappedSourcePolicy()` and the option. |`WARN` + +|`mapstruct. +disableBuilders` +|If set to `true`, then MapStruct will not use builder patterns when doing the mapping. This is equivalent to doing `@Mapper( builder = @Builder( disableBuilder = true ) )` for all of your mappers. +|`false` |=== === Using MapStruct with the Java Module System diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 9d5124ee97..d1c63c25a2 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -532,7 +532,7 @@ Otherwise, you would need to write a custom `BuilderProvider` [TIP] ==== -In case you want to disable using builders then you can use the `NoOpBuilderProvider` by creating a `org.mapstruct.ap.spi.BuilderProvider` file in the `META-INF/services` directory with `org.mapstruct.ap.spi.NoOpBuilderProvider` as it's content. +In case you want to disable using builders then you can pass the MapStruct processor option `mapstruct.disableBuilders` to the compiler. e.g. `-Amapstruct.disableBuilders=true`. ==== [[mapping-with-constructors]] diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 952257753a..0cc6b7cce3 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -87,6 +87,7 @@ MappingProcessor.UNMAPPED_SOURCE_POLICY, MappingProcessor.DEFAULT_COMPONENT_MODEL, MappingProcessor.DEFAULT_INJECTION_STRATEGY, + MappingProcessor.DISABLE_BUILDERS, MappingProcessor.VERBOSE }) public class MappingProcessor extends AbstractProcessor { @@ -104,6 +105,7 @@ public class MappingProcessor extends AbstractProcessor { protected static final String DEFAULT_COMPONENT_MODEL = "mapstruct.defaultComponentModel"; protected static final String DEFAULT_INJECTION_STRATEGY = "mapstruct.defaultInjectionStrategy"; protected static final String ALWAYS_GENERATE_SERVICE_FILE = "mapstruct.alwaysGenerateServicesFile"; + protected static final String DISABLE_BUILDERS = "mapstruct.disableBuilders"; protected static final String VERBOSE = "mapstruct.verbose"; private Options options; @@ -130,6 +132,7 @@ public synchronized void init(ProcessingEnvironment processingEnv) { processingEnv.getElementUtils(), processingEnv.getTypeUtils(), processingEnv.getMessager(), + options.isDisableBuilders(), options.isVerbose() ); } @@ -146,6 +149,7 @@ private Options createOptions() { processingEnv.getOptions().get( DEFAULT_COMPONENT_MODEL ), processingEnv.getOptions().get( DEFAULT_INJECTION_STRATEGY ), Boolean.valueOf( processingEnv.getOptions().get( ALWAYS_GENERATE_SERVICE_FILE ) ), + Boolean.valueOf( processingEnv.getOptions().get( DISABLE_BUILDERS ) ), Boolean.valueOf( processingEnv.getOptions().get( VERBOSE ) ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java index 46a90d4b43..65089cc139 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java @@ -21,13 +21,16 @@ public class Options { private final boolean alwaysGenerateSpi; private final String defaultComponentModel; private final String defaultInjectionStrategy; + private final boolean disableBuilders; private final boolean verbose; public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVersionComment, ReportingPolicyGem unmappedTargetPolicy, ReportingPolicyGem unmappedSourcePolicy, String defaultComponentModel, String defaultInjectionStrategy, - boolean alwaysGenerateSpi, boolean verbose) { + boolean alwaysGenerateSpi, + boolean disableBuilders, + boolean verbose) { this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; this.suppressGeneratorVersionComment = suppressGeneratorVersionComment; this.unmappedTargetPolicy = unmappedTargetPolicy; @@ -35,6 +38,7 @@ public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVers this.defaultComponentModel = defaultComponentModel; this.defaultInjectionStrategy = defaultInjectionStrategy; this.alwaysGenerateSpi = alwaysGenerateSpi; + this.disableBuilders = disableBuilders; this.verbose = verbose; } @@ -66,6 +70,10 @@ public boolean isAlwaysGenerateSpi() { return alwaysGenerateSpi; } + public boolean isDisableBuilders() { + return disableBuilders; + } + public boolean isVerbose() { return verbose; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java index 86fb7e3092..7421dfde7a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java @@ -31,6 +31,7 @@ import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; import org.mapstruct.ap.spi.ImmutablesBuilderProvider; import org.mapstruct.ap.spi.MapStructProcessingEnvironment; +import org.mapstruct.ap.spi.NoOpBuilderProvider; /** * Keeps contextual data in the scope of the entire annotation processor ("application scope"). @@ -51,14 +52,17 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen private Elements elementUtils; private Types typeUtils; private Messager messager; + private boolean disableBuilder; private boolean verbose; - public AnnotationProcessorContext(Elements elementUtils, Types typeUtils, Messager messager, boolean verbose) { + public AnnotationProcessorContext(Elements elementUtils, Types typeUtils, Messager messager, boolean disableBuilder, + boolean verbose) { astModifyingAnnotationProcessors = java.util.Collections.unmodifiableList( findAstModifyingAnnotationProcessors( messager ) ); this.elementUtils = elementUtils; this.typeUtils = typeUtils; this.messager = messager; + this.disableBuilder = disableBuilder; this.verbose = verbose; } @@ -103,7 +107,9 @@ else if ( elementUtils.getTypeElement( FreeBuilderConstants.FREE_BUILDER_FQN ) ! + this.accessorNamingStrategy.getClass().getCanonicalName() ); } - this.builderProvider = Services.get( BuilderProvider.class, defaultBuilderProvider ); + this.builderProvider = this.disableBuilder ? + new NoOpBuilderProvider() : + Services.get( BuilderProvider.class, defaultBuilderProvider ); this.builderProvider.init( this ); if ( verbose ) { messager.printMessage( diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleNotRealyImmutableBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleNotRealyImmutableBuilderTest.java index cc11a8fcdb..30cf5ff12d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleNotRealyImmutableBuilderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleNotRealyImmutableBuilderTest.java @@ -9,12 +9,13 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.mapstruct.factory.Mappers; import static org.assertj.core.api.Assertions.assertThat; -@IssueKey("1743") +@IssueKey("1661") @WithClasses({ SimpleMutablePerson.class, SimpleNotRealyImmutablePerson.class @@ -36,4 +37,18 @@ public void testSimpleImmutableBuilderHappyPath() { assertThat( targetObject.getName() ).isEqualTo( "Bob" ); } + + @ProcessorTest + @WithClasses({ SimpleWithBuilderMapper.class }) + @ProcessorOption( name = "mapstruct.disableBuilders", value = "true") + public void builderGloballyDisabled() { + SimpleWithBuilderMapper mapper = Mappers.getMapper( SimpleWithBuilderMapper.class ); + SimpleMutablePerson source = new SimpleMutablePerson(); + source.setFullName( "Bob" ); + + SimpleNotRealyImmutablePerson targetObject = mapper.toNotRealyImmutable( source ); + + assertThat( targetObject.getName() ).isEqualTo( "Bob" ); + + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleWithBuilderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleWithBuilderMapper.java new file mode 100644 index 0000000000..6df547db78 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/off/SimpleWithBuilderMapper.java @@ -0,0 +1,17 @@ +/* + * 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.builder.off; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface SimpleWithBuilderMapper { + + @Mapping(target = "name", source = "fullName") + SimpleNotRealyImmutablePerson toNotRealyImmutable(SimpleMutablePerson source); +} From 464adc914306ce32c5a5c89cd537009806c6ec34 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Jan 2022 09:24:56 +0100 Subject: [PATCH 235/597] #2682 Change RetentionPolicy of `@DecoratedWith` to `CLASS` In some circumstances (used with other types of aggregating processors, e.g. Spring) the Gradle incremental compilation works correctly only for classes annotated with the `CLASS` or `RUNTIME` retention policy. The `@DecoratedWith` is the only annotation from MapStruct that was `SOURCE` retention. With this commit we are changing its retention policy in order for better compatibility with the Gradle Incremental compilation --- core/src/main/java/org/mapstruct/DecoratedWith.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/mapstruct/DecoratedWith.java b/core/src/main/java/org/mapstruct/DecoratedWith.java index 78b2c580ff..8a644f4b22 100644 --- a/core/src/main/java/org/mapstruct/DecoratedWith.java +++ b/core/src/main/java/org/mapstruct/DecoratedWith.java @@ -145,7 +145,7 @@ * @author Gunnar Morling */ @Target(ElementType.TYPE) -@Retention(RetentionPolicy.SOURCE) +@Retention(RetentionPolicy.CLASS) public @interface DecoratedWith { /** From aed3ff5295fd91f28622f41ea14cf0a4e9670efa Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 23 Jan 2022 22:04:12 +0100 Subject: [PATCH 236/597] #1997 Use builders to construct empty objects in update wrapper --- .../ap/internal/model/MethodReference.java | 53 +++++++++++++++++++ .../model/ObjectFactoryMethodResolver.java | 6 ++- .../ap/internal/model/PropertyMapping.java | 22 ++++++++ .../ap/internal/model/MethodReference.ftl | 2 + .../org/mapstruct/ap/test/bugs/_1997/Car.java | 44 +++++++++++++++ .../ap/test/bugs/_1997/CarDetail.java | 44 +++++++++++++++ .../ap/test/bugs/_1997/CarInsurance.java | 44 +++++++++++++++ .../test/bugs/_1997/CarInsuranceMapper.java | 23 ++++++++ .../ap/test/bugs/_1997/Issue1997Test.java | 35 ++++++++++++ 9 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/Car.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarDetail.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarInsurance.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarInsuranceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/Issue1997Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java index 78a3830f45..e2e518c55b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.model; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; @@ -58,8 +59,10 @@ public class MethodReference extends ModelElement implements Assignment { private final Type definingType; private final List parameterBindings; private final Parameter providingParameter; + private final List methodsToChain; private final boolean isStatic; private final boolean isConstructor; + private final boolean isMethodChaining; /** * Creates a new reference to the given method. @@ -95,6 +98,8 @@ protected MethodReference(Method method, MapperReference declaringMapper, Parame this.isStatic = method.isStatic(); this.name = method.getName(); this.isConstructor = false; + this.methodsToChain = Collections.emptyList(); + this.isMethodChaining = false; } private MethodReference(BuiltInMethod method, ConversionContext contextParam) { @@ -111,6 +116,8 @@ private MethodReference(BuiltInMethod method, ConversionContext contextParam) { this.isStatic = method.isStatic(); this.name = method.getName(); this.isConstructor = false; + this.methodsToChain = Collections.emptyList(); + this.isMethodChaining = false; } private MethodReference(String name, Type definingType, boolean isStatic) { @@ -127,6 +134,8 @@ private MethodReference(String name, Type definingType, boolean isStatic) { this.providingParameter = null; this.isStatic = isStatic; this.isConstructor = false; + this.methodsToChain = Collections.emptyList(); + this.isMethodChaining = false; } private MethodReference(Type definingType, List parameterBindings) { @@ -142,6 +151,8 @@ private MethodReference(Type definingType, List parameterBindi this.providingParameter = null; this.isStatic = false; this.isConstructor = true; + this.methodsToChain = Collections.emptyList(); + this.isMethodChaining = false; if ( parameterBindings.isEmpty() ) { this.importTypes = Collections.emptySet(); @@ -159,6 +170,24 @@ private MethodReference(Type definingType, List parameterBindi } } + private MethodReference(MethodReference... references) { + this.name = null; + this.definingType = null; + this.sourceParameters = Collections.emptyList(); + this.returnType = null; + this.declaringMapper = null; + this.importTypes = Collections.emptySet(); + this.thrownTypes = Collections.emptyList(); + this.isUpdateMethod = false; + this.contextParam = null; + this.parameterBindings = null; + this.providingParameter = null; + this.isStatic = false; + this.isConstructor = false; + this.methodsToChain = Arrays.asList( references ); + this.isMethodChaining = true; + } + public MapperReference getDeclaringMapper() { return declaringMapper; } @@ -267,6 +296,12 @@ public Set getImportTypes() { if ( isStatic() ) { imported.add( definingType ); } + if ( isMethodChaining() ) { + for ( MethodReference methodToChain : methodsToChain ) { + imported.addAll( methodToChain.getImportTypes() ); + } + } + return imported; } @@ -276,6 +311,12 @@ public List getThrownTypes() { if ( assignment != null ) { exceptions.addAll( assignment.getThrownTypes() ); } + if ( isMethodChaining() ) { + for ( MethodReference methodToChain : methodsToChain ) { + exceptions.addAll( methodToChain.getThrownTypes() ); + } + + } return exceptions; } @@ -311,6 +352,14 @@ public boolean isConstructor() { return isConstructor; } + public boolean isMethodChaining() { + return isMethodChaining; + } + + public List getMethodsToChain() { + return methodsToChain; + } + public List getParameterBindings() { return parameterBindings; } @@ -385,6 +434,10 @@ public static MethodReference forConstructorInvocation(Type type, List> getMatchingFactoryMethods( Meth } public static MethodReference getBuilderFactoryMethod(Method method, BuilderType builder ) { + return getBuilderFactoryMethod( method.getReturnType(), builder ); + } + + public static MethodReference getBuilderFactoryMethod(Type typeToBuild, BuilderType builder ) { if ( builder == null ) { return null; } @@ -149,7 +153,7 @@ public static MethodReference getBuilderFactoryMethod(Method method, BuilderType return null; } - if ( !builder.getBuildingType().isAssignableTo( method.getReturnType() ) ) { + if ( !builder.getBuildingType().isAssignableTo( typeToBuild ) ) { //TODO print error message return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index f17ab9ddb1..ad788de43b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -419,6 +419,28 @@ private Assignment assignToPlainViaSetter(Type targetType, Assignment rhs) { Assignment factory = ObjectFactoryMethodResolver .getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( rightHandSide ), ctx ); + + if ( factory == null && targetBuilderType != null) { + // If there is no dedicated factory method and the target has a builder we will try to use that + MethodReference builderFactoryMethod = ObjectFactoryMethodResolver.getBuilderFactoryMethod( + targetType, + targetBuilderType + ); + + if ( builderFactoryMethod != null ) { + + MethodReference finisherMethod = BuilderFinisherMethodResolver.getBuilderFinisherMethod( + method, + targetBuilderType, + ctx + ); + + if ( finisherMethod != null ) { + factory = MethodReference.forMethodChaining( builderFactoryMethod, finisherMethod ); + } + } + + } return new UpdateWrapper( rhs, method.getThrownTypes(), diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl index d0ee7bef3b..271ba58f9b 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl @@ -18,6 +18,8 @@ <@includeModel object=definingType/>.<@methodCall/> <#elseif constructor> new <@includeModel object=definingType/><#if (parameterBindings?size > 0)>( <@arguments/> )<#else>() + <#elseif methodChaining> + <#list methodsToChain as methodToChain><@includeModel object=methodToChain /><#if methodToChain_has_next>. <#else> <@methodCall/> diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/Car.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/Car.java new file mode 100644 index 0000000000..87722cdb50 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/Car.java @@ -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._1997; + +/** + * @author Filip Hrisafov + */ +public class Car { + private String model; + + private Car(Builder builder) { + this.model = builder.model; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String model; + + public Builder model(String model) { + this.model = model; + return this; + } + + public Car build() { + return new Car( this ); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarDetail.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarDetail.java new file mode 100644 index 0000000000..84472d1263 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarDetail.java @@ -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._1997; + +/** + * @author Filip Hrisafov + */ +public class CarDetail { + private String model; + + private CarDetail(Builder builder) { + this.model = builder.model; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String model; + + public Builder model(String model) { + this.model = model; + return this; + } + + public CarDetail build() { + return new CarDetail( this ); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarInsurance.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarInsurance.java new file mode 100644 index 0000000000..83fb4350dc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarInsurance.java @@ -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._1997; + +/** + * @author Filip Hrisafov + */ +public class CarInsurance { + private CarDetail detail; + + private CarInsurance(Builder builder) { + this.detail = builder.detail; + } + + public CarDetail getDetail() { + return detail; + } + + public void setDetail(CarDetail detail) { + this.detail = detail; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private CarDetail detail; + + public Builder detail(CarDetail detail) { + this.detail = detail; + return this; + } + + public CarInsurance build() { + return new CarInsurance( this ); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarInsuranceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarInsuranceMapper.java new file mode 100644 index 0000000000..79369b9a79 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/CarInsuranceMapper.java @@ -0,0 +1,23 @@ +/* + * 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._1997; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CarInsuranceMapper { + + CarInsuranceMapper INSTANCE = Mappers.getMapper( CarInsuranceMapper.class ); + + @Mapping(source = "model", target = "detail.model") + void update(Car source, @MappingTarget CarInsurance target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/Issue1997Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/Issue1997Test.java new file mode 100644 index 0000000000..5645ca173b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1997/Issue1997Test.java @@ -0,0 +1,35 @@ +/* + * 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._1997; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Car.class, + CarDetail.class, + CarInsurance.class, + CarInsuranceMapper.class +}) +class Issue1997Test { + + @ProcessorTest + void shouldCorrectCreateIntermediateObjectsWithBuilder() { + Car source = Car.builder().model( "Model S" ).build(); + CarInsurance target = CarInsurance.builder().build(); + assertThat( target.getDetail() ).isNull(); + + CarInsuranceMapper.INSTANCE.update( source, target ); + + assertThat( target.getDetail() ).isNotNull(); + assertThat( target.getDetail().getModel() ).isEqualTo( "Model S" ); + } +} From 12070186a43f9d4f03c0b441cbe5cd56c16089a9 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 22 Jan 2022 18:57:41 +0100 Subject: [PATCH 237/597] #2567 Add support for Jakarta Injection Support for Jakarta is done in 2 ways: * current jsr330 component model - In this case Jakarta Inject will be used if javax.inject is not available * new jakarta component model - In this case Jakarta Inject will always be used --- .../java/org/mapstruct/DecoratedWith.java | 8 +- .../java/org/mapstruct/InjectionStrategy.java | 2 +- core/src/main/java/org/mapstruct/Mapper.java | 7 +- .../main/java/org/mapstruct/MapperConfig.java | 7 +- .../java/org/mapstruct/MappingConstants.java | 15 +++- .../main/asciidoc/chapter-2-set-up.asciidoc | 3 +- .../test/resources/fullFeatureTest/pom.xml | 4 + parent/pom.xml | 5 ++ processor/pom.xml | 5 ++ .../ap/internal/gem/MappingConstantsGem.java | 2 + .../processor/JakartaComponentProcessor.java | 78 +++++++++++++++++++ .../processor/Jsr330ComponentProcessor.java | 24 +++++- ...p.internal.processor.ModelElementProcessor | 1 + .../jsr330/JakartaJsr330DecoratorTest.java | 55 +++++++++++++ .../jakarta/jsr330/PersonMapper.java | 25 ++++++ .../jakarta/jsr330/PersonMapperDecorator.java | 27 +++++++ .../mapstruct/ap/test/gem/ConstantTest.java | 2 + ...akartaDefaultCompileOptionFieldMapper.java | 21 +++++ ...akartaDefaultCompileOptionFieldMapper.java | 26 +++++++ ...30DefaultCompileOptionFieldMapperTest.java | 55 +++++++++++++ ...taDefaultCompileOptionFieldMapperTest.java | 53 +++++++++++++ ...JakartaCompileOptionConstructorMapper.java | 24 ++++++ ...JakartaCompileOptionConstructorMapper.java | 26 +++++++ ...330CompileOptionConstructorMapperTest.java | 59 ++++++++++++++ ...rtaCompileOptionConstructorMapperTest.java | 57 ++++++++++++++ .../constructor/ConstructorJakartaConfig.java | 18 +++++ .../CustomerJakartaConstructorMapper.java | 26 +++++++ .../GenderJakartaConstructorMapper.java | 25 ++++++ ...JakartaAndJsr330ConstructorMapperTest.java | 58 ++++++++++++++ .../JakartaConstructorMapperTest.java | 56 +++++++++++++ .../jakarta/field/CustomerFieldMapper.java | 22 ++++++ .../jakarta/field/FieldJakartaConfig.java | 17 ++++ .../field/GenderJakartaFieldMapper.java | 25 ++++++ .../JakartaAndJsr330FieldMapperTest.java | 53 +++++++++++++ .../jakarta/field/JakartaFieldMapperTest.java | 51 ++++++++++++ ...30DefaultCompileOptionFieldMapperTest.java | 3 + ...330CompileOptionConstructorMapperTest.java | 3 + .../Jsr330ConstructorMapperTest.java | 3 + .../jsr330/field/Jsr330FieldMapperTest.java | 3 + ...Jsr330DefaultCompileOptionFieldMapper.java | 21 +++++ ...Jsr330DefaultCompileOptionFieldMapper.java | 26 +++++++ ...30DefaultCompileOptionFieldMapperTest.java | 55 +++++++++++++ ...30DefaultCompileOptionFieldMapperTest.java | 53 +++++++++++++ .../ap/testutil/WithJakartaInject.java | 27 +++++++ 44 files changed, 1123 insertions(+), 13 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/JakartaJsr330DecoratorTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/PersonMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/PersonMapperDecorator.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/CustomerJakartaDefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/GenderJakartaDefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/JakartaAndJsr330DefaultCompileOptionFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/JakartaDefaultCompileOptionFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/CustomerJakartaCompileOptionConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/GenderJakartaCompileOptionConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/JakartaAndJsr330CompileOptionConstructorMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/JakartaCompileOptionConstructorMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/ConstructorJakartaConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/CustomerJakartaConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/GenderJakartaConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/JakartaAndJsr330ConstructorMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/JakartaConstructorMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/CustomerFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/FieldJakartaConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/GenderJakartaFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/JakartaAndJsr330FieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/JakartaFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/CustomerJsr330DefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/GenderJsr330DefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/JakartaAndJsr330DefaultCompileOptionFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/JakartaJsr330DefaultCompileOptionFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaInject.java diff --git a/core/src/main/java/org/mapstruct/DecoratedWith.java b/core/src/main/java/org/mapstruct/DecoratedWith.java index 8a644f4b22..b4ca05f3de 100644 --- a/core/src/main/java/org/mapstruct/DecoratedWith.java +++ b/core/src/main/java/org/mapstruct/DecoratedWith.java @@ -103,12 +103,12 @@ * private PersonMapper personMapper; // injects the decorator, with the injected original mapper * * - *

    3. Component model 'jsr330'

    + *

    3. Component model 'jsr330' or 'jakarta'

    *

    Referencing the original mapper

    *

    - * JSR 330 doesn't specify qualifiers and only allows to specifically name the beans. Hence, the generated - * implementation of the original mapper is annotated with - * {@code @javax.inject.Named("fully-qualified-name-of-generated-impl")} and {@code @Singleton} (please note that when + * JSR 330 / Jakarta Inject doesn't specify qualifiers and only allows to specifically name the beans. Hence, + * the generated implementation of the original mapper is annotated with + * {@code @Named("fully-qualified-name-of-generated-impl")} and {@code @Singleton} (please note that when * using a decorator, the class name of the mapper implementation ends with an underscore). To inject that bean in your * decorator, add the same annotation to the delegate field (e.g. by copy/pasting it from the generated class): * diff --git a/core/src/main/java/org/mapstruct/InjectionStrategy.java b/core/src/main/java/org/mapstruct/InjectionStrategy.java index 84baa8afa9..b841667671 100644 --- a/core/src/main/java/org/mapstruct/InjectionStrategy.java +++ b/core/src/main/java/org/mapstruct/InjectionStrategy.java @@ -7,7 +7,7 @@ /** * Strategy for handling injection. This is only used on annotated based component models such as CDI, Spring and - * JSR330. + * JSR330 / Jakarta. * * @author Kevin Grüneberg */ diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index c5aed661f3..0436c0cf69 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -142,7 +142,12 @@ * can be retrieved via {@code @Autowired} *

  • * {@code jsr330}: the generated mapper is annotated with {@code @javax.inject.Named} and - * {@code @Singleton}, and can be retrieved via {@code @Inject}
  • + * {@code @Singleton}, and can be retrieved via {@code @Inject}. + * The annotations will either be from javax.inject or jakarta.inject, + * depending on which one is available, with javax.inject having precedence. + *
  • + * {@code jakarta}: the generated mapper is annotated with {@code @jakarta.inject.Named} and + * {@code @Singleton}, and can be retrieved via {@code @Inject}.
  • * * The method overrides a componentModel set in a central configuration set * by {@link #config() } diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java index 757a4ab0af..893801cdaa 100644 --- a/core/src/main/java/org/mapstruct/MapperConfig.java +++ b/core/src/main/java/org/mapstruct/MapperConfig.java @@ -131,7 +131,12 @@ * can be retrieved via {@code @Autowired} *
  • * {@code jsr330}: the generated mapper is annotated with {@code @javax.inject.Named} and - * {@code @Singleton}, and can be retrieved via {@code @Inject}
  • + * {@code @Singleton}, and can be retrieved via {@code @Inject}. + * The annotations will either be from javax.inject or jakarta.inject, + * depending on which one is available, with javax.inject having precedence. + *
  • + * {@code jakarta}: the generated mapper is annotated with {@code @jakarta.inject.Named} and + * {@code @Singleton}, and can be retrieved via {@code @Inject}.
  • * * * @return The component model for the generated mapper. diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java index 002f005299..ce2438298c 100644 --- a/core/src/main/java/org/mapstruct/MappingConstants.java +++ b/core/src/main/java/org/mapstruct/MappingConstants.java @@ -120,11 +120,24 @@ private ComponentModel() { public static final String SPRING = "spring"; /** - * The generated mapper is annotated with @javax.inject.Named and @Singleton, and can be retrieved via @Inject + * The generated mapper is annotated with @Named and @Singleton, and can be retrieved via @Inject. + * The annotations are either from {@code javax.inject} or {@code jakarta.inject}. + * Priority have the {@code javax.inject} annotations. + * In case you want to only use Jakarta then use {@link #JAKARTA}. * + * @see #JAKARTA */ public static final String JSR330 = "jsr330"; + /** + * The generated mapper is annotated with @Named and @Singleton, and can be retrieved via @Inject. + * The annotations are from {@code jakarta.inject}. + * In case you want to use {@code javax.inject} then use {@link #JSR330}. + * + * @see #JSR330 + */ + public static final String JAKARTA = "jakarta"; + } } diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 54906f7893..d95983e840 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -217,7 +217,8 @@ Supported values are: * `default`: the mapper uses no component model, instances are typically retrieved via `Mappers#getMapper(Class)` * `cdi`: the generated mapper is an application-scoped CDI bean and can be retrieved via `@Inject` * `spring`: the generated mapper is a singleton-scoped Spring bean and can be retrieved via `@Autowired` -* `jsr330`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject`, e.g. using Spring +* `jsr330`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject` (from javax.inject or jakarta.inject, depending which one is available with javax.inject having priority), e.g. using Spring +* `jakarta`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject` (from jakarta.inject), e.g. using Spring If a component model is given for a specific mapper via `@Mapper#componentModel()`, the value from the annotation takes precedence. |`default` diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index ebf4590d30..ec26f45ce2 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -60,6 +60,10 @@ javax.inject javax.inject
    + + jakarta.inject + jakarta.inject-api + diff --git a/parent/pom.xml b/parent/pom.xml index 21413dcef0..1f75e9d6d5 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -159,6 +159,11 @@ javax.inject 1 + + jakarta.inject + jakarta.inject-api + 2.0.1 + org.jboss.arquillian arquillian-bom diff --git a/processor/pom.xml b/processor/pom.xml index 4f2476ceb0..2a3c38abfc 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -83,6 +83,11 @@ javax.inject test + + jakarta.inject + jakarta.inject-api + test + diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java index b49b9d17e5..cb6d49c0cb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java @@ -49,6 +49,8 @@ private ComponentModelGem() { public static final String SPRING = "spring"; public static final String JSR330 = "jsr330"; + + public static final String JAKARTA = "jakarta"; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java new file mode 100644 index 0000000000..5161ea689c --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java @@ -0,0 +1,78 @@ +/* + * 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.internal.processor; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.mapstruct.ap.internal.gem.MappingConstantsGem; +import org.mapstruct.ap.internal.model.Annotation; +import org.mapstruct.ap.internal.model.Mapper; + +/** + * A {@link ModelElementProcessor} which converts the given {@link Mapper} + * object into a Jakarta Inject style bean in case "jakarta" is configured as the + * target component model for this mapper. + * + * @author Filip Hrisafov + */ +public class JakartaComponentProcessor extends AnnotationBasedComponentModelProcessor { + @Override + protected String getComponentModelIdentifier() { + return MappingConstantsGem.ComponentModelGem.JAKARTA; + } + + @Override + protected List getTypeAnnotations(Mapper mapper) { + if ( mapper.getDecorator() == null ) { + return Arrays.asList( singleton(), named() ); + } + else { + return Arrays.asList( singleton(), namedDelegate( mapper ) ); + } + } + + @Override + protected List getDecoratorAnnotations() { + return Arrays.asList( singleton(), named() ); + } + + @Override + protected List getDelegatorReferenceAnnotations(Mapper mapper) { + return Arrays.asList( inject(), namedDelegate( mapper ) ); + } + + @Override + protected List getMapperReferenceAnnotations() { + return Collections.singletonList( inject() ); + } + + @Override + protected boolean requiresGenerationOfDecoratorClass() { + return true; + } + + private Annotation singleton() { + return new Annotation( getTypeFactory().getType( "jakarta.inject.Singleton" ) ); + } + + private Annotation named() { + return new Annotation( getTypeFactory().getType( "jakarta.inject.Named" ) ); + } + + private Annotation namedDelegate(Mapper mapper) { + return new Annotation( + getTypeFactory().getType( "jakarta.inject.Named" ), + Collections.singletonList( '"' + mapper.getPackageName() + "." + mapper.getName() + '"' ) + ); + } + + private Annotation inject() { + return new Annotation( getTypeFactory().getType( "jakarta.inject.Inject" ) ); + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java index 18a6e4f69d..6a6cc16580 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java @@ -12,6 +12,8 @@ import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Mapper; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.AnnotationProcessingException; /** * A {@link ModelElementProcessor} which converts the given {@link Mapper} @@ -58,21 +60,35 @@ protected boolean requiresGenerationOfDecoratorClass() { } private Annotation singleton() { - return new Annotation( getTypeFactory().getType( "javax.inject.Singleton" ) ); + return new Annotation( getType( "Singleton" ) ); } private Annotation named() { - return new Annotation( getTypeFactory().getType( "javax.inject.Named" ) ); + return new Annotation( getType( "Named" ) ); } private Annotation namedDelegate(Mapper mapper) { return new Annotation( - getTypeFactory().getType( "javax.inject.Named" ), + getType( "Named" ), Collections.singletonList( '"' + mapper.getPackageName() + "." + mapper.getName() + '"' ) ); } private Annotation inject() { - return new Annotation( getTypeFactory().getType( "javax.inject.Inject" ) ); + return new Annotation( getType( "Inject" ) ); + } + + private Type getType(String simpleName) { + if ( getTypeFactory().isTypeAvailable( "javax.inject." + simpleName ) ) { + return getTypeFactory().getType( "javax.inject." + simpleName ); + } + + if ( getTypeFactory().isTypeAvailable( "jakarta.inject." + simpleName ) ) { + return getTypeFactory().getType( "jakarta.inject." + simpleName ); + } + + throw new AnnotationProcessingException( + "Couldn't find any of the JSR330 or Jakarta Dependency Inject types." + + " Are you missing a dependency on your classpath?" ); } } diff --git a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor index 7b27e54faf..cab1d83ed7 100644 --- a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor +++ b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor @@ -4,6 +4,7 @@ org.mapstruct.ap.internal.processor.CdiComponentProcessor org.mapstruct.ap.internal.processor.Jsr330ComponentProcessor +org.mapstruct.ap.internal.processor.JakartaComponentProcessor org.mapstruct.ap.internal.processor.MapperCreationProcessor org.mapstruct.ap.internal.processor.MapperRenderingProcessor org.mapstruct.ap.internal.processor.MethodRetrievalProcessor diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/JakartaJsr330DecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/JakartaJsr330DecoratorTest.java new file mode 100644 index 0000000000..2e0dfcec00 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/JakartaJsr330DecoratorTest.java @@ -0,0 +1,55 @@ +/* + * 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.decorator.jakarta.jsr330; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +@WithClasses({ + Person.class, + PersonDto.class, + Address.class, + AddressDto.class, + PersonMapper.class, + PersonMapperDecorator.class +}) +@IssueKey("2567") +@WithJakartaInject +// We can't use Spring for testing since Spring 5 does not support Jakarta Inject +// However, we can test the generated source code +public class JakartaJsr330DecoratorTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void hasCorrectImports() { + // check the decorator + generatedSource.forMapper( PersonMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "@Singleton" + lineSeparator() + "@Named" ) + .doesNotContain( "javax.inject" ); + // check the plain mapper + generatedSource.forDecoratedMapper( PersonMapper.class ).content() + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "@Singleton" + lineSeparator() + "@Named" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/PersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/PersonMapper.java new file mode 100644 index 0000000000..74ef38439f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/PersonMapper.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.ap.test.decorator.jakarta.jsr330; + +import org.mapstruct.DecoratedWith; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; + +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330) +@DecoratedWith(PersonMapperDecorator.class) +public interface PersonMapper { + + @Mapping( target = "name", ignore = true ) + PersonDto personToPersonDto(Person person); + + AddressDto addressToAddressDto(Address address); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/PersonMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/PersonMapperDecorator.java new file mode 100644 index 0000000000..f16a9f8c1e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/jsr330/PersonMapperDecorator.java @@ -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.decorator.jakarta.jsr330; + +import jakarta.inject.Inject; +import jakarta.inject.Named; + +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; + +public abstract class PersonMapperDecorator implements PersonMapper { + + @Inject + @Named("org.mapstruct.ap.test.decorator.jakarta.jsr330.PersonMapperImpl_") + private PersonMapper delegate; + + @Override + public PersonDto personToPersonDto(Person person) { + PersonDto dto = delegate.personToPersonDto( person ); + dto.setName( person.getFirstName() + " " + person.getLastName() ); + + return dto; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java index e3a5b7c264..548ee61f2e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java @@ -40,5 +40,7 @@ public void componentModelContantsShouldBeEqual() { assertThat( MappingConstants.ComponentModel.CDI ).isEqualTo( MappingConstantsGem.ComponentModelGem.CDI ); assertThat( MappingConstants.ComponentModel.SPRING ).isEqualTo( MappingConstantsGem.ComponentModelGem.SPRING ); assertThat( MappingConstants.ComponentModel.JSR330 ).isEqualTo( MappingConstantsGem.ComponentModelGem.JSR330 ); + assertThat( MappingConstants.ComponentModel.JAKARTA ) + .isEqualTo( MappingConstantsGem.ComponentModelGem.JAKARTA ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/CustomerJakartaDefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/CustomerJakartaDefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..2fb6ae0d07 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/CustomerJakartaDefaultCompileOptionFieldMapper.java @@ -0,0 +1,21 @@ +/* + * 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.injectionstrategy.jakarta._default; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA, + uses = GenderJakartaDefaultCompileOptionFieldMapper.class) +public interface CustomerJakartaDefaultCompileOptionFieldMapper { + + CustomerDto asTarget(CustomerEntity customerEntity); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/GenderJakartaDefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/GenderJakartaDefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..da3409ef41 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/GenderJakartaDefaultCompileOptionFieldMapper.java @@ -0,0 +1,26 @@ +/* + * 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.injectionstrategy.jakarta._default; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA) +public interface GenderJakartaDefaultCompileOptionFieldMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/JakartaAndJsr330DefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/JakartaAndJsr330DefaultCompileOptionFieldMapperTest.java new file mode 100644 index 0000000000..c647605e66 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/JakartaAndJsr330DefaultCompileOptionFieldMapperTest.java @@ -0,0 +1,55 @@ +/* + * 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.injectionstrategy.jakarta._default; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.WithJavaxInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test field injection for component model jakarta. + * Default value option mapstruct.defaultInjectionStrategy is "field" + * + * @author Filip Hrisafov + */ +@IssueKey("2567") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJakartaDefaultCompileOptionFieldMapper.class, + GenderJakartaDefaultCompileOptionFieldMapper.class +}) +@WithJakartaInject +@WithJavaxInject +public class JakartaAndJsr330DefaultCompileOptionFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerJakartaDefaultCompileOptionFieldMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "@Inject" + lineSeparator() + " private GenderJakartaDefaultCompileOptionFieldMapper" ) + .doesNotContain( "public CustomerJakartaDefaultCompileOptionFieldMapperImpl(" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/JakartaDefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/JakartaDefaultCompileOptionFieldMapperTest.java new file mode 100644 index 0000000000..01b43c5b46 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/_default/JakartaDefaultCompileOptionFieldMapperTest.java @@ -0,0 +1,53 @@ +/* + * 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.injectionstrategy.jakarta._default; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test field injection for component model jakarta. + * Default value option mapstruct.defaultInjectionStrategy is "field" + * + * @author Filip Hrisafov + */ +@IssueKey("2567") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJakartaDefaultCompileOptionFieldMapper.class, + GenderJakartaDefaultCompileOptionFieldMapper.class +}) +@WithJakartaInject +public class JakartaDefaultCompileOptionFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerJakartaDefaultCompileOptionFieldMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "@Inject" + lineSeparator() + " private GenderJakartaDefaultCompileOptionFieldMapper" ) + .doesNotContain( "public CustomerJakartaDefaultCompileOptionFieldMapperImpl(" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/CustomerJakartaCompileOptionConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/CustomerJakartaCompileOptionConstructorMapper.java new file mode 100644 index 0000000000..9775d05c87 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/CustomerJakartaCompileOptionConstructorMapper.java @@ -0,0 +1,24 @@ +/* + * 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.injectionstrategy.jakarta.compileoptionconstructor; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Filip Hrisafov + */ +@Mapper( componentModel = MappingConstants.ComponentModel.JAKARTA, + uses = GenderJakartaCompileOptionConstructorMapper.class ) +public interface CustomerJakartaCompileOptionConstructorMapper { + + @Mapping(target = "gender", source = "gender") + CustomerDto asTarget(CustomerEntity customerEntity); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/GenderJakartaCompileOptionConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/GenderJakartaCompileOptionConstructorMapper.java new file mode 100644 index 0000000000..4539bb1c69 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/GenderJakartaCompileOptionConstructorMapper.java @@ -0,0 +1,26 @@ +/* + * 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.injectionstrategy.jakarta.compileoptionconstructor; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA) +public interface GenderJakartaCompileOptionConstructorMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/JakartaAndJsr330CompileOptionConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/JakartaAndJsr330CompileOptionConstructorMapperTest.java new file mode 100644 index 0000000000..c897891b14 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/JakartaAndJsr330CompileOptionConstructorMapperTest.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.injectionstrategy.jakarta.compileoptionconstructor; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.WithJavaxInject; +import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test constructor injection for component model jakarta with compile option + * mapstruct.defaultInjectionStrategy=constructor + * + * @author Filip Hrisafov + */ +@IssueKey("2567") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJakartaCompileOptionConstructorMapper.class, + GenderJakartaCompileOptionConstructorMapper.class +}) +@ProcessorOption(name = "mapstruct.defaultInjectionStrategy", value = "constructor") +@WithJakartaInject +@WithJavaxInject +public class JakartaAndJsr330CompileOptionConstructorMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerJakartaCompileOptionConstructorMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "private final GenderJakartaCompileOptionConstructorMapper" ) + .contains( "@Inject" + lineSeparator() + + " public CustomerJakartaCompileOptionConstructorMapperImpl" + + "(GenderJakartaCompileOptionConstructorMapper" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/JakartaCompileOptionConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/JakartaCompileOptionConstructorMapperTest.java new file mode 100644 index 0000000000..cc36591bb7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/compileoptionconstructor/JakartaCompileOptionConstructorMapperTest.java @@ -0,0 +1,57 @@ +/* + * 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.injectionstrategy.jakarta.compileoptionconstructor; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test constructor injection for component model jakarta with compile option + * mapstruct.defaultInjectionStrategy=constructor + * + * @author Filip Hrisafov + */ +@IssueKey("2567") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJakartaCompileOptionConstructorMapper.class, + GenderJakartaCompileOptionConstructorMapper.class +}) +@ProcessorOption(name = "mapstruct.defaultInjectionStrategy", value = "constructor") +@WithJakartaInject +public class JakartaCompileOptionConstructorMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerJakartaCompileOptionConstructorMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "private final GenderJakartaCompileOptionConstructorMapper" ) + .contains( "@Inject" + lineSeparator() + + " public CustomerJakartaCompileOptionConstructorMapperImpl" + + "(GenderJakartaCompileOptionConstructorMapper" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/ConstructorJakartaConfig.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/ConstructorJakartaConfig.java new file mode 100644 index 0000000000..0bc9bb16e6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/ConstructorJakartaConfig.java @@ -0,0 +1,18 @@ +/* + * 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.injectionstrategy.jakarta.constructor; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.MapperConfig; +import org.mapstruct.MappingConstants; + +/** + * @author Filip Hrisafov + */ +@MapperConfig(componentModel = MappingConstants.ComponentModel.JAKARTA, + injectionStrategy = InjectionStrategy.CONSTRUCTOR) +public interface ConstructorJakartaConfig { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/CustomerJakartaConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/CustomerJakartaConstructorMapper.java new file mode 100644 index 0000000000..73d201cbfb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/CustomerJakartaConstructorMapper.java @@ -0,0 +1,26 @@ +/* + * 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.injectionstrategy.jakarta.constructor; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Filip Hrisafov + */ +@Mapper( componentModel = MappingConstants.ComponentModel.JAKARTA, + uses = GenderJakartaConstructorMapper.class, + injectionStrategy = InjectionStrategy.CONSTRUCTOR ) +public interface CustomerJakartaConstructorMapper { + + @Mapping(target = "gender", source = "gender") + CustomerDto asTarget(CustomerEntity customerEntity); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/GenderJakartaConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/GenderJakartaConstructorMapper.java new file mode 100644 index 0000000000..7cbd0bb3df --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/GenderJakartaConstructorMapper.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.ap.test.injectionstrategy.jakarta.constructor; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Filip Hrisafov + */ +@Mapper(config = ConstructorJakartaConfig.class) +public interface GenderJakartaConstructorMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/JakartaAndJsr330ConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/JakartaAndJsr330ConstructorMapperTest.java new file mode 100644 index 0000000000..03b5dcf2bb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/JakartaAndJsr330ConstructorMapperTest.java @@ -0,0 +1,58 @@ +/* + * 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.injectionstrategy.jakarta.constructor; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.WithJavaxInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import static java.lang.System.lineSeparator; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2567") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJakartaConstructorMapper.class, + GenderJakartaConstructorMapper.class, + ConstructorJakartaConfig.class +}) +@ComponentScan(basePackageClasses = CustomerJakartaConstructorMapper.class) +@Configuration +@WithJakartaInject +@WithJavaxInject +public class JakartaAndJsr330ConstructorMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerJakartaConstructorMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "private final GenderJakartaConstructorMapper" ) + .contains( "@Inject" + lineSeparator() + + " public CustomerJakartaConstructorMapperImpl(GenderJakartaConstructorMapper" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/JakartaConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/JakartaConstructorMapperTest.java new file mode 100644 index 0000000000..45ea7efca8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/constructor/JakartaConstructorMapperTest.java @@ -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.injectionstrategy.jakarta.constructor; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import static java.lang.System.lineSeparator; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2567") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJakartaConstructorMapper.class, + GenderJakartaConstructorMapper.class, + ConstructorJakartaConfig.class +}) +@ComponentScan(basePackageClasses = CustomerJakartaConstructorMapper.class) +@Configuration +@WithJakartaInject +public class JakartaConstructorMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerJakartaConstructorMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "private final GenderJakartaConstructorMapper" ) + .contains( "@Inject" + lineSeparator() + + " public CustomerJakartaConstructorMapperImpl(GenderJakartaConstructorMapper" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/CustomerFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/CustomerFieldMapper.java new file mode 100644 index 0000000000..550d4ee945 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/CustomerFieldMapper.java @@ -0,0 +1,22 @@ +/* + * 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.injectionstrategy.jakarta.field; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA, uses = GenderJakartaFieldMapper.class, + injectionStrategy = InjectionStrategy.FIELD) +public interface CustomerFieldMapper { + + CustomerDto asTarget(CustomerEntity customerEntity); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/FieldJakartaConfig.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/FieldJakartaConfig.java new file mode 100644 index 0000000000..b3ad9474cf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/FieldJakartaConfig.java @@ -0,0 +1,17 @@ +/* + * 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.injectionstrategy.jakarta.field; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.MapperConfig; +import org.mapstruct.MappingConstants; + +/** + * @author Filip Hrisafov + */ +@MapperConfig(componentModel = MappingConstants.ComponentModel.JAKARTA, injectionStrategy = InjectionStrategy.FIELD) +public interface FieldJakartaConfig { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/GenderJakartaFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/GenderJakartaFieldMapper.java new file mode 100644 index 0000000000..ee5b959307 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/GenderJakartaFieldMapper.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.ap.test.injectionstrategy.jakarta.field; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Filip Hrisafov + */ +@Mapper(config = FieldJakartaConfig.class) +public interface GenderJakartaFieldMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/JakartaAndJsr330FieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/JakartaAndJsr330FieldMapperTest.java new file mode 100644 index 0000000000..56ea535571 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/JakartaAndJsr330FieldMapperTest.java @@ -0,0 +1,53 @@ +/* + * 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.injectionstrategy.jakarta.field; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.WithJavaxInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerFieldMapper.class, + GenderJakartaFieldMapper.class, + FieldJakartaConfig.class +}) +@IssueKey("2567") +@WithJakartaInject +@WithJavaxInject +public class JakartaAndJsr330FieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerFieldMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "@Inject" + lineSeparator() + " private GenderJakartaFieldMapper" ) + .doesNotContain( "public CustomerJakartaFieldMapperImpl(" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/JakartaFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/JakartaFieldMapperTest.java new file mode 100644 index 0000000000..2257bcb7ba --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/field/JakartaFieldMapperTest.java @@ -0,0 +1,51 @@ +/* + * 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.injectionstrategy.jakarta.field; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerFieldMapper.class, + GenderJakartaFieldMapper.class, + FieldJakartaConfig.class +}) +@IssueKey("2567") +@WithJakartaInject +public class JakartaFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerFieldMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "@Inject" + lineSeparator() + " private GenderJakartaFieldMapper" ) + .doesNotContain( "public CustomerJakartaFieldMapperImpl(" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java index dd639684b8..6b92d9815a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java @@ -87,6 +87,9 @@ public void shouldConvertToTarget() { public void shouldHaveFieldInjection() { generatedSource.forMapper( CustomerJsr330DefaultCompileOptionFieldMapper.class ) .content() + .contains( "import javax.inject.Inject;" ) + .contains( "import javax.inject.Named;" ) + .contains( "import javax.inject.Singleton;" ) .contains( "@Inject" + lineSeparator() + " private GenderJsr330DefaultCompileOptionFieldMapper" ) .doesNotContain( "public CustomerJsr330DefaultCompileOptionFieldMapperImpl(" ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java index 24bfba5511..fe9cf295f4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java @@ -86,6 +86,9 @@ public void shouldConvertToTarget() { public void shouldHaveConstructorInjectionFromCompileOption() { generatedSource.forMapper( CustomerJsr330CompileOptionConstructorMapper.class ) .content() + .contains( "import javax.inject.Inject;" ) + .contains( "import javax.inject.Named;" ) + .contains( "import javax.inject.Singleton;" ) .contains( "private final GenderJsr330CompileOptionConstructorMapper" ) .contains( "@Inject" + lineSeparator() + " public CustomerJsr330CompileOptionConstructorMapperImpl" + diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java index 8543d57e38..a5cfce5c2e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java @@ -86,6 +86,9 @@ public void shouldConvertToTarget() { public void shouldHaveConstructorInjection() { generatedSource.forMapper( CustomerJsr330ConstructorMapper.class ) .content() + .contains( "import javax.inject.Inject;" ) + .contains( "import javax.inject.Named;" ) + .contains( "import javax.inject.Singleton;" ) .contains( "private final GenderJsr330ConstructorMapper" ) .contains( "@Inject" + lineSeparator() + " public CustomerJsr330ConstructorMapperImpl(GenderJsr330ConstructorMapper" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java index d8cf0c3925..84e67f63a8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java @@ -89,6 +89,9 @@ public void shouldConvertToTarget() { public void shouldHaveFieldInjection() { generatedSource.forMapper( CustomerJsr330FieldMapper.class ) .content() + .contains( "import javax.inject.Inject;" ) + .contains( "import javax.inject.Named;" ) + .contains( "import javax.inject.Singleton;" ) .contains( "@Inject" + lineSeparator() + " private GenderJsr330FieldMapper" ) .doesNotContain( "public CustomerJsr330FieldMapperImpl(" ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/CustomerJsr330DefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/CustomerJsr330DefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..80f5c60ef0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/CustomerJsr330DefaultCompileOptionFieldMapper.java @@ -0,0 +1,21 @@ +/* + * 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.injectionstrategy.jsr330.jakarta; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Andrei Arlou + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330, + uses = GenderJsr330DefaultCompileOptionFieldMapper.class) +public interface CustomerJsr330DefaultCompileOptionFieldMapper { + + CustomerDto asTarget(CustomerEntity customerEntity); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/GenderJsr330DefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/GenderJsr330DefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..4a9b56c1b2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/GenderJsr330DefaultCompileOptionFieldMapper.java @@ -0,0 +1,26 @@ +/* + * 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.injectionstrategy.jsr330.jakarta; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Andrei Arlou + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330) +public interface GenderJsr330DefaultCompileOptionFieldMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/JakartaAndJsr330DefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/JakartaAndJsr330DefaultCompileOptionFieldMapperTest.java new file mode 100644 index 0000000000..da7176fc7a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/JakartaAndJsr330DefaultCompileOptionFieldMapperTest.java @@ -0,0 +1,55 @@ +/* + * 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.injectionstrategy.jsr330.jakarta; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.WithJavaxInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test field injection for component model jsr330. + * Default value option mapstruct.defaultInjectionStrategy is "field" + * + * @author Filip Hrisafov + */ +@IssueKey("2567") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJsr330DefaultCompileOptionFieldMapper.class, + GenderJsr330DefaultCompileOptionFieldMapper.class +}) +@WithJakartaInject +@WithJavaxInject +public class JakartaAndJsr330DefaultCompileOptionFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJavaxInjection() { + generatedSource.forMapper( CustomerJsr330DefaultCompileOptionFieldMapper.class ) + .content() + .contains( "import javax.inject.Inject;" ) + .contains( "import javax.inject.Named;" ) + .contains( "import javax.inject.Singleton;" ) + .contains( "@Inject" + lineSeparator() + " private GenderJsr330DefaultCompileOptionFieldMapper" ) + .doesNotContain( "public CustomerJsr330DefaultCompileOptionFieldMapperImpl(" ) + .doesNotContain( "jakarta.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/JakartaJsr330DefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/JakartaJsr330DefaultCompileOptionFieldMapperTest.java new file mode 100644 index 0000000000..f78721fb1c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/jakarta/JakartaJsr330DefaultCompileOptionFieldMapperTest.java @@ -0,0 +1,53 @@ +/* + * 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.injectionstrategy.jsr330.jakarta; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test field injection for component model jsr330. + * Default value option mapstruct.defaultInjectionStrategy is "field" + * + * @author Filip Hrisafov + */ +@IssueKey("2567") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJsr330DefaultCompileOptionFieldMapper.class, + GenderJsr330DefaultCompileOptionFieldMapper.class +}) +@WithJakartaInject +public class JakartaJsr330DefaultCompileOptionFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerJsr330DefaultCompileOptionFieldMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "@Inject" + lineSeparator() + " private GenderJsr330DefaultCompileOptionFieldMapper" ) + .doesNotContain( "public CustomerJsr330DefaultCompileOptionFieldMapperImpl(" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaInject.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaInject.java new file mode 100644 index 0000000000..ccb0b030a1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaInject.java @@ -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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Spring Dependencies + * + * @author Filip Hrisafov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithTestDependency({ + "jakarta.inject-api", +}) +public @interface WithJakartaInject { + +} From 37835a560770c307b1955a11cb399985bdc6a7b4 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 30 Jan 2022 20:49:05 +0100 Subject: [PATCH 238/597] #2677 Use type without bounds when looking for read / presence accessor in a SourceReference --- .../model/beanmapping/SourceReference.java | 7 +- .../ap/test/bugs/_2677/Issue2677Mapper.java | 117 ++++++++++++++++++ .../ap/test/bugs/_2677/Issue2677Test.java | 92 ++++++++++++++ 3 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2677/Issue2677Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2677/Issue2677Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index d5c18a99f1..8824f95901 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -311,11 +311,12 @@ private List matchWithSourceAccessorTypes(Type type, String[] ent Type newType = type; for ( int i = 0; i < entryNames.length; i++ ) { boolean matchFound = false; - ReadAccessor readAccessor = newType.getReadAccessor( entryNames[i] ); + Type noBoundsType = newType.withoutBounds(); + ReadAccessor readAccessor = noBoundsType.getReadAccessor( entryNames[i] ); if ( readAccessor != null ) { - PresenceCheckAccessor presenceChecker = newType.getPresenceChecker( entryNames[i] ); + PresenceCheckAccessor presenceChecker = noBoundsType.getPresenceChecker( entryNames[i] ); newType = typeFactory.getReturnType( - (DeclaredType) newType.getTypeMirror(), + (DeclaredType) noBoundsType.getTypeMirror(), readAccessor ); sourceEntries.add( forSourceReference( diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2677/Issue2677Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2677/Issue2677Mapper.java new file mode 100644 index 0000000000..e74ad1e770 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2677/Issue2677Mapper.java @@ -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._2677; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2677Mapper { + + Issue2677Mapper INSTANCE = Mappers.getMapper( Issue2677Mapper.class ); + + @Mapping(target = "id", source = "value.id") + Output map(Wrapper in); + + @Mapping(target = ".", source = "value") + Output mapImplicitly(Wrapper in); + + @Mapping(target = "id", source = "value.id") + Output mapFromParent(Wrapper in); + + @Mapping(target = "id", source = "value.id") + Output mapFromChild(Wrapper in); + + @Mapping( target = "value", source = "wrapperValue") + Wrapper mapToWrapper(String wrapperValue, Wrapper wrapper); + + @Mapping(target = "id", source = "value.id") + Output mapWithPresenceCheck(Wrapper in); + + class Wrapper { + private final T value; + private final String status; + + public Wrapper(T value, String status) { + this.value = value; + this.status = status; + } + + public String getStatus() { + return status; + } + + public T getValue() { + return value; + } + } + + class Parent { + private final int id; + + public Parent(int id) { + this.id = id; + } + + public int getId() { + return id; + } + } + + class Child extends Parent { + private final String whatever; + + public Child(int id, String whatever) { + super( id ); + this.whatever = whatever; + } + + public String getWhatever() { + return whatever; + } + } + + class ParentWithPresenceCheck { + private final int id; + + public ParentWithPresenceCheck(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public boolean hasId() { + return id > 10; + } + } + + class Output { + private int id; + private String status; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2677/Issue2677Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2677/Issue2677Test.java new file mode 100644 index 0000000000..0eb4d6d794 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2677/Issue2677Test.java @@ -0,0 +1,92 @@ +/* + * 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._2677; + +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 Filip Hrisafov + */ +@IssueKey("2677") +@WithClasses({ + Issue2677Mapper.class +}) +class Issue2677Test { + + @ProcessorTest + void shouldCorrectlyUseGenericsWithExtends() { + Issue2677Mapper.Parent parent = new Issue2677Mapper.Parent( 10 ); + Issue2677Mapper.Child child = new Issue2677Mapper.Child( 15, "Test" ); + + Issue2677Mapper.Output output = Issue2677Mapper.INSTANCE.map( new Issue2677Mapper.Wrapper<>( + parent, + "extends" + ) ); + + assertThat( output.getStatus() ).isEqualTo( "extends" ); + assertThat( output.getId() ).isEqualTo( 10 ); + + output = Issue2677Mapper.INSTANCE.mapFromChild( new Issue2677Mapper.Wrapper<>( + child, + "child" + ) ); + + assertThat( output.getStatus() ).isEqualTo( "child" ); + assertThat( output.getId() ).isEqualTo( 15 ); + + output = Issue2677Mapper.INSTANCE.mapFromParent( new Issue2677Mapper.Wrapper<>( + parent, + "parent" + ) ); + + assertThat( output.getStatus() ).isEqualTo( "parent" ); + assertThat( output.getId() ).isEqualTo( 10 ); + + output = Issue2677Mapper.INSTANCE.mapImplicitly( new Issue2677Mapper.Wrapper<>( + child, + "implicit" + ) ); + + assertThat( output.getStatus() ).isEqualTo( "implicit" ); + assertThat( output.getId() ).isEqualTo( 15 ); + + Issue2677Mapper.Wrapper result = Issue2677Mapper.INSTANCE.mapToWrapper( + "test", + new Issue2677Mapper.Wrapper<>( + child, + "super" + ) + ); + + assertThat( result.getStatus() ).isEqualTo( "super" ); + assertThat( result.getValue() ).isEqualTo( "test" ); + + output = Issue2677Mapper.INSTANCE.mapWithPresenceCheck( + new Issue2677Mapper.Wrapper<>( + new Issue2677Mapper.ParentWithPresenceCheck( 8 ), + "presenceCheck" + ) + ); + + assertThat( output.getStatus() ).isEqualTo( "presenceCheck" ); + assertThat( output.getId() ).isEqualTo( 0 ); + + output = Issue2677Mapper.INSTANCE.mapWithPresenceCheck( + new Issue2677Mapper.Wrapper<>( + new Issue2677Mapper.ParentWithPresenceCheck( 15 ), + "presenceCheck" + ) + ); + + assertThat( output.getStatus() ).isEqualTo( "presenceCheck" ); + assertThat( output.getId() ).isEqualTo( 15 ); + + } +} From 2a2c11e871001909187cea173d442624e724c64c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 30 Jan 2022 20:52:22 +0100 Subject: [PATCH 239/597] #2629 Use ModuleElement when getting type element Prior to this MapStruct would only use `Elements#getTypeElement`. With this PR if the mapper being generated is within a module MapStruct will use that module for the methods that are needed (getTypeElement and getPackageElement). Adapt the build to require a minimum Java 11 for building the processor module. Adapt the GitHub actions to properly run integration tests with Java 8 Ignore Java 9 usages for the animal-sniffer-plugin --- .github/workflows/main.yml | 36 ++++++-- .../itest/tests/MavenIntegrationTest.java | 8 ++ .../test/resources/lombokModuleTest/pom.xml | 85 +++++++++++++++++++ .../src/main/java/module-info.java | 9 ++ .../org/mapstruct/itest/lombok/Address.java | 18 ++++ .../mapstruct/itest/lombok/AddressDto.java | 19 +++++ .../org/mapstruct/itest/lombok/Person.java | 30 +++++++ .../org/mapstruct/itest/lombok/PersonDto.java | 30 +++++++ .../mapstruct/itest/lombok/PersonMapper.java | 21 +++++ .../itest/lombok/LombokMapperTest.java | 37 ++++++++ parent/pom.xml | 12 ++- processor/pom.xml | 1 + .../org/mapstruct/ap/MappingProcessor.java | 6 +- .../DefaultModelElementProcessorContext.java | 5 +- .../util/AbstractElementUtilsDecorator.java | 33 ++++++- .../util/EclipseElementUtilsDecorator.java | 4 +- .../ap/internal/util/ElementUtils.java | 7 +- .../internal/util/IgnoreJRERequirement.java | 16 ++++ .../util/JavacElementUtilsDecorator.java | 4 +- 19 files changed, 357 insertions(+), 24 deletions(-) create mode 100644 integrationtest/src/test/resources/lombokModuleTest/pom.xml create mode 100644 integrationtest/src/test/resources/lombokModuleTest/src/main/java/module-info.java create mode 100644 integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Address.java create mode 100644 integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/AddressDto.java create mode 100644 integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Person.java create mode 100644 integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonDto.java create mode 100644 integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonMapper.java create mode 100644 integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/IgnoreJRERequirement.java diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b023631628..6d80d2da89 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - java: [11, 13, 16, 17] + java: [13, 16, 17] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: @@ -25,15 +25,15 @@ jobs: - name: 'Test' run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true linux: - name: 'Linux JDK 8' + name: 'Linux JDK 11' runs-on: ubuntu-latest steps: - name: 'Checkout' uses: actions/checkout@v2 - - name: 'Set up JDK 8' + - name: 'Set up JDK 11' uses: actions/setup-java@v1 with: - java-version: 8 + java-version: 11 - name: 'Test' run: ./mvnw ${MAVEN_ARGS} install - name: 'Generate coverage report' @@ -43,25 +43,43 @@ jobs: - name: 'Publish Snapshots' if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'mapstruct/mapstruct' run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy + linux-jdk-8: + name: 'Linux JDK 8' + runs-on: ubuntu-latest + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + - name: 'Set up JDK 11 for building everything' + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: 'Install Processor' + run: ./mvnw ${MAVEN_ARGS} -DskipTests install -pl processor -am + - name: 'Set up JDK 8 for running integration tests' + uses: actions/setup-java@v1 + with: + java-version: 8 + - name: 'Run integration tests' + run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest windows: name: 'Windows' runs-on: windows-latest steps: - uses: actions/checkout@v2 - - name: 'Set up JDK 8' + - name: 'Set up JDK 11' uses: actions/setup-java@v1 with: - java-version: 8 + java-version: 11 - name: 'Test' - run: ./mvnw ${MAVEN_ARGS} install + run: ./mvnw %MAVEN_ARGS% install mac: name: 'Mac OS' runs-on: macos-latest steps: - uses: actions/checkout@v2 - - name: 'Set up JDK 8' + - name: 'Set up JDK 11' uses: actions/setup-java@v1 with: - java-version: 8 + java-version: 11 - name: 'Test' run: ./mvnw ${MAVEN_ARGS} install diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 9696542f58..4f4182d5c7 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -85,6 +85,14 @@ void jsr330Test() { void lombokBuilderTest() { } + @ProcessorTest(baseDir = "lombokModuleTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC, + ProcessorTest.ProcessorType.JAVAC_WITH_PATHS + }) + @EnabledForJreRange(min = JRE.JAVA_11) + void lombokModuleTest() { + } + @ProcessorTest(baseDir = "namingStrategyTest", processorTypes = { ProcessorTest.ProcessorType.JAVAC }) diff --git a/integrationtest/src/test/resources/lombokModuleTest/pom.xml b/integrationtest/src/test/resources/lombokModuleTest/pom.xml new file mode 100644 index 0000000000..16b32cfb4f --- /dev/null +++ b/integrationtest/src/test/resources/lombokModuleTest/pom.xml @@ -0,0 +1,85 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + lombokModuleIntegrationTest + jar + + + 11 + 11 + + + + + org.projectlombok + lombok + compile + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + compile + + + + + + generate-via-compiler-plugin-with-annotation-processor-paths + + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + \${compiler-id} + + + ${project.groupId} + mapstruct-processor + ${mapstruct.version} + + + org.projectlombok + lombok + 1.18.22 + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + + + + org.eclipse.tycho + tycho-compiler-jdt + ${org.eclipse.tycho.compiler-jdt.version} + + + + + + + + diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/module-info.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/module-info.java new file mode 100644 index 0000000000..9bc85bea86 --- /dev/null +++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/module-info.java @@ -0,0 +1,9 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +module org.example { + requires org.mapstruct; + requires lombok; +} diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Address.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Address.java new file mode 100644 index 0000000000..f83c6881ce --- /dev/null +++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Address.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.lombok; + +public class Address { + private final String addressLine; + + public Address(String addressLine) { + this.addressLine = addressLine; + } + + public String getAddressLine() { + return addressLine; + } +} diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/AddressDto.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/AddressDto.java new file mode 100644 index 0000000000..d6b4f30f88 --- /dev/null +++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/AddressDto.java @@ -0,0 +1,19 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.lombok; + +public class AddressDto { + + private final String addressLine; + + public AddressDto(String addressLine) { + this.addressLine = addressLine; + } + + public String getAddressLine() { + return addressLine; + } +} diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Person.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Person.java new file mode 100644 index 0000000000..b6ca47fca1 --- /dev/null +++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Person.java @@ -0,0 +1,30 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.lombok; + +public class Person { + private final String name; + private final int age; + private final Address address; + + public Person(String name, int age, Address address) { + this.name = name; + this.age = age; + this.address = address; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public Address getAddress() { + return address; + } +} diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonDto.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonDto.java new file mode 100644 index 0000000000..3223778663 --- /dev/null +++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonDto.java @@ -0,0 +1,30 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.lombok; + +public class PersonDto { + private final String name; + private final int age; + private final AddressDto address; + + public PersonDto(String name, int age, AddressDto address) { + this.name = name; + this.age = age; + this.address = address; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public AddressDto getAddress() { + return address; + } +} diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonMapper.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonMapper.java new file mode 100644 index 0000000000..27184bad27 --- /dev/null +++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonMapper.java @@ -0,0 +1,21 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.lombok; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface PersonMapper { + + PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class ); + + Person fromDto(PersonDto personDto); + PersonDto toDto(Person personDto); +} diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java b/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java new file mode 100644 index 0000000000..f8b702896d --- /dev/null +++ b/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.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.itest.lombok; + +import org.junit.Test; +import org.mapstruct.factory.Mappers; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for generation of Lombok Builder Mapper implementations + * + * @author Eric Martineau + */ +public class LombokMapperTest { + + @Test + public void testSimpleImmutableBuilderHappyPath() { + PersonDto personDto = PersonMapper.INSTANCE.toDto( new Person( "Bob", 33, new Address( "Wild Drive" ) ) ); + assertThat( personDto.getAge() ).isEqualTo( 33 ); + assertThat( personDto.getName() ).isEqualTo( "Bob" ); + assertThat( personDto.getAddress() ).isNotNull(); + assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "Wild Drive" ); + } + + @Test + public void testLombokToImmutable() { + Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) ); + assertThat( person.getAge() ).isEqualTo( 33 ); + assertThat( person.getName() ).isEqualTo( "Bob" ); + assertThat( person.getAddress() ).isNotNull(); + assertThat( person.getAddress().getAddressLine() ).isEqualTo( "Wild Drive" ); + } +} diff --git a/parent/pom.xml b/parent/pom.xml index 1f75e9d6d5..4ca3d9b2c0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -36,6 +36,11 @@ jdt_apt + + 1.8 @@ -214,7 +219,7 @@ org.projectlombok lombok - 1.18.20 + 1.18.22 org.immutables @@ -666,6 +671,9 @@ java18 1.0 + + org.mapstruct.ap.internal.util.IgnoreJRERequirement + @@ -685,7 +693,7 @@ - [1.8,) + [${minimum.java.version},) diff --git a/processor/pom.xml b/processor/pom.xml index 2a3c38abfc..ec3f20a241 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -24,6 +24,7 @@ + 11 diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 0cc6b7cce3..4f9e0fa384 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -269,7 +269,11 @@ private void processMapperElements(Set mapperElements, RoundContext // of one outer interface List tst = mapperElement.getEnclosedElements(); ProcessorContext context = new DefaultModelElementProcessorContext( - processingEnv, options, roundContext, getDeclaredTypesNotToBeImported( mapperElement ) + processingEnv, + options, + roundContext, + getDeclaredTypesNotToBeImported( mapperElement ), + mapperElement ); processMapperTypeElement( context, mapperElement ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java index d6a05cec32..51ea5dd786 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java @@ -13,6 +13,7 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; import org.mapstruct.ap.internal.model.common.TypeFactory; @@ -46,14 +47,14 @@ public class DefaultModelElementProcessorContext implements ProcessorContext { private final RoundContext roundContext; public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options, - RoundContext roundContext, Map notToBeImported) { + RoundContext roundContext, Map notToBeImported, TypeElement mapperElement) { this.processingEnvironment = processingEnvironment; this.messager = new DelegatingMessager( processingEnvironment.getMessager(), options.isVerbose() ); this.accessorNaming = roundContext.getAnnotationProcessorContext().getAccessorNaming(); this.versionInformation = DefaultVersionInformation.fromProcessingEnvironment( processingEnvironment ); this.delegatingTypes = TypeUtils.create( processingEnvironment, versionInformation ); - this.delegatingElements = ElementUtils.create( processingEnvironment, versionInformation ); + this.delegatingElements = ElementUtils.create( processingEnvironment, versionInformation, mapperElement ); this.roundContext = roundContext; this.typeFactory = new TypeFactory( delegatingElements, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java index 14734b9c8e..72a44d106f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java @@ -13,11 +13,13 @@ import java.util.ListIterator; import java.util.Map; import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; +import javax.lang.model.element.ModuleElement; import javax.lang.model.element.Name; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; @@ -35,19 +37,44 @@ public abstract class AbstractElementUtilsDecorator implements ElementUtils { private final Elements delegate; + /** + * The module element when running with the module system, + * {@code null} otherwise. + */ + private final Element moduleElement; - AbstractElementUtilsDecorator(ProcessingEnvironment processingEnv) { + @IgnoreJRERequirement + AbstractElementUtilsDecorator(ProcessingEnvironment processingEnv, TypeElement mapperElement) { this.delegate = processingEnv.getElementUtils(); + if ( SourceVersion.RELEASE_8.compareTo( processingEnv.getSourceVersion() ) >= 0 ) { + // We are running with Java 8 or lower + this.moduleElement = null; + } + else { + this.moduleElement = this.delegate.getModuleOf( mapperElement ); + } } @Override + @IgnoreJRERequirement public PackageElement getPackageElement(CharSequence name) { - return delegate.getPackageElement( name ); + if ( this.moduleElement == null ) { + return this.delegate.getPackageElement( name ); + } + + // If the module element is not null then we must be running on Java 8+ + return this.delegate.getPackageElement( (ModuleElement) moduleElement, name ); } @Override + @IgnoreJRERequirement public TypeElement getTypeElement(CharSequence name) { - return delegate.getTypeElement( name ); + if ( this.moduleElement == null ) { + return this.delegate.getTypeElement( name ); + } + + // If the module element is not null then we must be running on Java 8+ + return this.delegate.getTypeElement( (ModuleElement) moduleElement, name ); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java index aaf1bb7e20..3c1dc84d36 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java @@ -13,8 +13,8 @@ public class EclipseElementUtilsDecorator extends AbstractElementUtilsDecorator private final Elements delegate; - EclipseElementUtilsDecorator(ProcessingEnvironment processingEnv) { - super( processingEnv ); + EclipseElementUtilsDecorator(ProcessingEnvironment processingEnv, TypeElement mapperElement) { + super( processingEnv, mapperElement ); this.delegate = processingEnv.getElementUtils(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java index fdd221c1a6..3e026cde80 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java @@ -16,12 +16,13 @@ public interface ElementUtils extends Elements { - static ElementUtils create(ProcessingEnvironment processingEnvironment, VersionInformation info ) { + static ElementUtils create(ProcessingEnvironment processingEnvironment, VersionInformation info, + TypeElement mapperElement) { if ( info.isEclipseJDTCompiler() ) { - return new EclipseElementUtilsDecorator( processingEnvironment ); + return new EclipseElementUtilsDecorator( processingEnvironment, mapperElement ); } else { - return new JavacElementUtilsDecorator( processingEnvironment ); + return new JavacElementUtilsDecorator( processingEnvironment, mapperElement ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/IgnoreJRERequirement.java b/processor/src/main/java/org/mapstruct/ap/internal/util/IgnoreJRERequirement.java new file mode 100644 index 0000000000..565ebcce12 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/IgnoreJRERequirement.java @@ -0,0 +1,16 @@ +/* + * 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.internal.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE }) +public @interface IgnoreJRERequirement { +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java index ad035743cf..fa0e46171b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java @@ -10,8 +10,8 @@ public class JavacElementUtilsDecorator extends AbstractElementUtilsDecorator { - JavacElementUtilsDecorator(ProcessingEnvironment processingEnv) { - super( processingEnv ); + JavacElementUtilsDecorator(ProcessingEnvironment processingEnv, TypeElement mapperElement) { + super( processingEnv, mapperElement ); } @Override From 7bb85d05c0a7f8dc755c271a37e8feb3b09a592e Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sun, 6 Feb 2022 20:03:23 +0100 Subject: [PATCH 240/597] 2696: Invert `@SubclassMappings` with `@InheritInverseConfiguration`. (#2708) * #2696: Added support for '@InheritInverseConfiguration' with '@SubclassMappings'. * #2696: Overriding of inverse inheritence implemented. New order has preference over inherited order. --- .../ap/internal/model/BeanMappingMethod.java | 1 + .../model/source/MappingMethodOptions.java | 50 ++++++++++--- .../internal/model/source/SourceMethod.java | 14 +++- .../model/source/SubclassMappingOptions.java | 72 ++++++++++++------ .../model/source/SubclassValidator.java | 19 +++-- .../processor/MapperCreationProcessor.java | 36 ++++++--- .../processor/MethodRetrievalProcessor.java | 22 ++++-- .../mapstruct/ap/internal/util/Message.java | 1 + .../ErroneousInverseSubclassMapper.java | 29 ++++++++ .../InverseOrderSubclassMapper.java | 40 ++++++++++ .../subclassmapping/SimpleSubclassMapper.java | 6 ++ .../subclassmapping/SubclassMappingTest.java | 74 +++++++++++++++---- .../fixture/SubSourceOverride.java | 18 +++++ .../fixture/SubSourceSeparate.java | 18 +++++ .../fixture/SubTargetSeparate.java | 18 +++++ .../fixture/SubclassAbstractMapper.java | 6 ++ .../fixture/SubclassFixtureTest.java | 4 + .../fixture/SubclassAbstractMapperImpl.java | 62 +++++++++++++++- 18 files changed, 417 insertions(+), 73 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousInverseSubclassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/InverseOrderSubclassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceOverride.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceSeparate.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTargetSeparate.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index bdfd8cf54a..267702826b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -157,6 +157,7 @@ public Builder forgedMethod(ForgedMethod forgedMethod) { return this; } + @Override public BeanMappingMethod build() { BeanMappingOptions beanMapping = method.getOptions().getBeanMapping(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 45b1175f8d..4a140c4fb9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import javax.lang.model.element.AnnotationMirror; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.model.common.Type; @@ -35,7 +36,8 @@ public class MappingMethodOptions { null, null, Collections.emptyList(), - Collections.emptySet() + Collections.emptySet(), + null ); private MapperOptions mapper; @@ -46,14 +48,16 @@ public class MappingMethodOptions { private EnumMappingOptions enumMappingOptions; private List valueMappings; private boolean fullyInitialized; - private Set subclassMapping; + private Set subclassMappings; + + private SubclassValidator subclassValidator; public MappingMethodOptions(MapperOptions mapper, Set mappings, IterableMappingOptions iterableMapping, MapMappingOptions mapMapping, BeanMappingOptions beanMapping, EnumMappingOptions enumMappingOptions, List valueMappings, - Set subclassMapping) { + Set subclassMappings, SubclassValidator subclassValidator) { this.mapper = mapper; this.mappings = mappings; this.iterableMapping = iterableMapping; @@ -61,7 +65,8 @@ public MappingMethodOptions(MapperOptions mapper, Set mappings, this.beanMapping = beanMapping; this.enumMappingOptions = enumMappingOptions; this.valueMappings = valueMappings; - this.subclassMapping = subclassMapping; + this.subclassMappings = subclassMappings; + this.subclassValidator = subclassValidator; } /** @@ -102,7 +107,7 @@ public List getValueMappings() { } public Set getSubclassMappings() { - return subclassMapping; + return subclassMappings; } public void setIterableMapping(IterableMappingOptions iterableMapping) { @@ -144,10 +149,13 @@ public void markAsFullyInitialized() { /** * Merges in all the mapping options configured, giving the already defined options precedence. * + * @param sourceMethod the method which inherits the options. * @param templateMethod the template method with the options to inherit, may be {@code null} * @param isInverse if {@code true}, the specified options are from an inverse method + * @param annotationMirror the annotation on which the compile errors will be shown. */ - public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse) { + public void applyInheritedOptions(SourceMethod sourceMethod, SourceMethod templateMethod, boolean isInverse, + AnnotationMirror annotationMirror) { MappingMethodOptions templateOptions = templateMethod.getOptions(); if ( null != templateOptions ) { if ( !getIterableMapping().hasAnnotation() && templateOptions.getIterableMapping().hasAnnotation() ) { @@ -184,7 +192,7 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } else { if ( templateOptions.getValueMappings() != null ) { - // iff there are also inherited mappings, we inverse and add them. + // if there are also inherited mappings, we inverse and add them. for ( ValueMappingOptions inheritedValueMapping : templateOptions.getValueMappings() ) { ValueMappingOptions valueMapping = isInverse ? inheritedValueMapping.inverse() : inheritedValueMapping; @@ -196,7 +204,13 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } } - // Do NOT inherit subclass mapping options!!! + if ( isInverse ) { + // normal inheritence of subclass mappings will result runtime in infinite loops. + List inheritedMappings = SubclassMappingOptions.copyForInverseInheritance( + templateOptions.getSubclassMappings(), + getBeanMapping() ); + addAllNonRedefined( sourceMethod, annotationMirror, inheritedMappings ); + } Set newMappings = new LinkedHashSet<>(); for ( MappingOptions mapping : templateOptions.getMappings() ) { @@ -218,6 +232,21 @@ public void applyInheritedOptions(SourceMethod templateMethod, boolean isInverse } } + private void addAllNonRedefined(SourceMethod sourceMethod, AnnotationMirror annotationMirror, + List inheritedMappings) { + Set redefinedSubclassMappings = new HashSet<>( subclassMappings ); + for ( SubclassMappingOptions subclassMappingOption : inheritedMappings ) { + if ( !redefinedSubclassMappings.contains( subclassMappingOption ) ) { + if ( subclassValidator.isValidUsage( + sourceMethod.getExecutable(), + annotationMirror, + subclassMappingOption.getSource() ) ) { + subclassMappings.add( subclassMappingOption ); + } + } + } + } + private void addAllNonRedefined(Set inheritedMappings) { Set redefinedSources = new HashSet<>(); Set redefinedTargets = new HashSet<>(); @@ -328,7 +357,7 @@ private String getFirstTargetPropertyName(MappingOptions mapping) { /** * SubclassMappingOptions are not inherited to forged methods. They would result in an infinite loop if they were. * - * @return a MappingMethodOptions without SubclassMappingOptions. + * @return a MappingMethodOptions without SubclassMappingOptions or SubclassValidator. */ public static MappingMethodOptions getForgedMethodInheritedOptions(MappingMethodOptions options) { return new MappingMethodOptions( @@ -339,7 +368,8 @@ public static MappingMethodOptions getForgedMethodInheritedOptions(MappingMethod options.beanMapping, options.enumMappingOptions, options.valueMappings, - Collections.emptySet() ); + Collections.emptySet(), + null ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 5895ecba2a..92180bca8a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -16,15 +16,14 @@ import javax.lang.model.element.Modifier; import org.mapstruct.ap.internal.gem.ConditionGem; -import org.mapstruct.ap.internal.util.TypeUtils; - +import org.mapstruct.ap.internal.gem.ObjectFactoryGem; import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.gem.ObjectFactoryGem; import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.internal.util.TypeUtils; import static org.mapstruct.ap.internal.model.source.MappingMethodUtils.isEnumMapping; import static org.mapstruct.ap.internal.util.Collections.first; @@ -98,6 +97,7 @@ public static class Builder { private Set subclassMappings; private boolean verboseLogging; + private SubclassValidator subclassValidator; public Builder setDeclaringMapper(Type declaringMapper) { this.declaringMapper = declaringMapper; @@ -159,6 +159,11 @@ public Builder setSubclassMappings(Set subclassMappings) return this; } + public Builder setSubclassValidator(SubclassValidator subclassValidator) { + this.subclassValidator = subclassValidator; + return this; + } + public Builder setTypeUtils(TypeUtils typeUtils) { this.typeUtils = typeUtils; return this; @@ -212,7 +217,8 @@ public SourceMethod build() { beanMapping, enumMappingOptions, valueMappings, - subclassMappings + subclassMappings, + subclassValidator ); this.typeParameters = this.executable.getTypeParameters() diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java index 54a0ba4565..08df8083bc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.internal.model.source; +import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.lang.model.element.ExecutableElement; @@ -31,11 +32,13 @@ public class SubclassMappingOptions extends DelegatingOptions { private final TypeMirror source; private final TypeMirror target; + private final TypeUtils typeUtils; - public SubclassMappingOptions(TypeMirror source, TypeMirror target, DelegatingOptions next) { + public SubclassMappingOptions(TypeMirror source, TypeMirror target, TypeUtils typeUtils, DelegatingOptions next) { super( next ); this.source = source; this.target = target; + this.typeUtils = typeUtils; } @Override @@ -84,7 +87,9 @@ private static boolean isConsistent(SubclassMappingGem gem, ExecutableElement me targetSubclass.toString() ); isConsistent = false; } - subclassValidator.isInCorrectOrder( method, gem.mirror(), targetSubclass ); + if ( !subclassValidator.isValidUsage( method, gem.mirror(), sourceSubclass ) ) { + isConsistent = false; + } return isConsistent; } @@ -115,10 +120,10 @@ public TypeMirror getTarget() { public static void addInstances(SubclassMappingsGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, FormattingMessager messager, TypeUtils typeUtils, Set mappings, - List sourceParameters, Type resultType) { - SubclassValidator subclassValidator = new SubclassValidator( messager, typeUtils ); + List sourceParameters, Type resultType, + SubclassValidator subclassValidator) { for ( SubclassMappingGem subclassMappingGem : gem.value().get() ) { - addAndValidateInstance( + addInstance( subclassMappingGem, method, beanMappingOptions, @@ -134,24 +139,8 @@ public static void addInstances(SubclassMappingsGem gem, ExecutableElement metho public static void addInstance(SubclassMappingGem subclassMapping, ExecutableElement method, BeanMappingOptions beanMappingOptions, FormattingMessager messager, TypeUtils typeUtils, Set mappings, - List sourceParameters, Type resultType) { - addAndValidateInstance( - subclassMapping, - method, - beanMappingOptions, - messager, - typeUtils, - mappings, - sourceParameters, - resultType, - new SubclassValidator( messager, typeUtils ) ); - } - - private static void addAndValidateInstance(SubclassMappingGem subclassMapping, ExecutableElement method, - BeanMappingOptions beanMappingOptions, FormattingMessager messager, - TypeUtils typeUtils, Set mappings, - List sourceParameters, Type resultType, - SubclassValidator subclassValidator) { + List sourceParameters, Type resultType, + SubclassValidator subclassValidator) { if ( !isConsistent( subclassMapping, method, @@ -166,6 +155,41 @@ private static void addAndValidateInstance(SubclassMappingGem subclassMapping, E TypeMirror sourceSubclass = subclassMapping.source().getValue(); TypeMirror targetSubclass = subclassMapping.target().getValue(); - mappings.add( new SubclassMappingOptions( sourceSubclass, targetSubclass, beanMappingOptions ) ); + mappings + .add( + new SubclassMappingOptions( + sourceSubclass, + targetSubclass, + typeUtils, + beanMappingOptions ) ); + } + + public static List copyForInverseInheritance(Set subclassMappings, + BeanMappingOptions beanMappingOptions) { + // we are not interested in keeping it unique at this point. + List mappings = new ArrayList<>(); + for ( SubclassMappingOptions subclassMapping : subclassMappings ) { + mappings.add( + new SubclassMappingOptions( + subclassMapping.target, + subclassMapping.source, + subclassMapping.typeUtils, + beanMappingOptions ) ); + } + return mappings; + } + + @Override + public boolean equals(Object obj) { + if ( obj == null || !( obj instanceof SubclassMappingOptions ) ) { + return false; + } + SubclassMappingOptions other = (SubclassMappingOptions) obj; + return typeUtils.isSameType( source, other.source ); + } + + @Override + public int hashCode() { + return 1; // use a stable value because TypeMirror is not safe to use for hashCode. } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassValidator.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassValidator.java index dd79ba0c02..601b588e42 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassValidator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassValidator.java @@ -20,19 +20,28 @@ * * @author Ben Zegveld */ -class SubclassValidator { +public class SubclassValidator { private final FormattingMessager messager; private final List handledSubclasses = new ArrayList<>(); private final TypeUtils typeUtils; - SubclassValidator(FormattingMessager messager, TypeUtils typeUtils) { + public SubclassValidator(FormattingMessager messager, TypeUtils typeUtils) { this.messager = messager; this.typeUtils = typeUtils; } - public boolean isInCorrectOrder(Element e, AnnotationMirror annotation, TypeMirror sourceType) { + public boolean isValidUsage(Element e, AnnotationMirror annotation, TypeMirror sourceType) { for ( TypeMirror typeMirror : handledSubclasses ) { + if ( typeUtils.isSameType( sourceType, typeMirror ) ) { + messager + .printMessage( + e, + annotation, + Message.SUBCLASSMAPPING_DOUBLE_SOURCE_SUBCLASS, + sourceType ); + return false; + } if ( typeUtils.isAssignable( sourceType, typeMirror ) ) { messager .printMessage( @@ -43,11 +52,11 @@ public boolean isInCorrectOrder(Element e, AnnotationMirror annotation, TypeMirr typeMirror, sourceType, typeMirror ); - return true; + return false; } } handledSubclasses.add( sourceType ); - return false; + return true; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index bae0cda1ae..d8acb10398 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -14,6 +14,7 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -320,7 +321,7 @@ private List getMappingMethods(MapperOptions mapperAnnotation, Li continue; } - mergeInheritedOptions( method, mapperAnnotation, methods, new ArrayList<>() ); + mergeInheritedOptions( method, mapperAnnotation, methods, new ArrayList<>(), null ); MappingMethodOptions mappingOptions = method.getOptions(); @@ -462,7 +463,8 @@ private M createWithElementMappingMethod(Sour } private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConfig, - List availableMethods, List initializingMethods) { + List availableMethods, List initializingMethods, + AnnotationMirror annotationMirror) { if ( initializingMethods.contains( method ) ) { // cycle detected @@ -497,10 +499,10 @@ private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConf // apply defined (@InheritConfiguration, @InheritInverseConfiguration) mappings if ( forwardTemplateMethod != null ) { - mappingOptions.applyInheritedOptions( forwardTemplateMethod, false ); + mappingOptions.applyInheritedOptions( method, forwardTemplateMethod, false, annotationMirror ); } if ( inverseTemplateMethod != null ) { - mappingOptions.applyInheritedOptions( inverseTemplateMethod, true ); + mappingOptions.applyInheritedOptions( method, inverseTemplateMethod, true, annotationMirror ); } // apply auto inherited options @@ -510,7 +512,8 @@ private void mergeInheritedOptions(SourceMethod method, MapperOptions mapperConf // but.. there should not be an @InheritedConfiguration if ( forwardTemplateMethod == null && inheritanceStrategy.isApplyForward() ) { if ( applicablePrototypeMethods.size() == 1 ) { - mappingOptions.applyInheritedOptions( first( applicablePrototypeMethods ), false ); + mappingOptions.applyInheritedOptions( method, first( applicablePrototypeMethods ), false, + annotationMirror ); } else if ( applicablePrototypeMethods.size() > 1 ) { messager.printMessage( @@ -523,7 +526,8 @@ else if ( applicablePrototypeMethods.size() > 1 ) { // or no @InheritInverseConfiguration if ( inverseTemplateMethod == null && inheritanceStrategy.isApplyReverse() ) { if ( applicableReversePrototypeMethods.size() == 1 ) { - mappingOptions.applyInheritedOptions( first( applicableReversePrototypeMethods ), true ); + mappingOptions.applyInheritedOptions( method, first( applicableReversePrototypeMethods ), true, + annotationMirror ); } else if ( applicableReversePrototypeMethods.size() > 1 ) { messager.printMessage( @@ -607,16 +611,23 @@ else if ( nameFilteredcandidates.size() > 1 ) { } } - return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods ); + return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods, + getAnnotationMirror( inverseConfiguration ) ); + } + + private AnnotationMirror getAnnotationMirror(InheritInverseConfigurationGem inverseConfiguration) { + return inverseConfiguration == null ? null : inverseConfiguration.mirror(); } private SourceMethod extractInitializedOptions(SourceMethod resultMethod, List rawMethods, MapperOptions mapperConfig, - List initializingMethods) { + List initializingMethods, + AnnotationMirror annotationMirror) { if ( resultMethod != null ) { if ( !resultMethod.getOptions().isFullyInitialized() ) { - mergeInheritedOptions( resultMethod, mapperConfig, rawMethods, initializingMethods ); + mergeInheritedOptions( resultMethod, mapperConfig, rawMethods, initializingMethods, + annotationMirror ); } return resultMethod; @@ -684,7 +695,12 @@ else if ( nameFilteredcandidates.size() > 1 ) { } } - return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods ); + return extractInitializedOptions( resultMethod, rawMethods, mapperConfig, initializingMethods, + getAnnotationMirror( inheritConfiguration ) ); + } + + private AnnotationMirror getAnnotationMirror(InheritConfigurationGem inheritConfiguration) { + return inheritConfiguration == null ? null : inheritConfiguration.mirror(); } private void reportErrorWhenAmbigousReverseMapping(List candidates, SourceMethod method, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index a31fb41a7a..201f1a1f22 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -45,6 +45,7 @@ import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.SubclassMappingOptions; +import org.mapstruct.ap.internal.model.source.SubclassValidator; import org.mapstruct.ap.internal.model.source.ValueMappingOptions; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.util.AccessorNamingUtils; @@ -307,11 +308,13 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, // We want to get as much error reporting as possible. // If targetParameter is not null it means we have an update method + SubclassValidator subclassValidator = new SubclassValidator( messager, typeUtils ); Set subclassMappingOptions = getSubclassMappings( sourceParameters, targetParameter != null ? null : resultType, method, - beanMappingOptions + beanMappingOptions, + subclassValidator ); return new SourceMethod.Builder() @@ -327,6 +330,7 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, .setValueMappingOptionss( getValueMappings( method ) ) .setEnumMappingOptions( enumMappingOptions ) .setSubclassMappings( subclassMappingOptions ) + .setSubclassValidator( subclassValidator ) .setTypeUtils( typeUtils ) .setTypeFactory( typeFactory ) .setPrototypeMethods( prototypeMethods ) @@ -601,8 +605,10 @@ private Set getMappings(ExecutableElement method, BeanMappingOpt * @return The subclass mappings for the given method */ private Set getSubclassMappings(List sourceParameters, Type resultType, - ExecutableElement method, BeanMappingOptions beanMapping) { - return new RepeatableSubclassMappings( sourceParameters, resultType ).getMappings( method, beanMapping ); + ExecutableElement method, BeanMappingOptions beanMapping, + SubclassValidator validator) { + return new RepeatableSubclassMappings( sourceParameters, resultType, validator ) + .getMappings( method, beanMapping ); } private class RepeatableMappings extends RepeatableMappingAnnotations { @@ -637,11 +643,13 @@ private class RepeatableSubclassMappings extends RepeatableMappingAnnotations { private final List sourceParameters; private final Type resultType; + private SubclassValidator validator; - RepeatableSubclassMappings(List sourceParameters, Type resultType) { + RepeatableSubclassMappings(List sourceParameters, Type resultType, SubclassValidator validator) { super( SUB_CLASS_MAPPING_FQN, SUB_CLASS_MAPPINGS_FQN ); this.sourceParameters = sourceParameters; this.resultType = resultType; + this.validator = validator; } @Override @@ -666,7 +674,8 @@ void addInstance(SubclassMappingGem gem, ExecutableElement method, BeanMappingOp typeUtils, mappings, sourceParameters, - resultType ); + resultType, + validator ); } @Override @@ -681,7 +690,8 @@ void addInstances(SubclassMappingsGem gem, ExecutableElement method, BeanMapping typeUtils, mappings, sourceParameters, - resultType ); + resultType, + validator ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 8588ec183a..6d8fed1a95 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -117,6 +117,7 @@ public enum Message { ENUMMAPPING_NO_ELEMENTS( "'nameTransformationStrategy', 'configuration' and 'unexpectedValueMappingException' are undefined in @EnumMapping, define at least one of them." ), ENUMMAPPING_ILLEGAL_TRANSFORMATION( "Illegal transformation for '%s' EnumTransformationStrategy. Error: '%s'." ), + SUBCLASSMAPPING_DOUBLE_SOURCE_SUBCLASS( "Subclass '%s' is already defined as a source." ), SUBCLASSMAPPING_ILLEGAL_SUBCLASS( "Class '%s' is not a subclass of '%s'." ), SUBCLASSMAPPING_NO_VALID_SUPERCLASS( "Could not find a parameter that is a superclass for '%s'." ), SUBCLASSMAPPING_UPDATE_METHODS_NOT_SUPPORTED( "SubclassMapping annotation can not be used for update methods." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousInverseSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousInverseSubclassMapper.java new file mode 100644 index 0000000000..a6cbba2cbc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/ErroneousInverseSubclassMapper.java @@ -0,0 +1,29 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousInverseSubclassMapper { + ErroneousInverseSubclassMapper INSTANCE = Mappers.getMapper( ErroneousInverseSubclassMapper.class ); + + @SubclassMapping( source = Bike.class, target = VehicleDto.class ) + @SubclassMapping( source = Car.class, target = VehicleDto.class ) + @Mapping( target = "maker", source = "vehicleManufacturingCompany" ) + VehicleDto map(Vehicle vehicle); + + @InheritInverseConfiguration + Vehicle mapInverse(VehicleDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/InverseOrderSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/InverseOrderSubclassMapper.java new file mode 100644 index 0000000000..302c036488 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/InverseOrderSubclassMapper.java @@ -0,0 +1,40 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; +import org.mapstruct.ap.test.subclassmapping.mappables.HatchBack; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollection; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollectionDto; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface InverseOrderSubclassMapper { + InverseOrderSubclassMapper INSTANCE = Mappers.getMapper( InverseOrderSubclassMapper.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + @SubclassMapping( source = HatchBack.class, target = CarDto.class ) + @SubclassMapping( source = Car.class, target = CarDto.class ) + @SubclassMapping( source = Bike.class, target = BikeDto.class ) + @Mapping( source = "vehicleManufacturingCompany", target = "maker") + VehicleDto map(Vehicle vehicle); + + VehicleCollection mapInverse(VehicleCollectionDto vehicles); + + @SubclassMapping( source = CarDto.class, target = Car.class ) + @InheritInverseConfiguration + Vehicle mapInverse(VehicleDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SimpleSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SimpleSubclassMapper.java index eb71797864..6fedf2c57a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SimpleSubclassMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SimpleSubclassMapper.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.subclassmapping; +import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.SubclassMapping; @@ -28,4 +29,9 @@ public interface SimpleSubclassMapper { @SubclassMapping( source = Bike.class, target = BikeDto.class ) @Mapping( source = "vehicleManufacturingCompany", target = "maker") VehicleDto map(Vehicle vehicle); + + VehicleCollection mapInverse(VehicleCollectionDto vehicles); + + @InheritInverseConfiguration + Vehicle mapInverse(VehicleDto dto); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java index e5a77b56d3..7df555ecc2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java @@ -34,12 +34,11 @@ VehicleCollectionDto.class, Vehicle.class, VehicleDto.class, - SimpleSubclassMapper.class, - SubclassMapperUsingExistingMappings.class, }) public class SubclassMappingTest { @ProcessorTest + @WithClasses( SimpleSubclassMapper.class ) void mappingIsDoneUsingSubclassMapping() { VehicleCollection vehicles = new VehicleCollection(); vehicles.getVehicles().add( new Car() ); @@ -54,6 +53,22 @@ void mappingIsDoneUsingSubclassMapping() { } @ProcessorTest + @WithClasses( SimpleSubclassMapper.class ) + void inverseMappingIsDoneUsingSubclassMapping() { + VehicleCollectionDto vehicles = new VehicleCollectionDto(); + vehicles.getVehicles().add( new CarDto() ); + vehicles.getVehicles().add( new BikeDto() ); + + VehicleCollection result = SimpleSubclassMapper.INSTANCE.mapInverse( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( Car.class, Bike.class ); + } + + @ProcessorTest + @WithClasses( SubclassMapperUsingExistingMappings.class ) void existingMappingsAreUsedWhenFound() { VehicleCollection vehicles = new VehicleCollection(); vehicles.getVehicles().add( new Car() ); @@ -66,19 +81,38 @@ void existingMappingsAreUsedWhenFound() { } @ProcessorTest - void subclassMappingInheritsMapping() { - VehicleCollection vehicles = new VehicleCollection(); - Car car = new Car(); - car.setVehicleManufacturingCompany( "BenZ" ); - vehicles.getVehicles().add( car ); + @WithClasses( SimpleSubclassMapper.class ) + void subclassMappingInheritsInverseMapping() { + VehicleCollectionDto vehiclesDto = new VehicleCollectionDto(); + CarDto carDto = new CarDto(); + carDto.setMaker( "BenZ" ); + vehiclesDto.getVehicles().add( carDto ); - VehicleCollectionDto result = SimpleSubclassMapper.INSTANCE.map( vehicles ); + VehicleCollection result = SimpleSubclassMapper.INSTANCE.mapInverse( vehiclesDto ); assertThat( result.getVehicles() ) - .extracting( VehicleDto::getMaker ) + .extracting( Vehicle::getVehicleManufacturingCompany ) .containsExactly( "BenZ" ); } + @ProcessorTest + @WithClasses( { + HatchBack.class, + InverseOrderSubclassMapper.class + } ) + void subclassMappingOverridesInverseInheritsMapping() { + VehicleCollectionDto vehicleDtos = new VehicleCollectionDto(); + CarDto carDto = new CarDto(); + carDto.setMaker( "BenZ" ); + vehicleDtos.getVehicles().add( carDto ); + + VehicleCollection result = InverseOrderSubclassMapper.INSTANCE.mapInverse( vehicleDtos ); + + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( Car.class ); + } + @ProcessorTest @WithClasses({ HatchBack.class, @@ -91,11 +125,11 @@ void subclassMappingInheritsMapping() { line = 28, alternativeLine = 30, message = "SubclassMapping annotation for " - + "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBackDto' found after " - + "'org.mapstruct.ap.test.subclassmapping.mappables.CarDto', but all " - + "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBackDto' " + + "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBack' found after " + + "'org.mapstruct.ap.test.subclassmapping.mappables.Car', but all " + + "'org.mapstruct.ap.test.subclassmapping.mappables.HatchBack' " + "objects are also instances of " - + "'org.mapstruct.ap.test.subclassmapping.mappables.CarDto'.") + + "'org.mapstruct.ap.test.subclassmapping.mappables.Car'.") }) void subclassOrderWarning() { } @@ -136,4 +170,18 @@ void unsupportedUpdateMethod() { }) void erroneousMethodWithSourceTargetType() { } + + @ProcessorTest + @WithClasses({ ErroneousInverseSubclassMapper.class }) + @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { + @Diagnostic(type = ErroneousInverseSubclassMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 28, + message = "Subclass " + + "'org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto'" + + " is already defined as a source." + ) + }) + void inverseSubclassMappingNotPossible() { + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceOverride.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceOverride.java new file mode 100644 index 0000000000..97b9079e87 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceOverride.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.fixture; + +public class SubSourceOverride extends ImplementedParentSource implements InterfaceParentSource { + private final String finalValue; + + public SubSourceOverride(String finalValue) { + this.finalValue = finalValue; + } + + public String getFinalValue() { + return finalValue; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceSeparate.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceSeparate.java new file mode 100644 index 0000000000..ccae04efff --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubSourceSeparate.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.fixture; + +public class SubSourceSeparate extends ImplementedParentSource implements InterfaceParentSource { + private final String separateValue; + + public SubSourceSeparate(String separateValue) { + this.separateValue = separateValue; + } + + public String getSeparateValue() { + return separateValue; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTargetSeparate.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTargetSeparate.java new file mode 100644 index 0000000000..6b98c24106 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubTargetSeparate.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.fixture; + +public class SubTargetSeparate extends ImplementedParentTarget implements InterfaceParentTarget { + private final String separateValue; + + public SubTargetSeparate(String separateValue) { + this.separateValue = separateValue; + } + + public String getSeparateValue() { + return separateValue; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapper.java index 8ef7908c30..c50afb9e57 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapper.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.subclassmapping.fixture; import org.mapstruct.BeanMapping; +import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; import org.mapstruct.SubclassMapping; @@ -18,4 +19,9 @@ public interface SubclassAbstractMapper { @SubclassMapping( source = SubSource.class, target = SubTarget.class ) @SubclassMapping( source = SubSourceOther.class, target = SubTargetOther.class ) AbstractParentTarget map(AbstractParentSource item); + + @SubclassMapping( source = SubTargetSeparate.class, target = SubSourceSeparate.class ) + @InheritInverseConfiguration + @SubclassMapping( source = SubTargetOther.class, target = SubSourceOverride.class ) + AbstractParentSource map(AbstractParentTarget item); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassFixtureTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassFixtureTest.java index 14dc16d4ab..9bc72942e6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassFixtureTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/SubclassFixtureTest.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.subclassmapping.fixture; import org.junit.jupiter.api.extension.RegisterExtension; + import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.runner.GeneratedSource; @@ -37,6 +38,9 @@ void subclassInterfaceParentFixture() { @ProcessorTest @WithClasses( { + SubSourceOverride.class, + SubSourceSeparate.class, + SubTargetSeparate.class, SubclassAbstractMapper.class, } ) void subclassAbstractParentFixture() { diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java index 6deed338c1..a66b646f1e 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java @@ -9,7 +9,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2021-09-12T14:37:10+0200", + date = "2022-01-31T19:35:15+0100", comments = "version: , compiler: Eclipse JDT (Batch) 3.20.0.v20191203-2131, environment: Java 11.0.12 (Azul Systems, Inc.)" ) public class SubclassAbstractMapperImpl implements SubclassAbstractMapper { @@ -31,6 +31,26 @@ else if (item instanceof SubSourceOther) { } } + @Override + public AbstractParentSource map(AbstractParentTarget item) { + if ( item == null ) { + return null; + } + + if (item instanceof SubTargetSeparate) { + return subTargetSeparateToSubSourceSeparate( (SubTargetSeparate) item ); + } + else if (item instanceof SubTargetOther) { + return subTargetOtherToSubSourceOverride( (SubTargetOther) item ); + } + else if (item instanceof SubTarget) { + return subTargetToSubSource( (SubTarget) item ); + } + else { + throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for " + item.getClass()); + } + } + protected SubTarget subSourceToSubTarget(SubSource subSource) { if ( subSource == null ) { return null; @@ -56,4 +76,44 @@ protected SubTargetOther subSourceOtherToSubTargetOther(SubSourceOther subSource return subTargetOther; } + + protected SubSourceSeparate subTargetSeparateToSubSourceSeparate(SubTargetSeparate subTargetSeparate) { + if ( subTargetSeparate == null ) { + return null; + } + + String separateValue = null; + + separateValue = subTargetSeparate.getSeparateValue(); + + SubSourceSeparate subSourceSeparate = new SubSourceSeparate( separateValue ); + + return subSourceSeparate; + } + + protected SubSourceOverride subTargetOtherToSubSourceOverride(SubTargetOther subTargetOther) { + if ( subTargetOther == null ) { + return null; + } + + String finalValue = null; + + finalValue = subTargetOther.getFinalValue(); + + SubSourceOverride subSourceOverride = new SubSourceOverride( finalValue ); + + return subSourceOverride; + } + + protected SubSource subTargetToSubSource(SubTarget subTarget) { + if ( subTarget == null ) { + return null; + } + + SubSource subSource = new SubSource(); + + subSource.setValue( subTarget.getValue() ); + + return subSource; + } } From 9b434f80f8572e3f2cf03ce2a5bc932ff3f2eeae Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sun, 6 Feb 2022 20:05:42 +0100 Subject: [PATCH 241/597] #2715: Updated documentation to reflect impact of conditions on update mappers. (#2740) * #2715: added an example with an update mapper for Conditional behavior. --- ...apter-10-advanced-mapping-options.asciidoc | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 0bbec1fe57..1cc19fcc3e 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -354,6 +354,56 @@ public class CarMapperImpl implements CarMapper { ---- ==== +When using this in combination with an update mapping method it will replace the `null-check` there, for example: + +.Update mapper using custom condition check method +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface CarMapper { + + CarDto carToCarDto(Car car, @MappingTarget CarDto carDto); + + @Condition + default boolean isNotEmpty(String value) { + return value != null && !value.isEmpty(); + } +} +---- +==== + +The generated update mapper will look like: + +.Custom condition check in generated implementation +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class CarMapperImpl implements CarMapper { + + @Override + public CarDto carToCarDto(Car car, CarDto carDto) { + if ( car == null ) { + return carDto; + } + + if ( isNotEmpty( car.getOwner() ) ) { + carDto.setOwner( car.getOwner() ); + } else { + carDto.setOwner( null ); + } + + // Mapping of other properties + + return carDto; + } +} +---- +==== + [IMPORTANT] ==== If there is a custom `@Condition` method applicable for the property it will have a precedence over a presence check method in the bean itself. From 0b2c7e58b264adb5674de51634509d70a4578da2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 23 Feb 2022 15:55:20 +0100 Subject: [PATCH 242/597] Add Christian Kosmowski to the copyright.txt --- copyright.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/copyright.txt b/copyright.txt index 01b6526c70..8008a66bb9 100644 --- a/copyright.txt +++ b/copyright.txt @@ -8,6 +8,7 @@ Andrei Arlou - https://github.com/Captain1653 Andres Jose Sebastian Rincon Gonzalez - https://github.com/stianrincon Arne Seime - https://github.com/seime Christian Bandowski - https://github.com/chris922 +Christian Kosmowski - https://github.com/ckosmowski Christian Schuster - https://github.com/chschu Christophe Labouisse - https://github.com/ggtools Ciaran Liedeman - https://github.com/cliedeman From b6a3aa151228c17183979da0fd1573bebd1e9ed3 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sat, 12 Mar 2022 18:01:15 +0100 Subject: [PATCH 243/597] #2758: fallback to param.variableName if ext.targetBeanName is not present in MethodReference handling. (#2759) --- .../ap/internal/model/MethodReference.ftl | 2 +- .../basic/ConditionalMappingTest.java | 17 ++++++++++- ...MethodWithMappingTargetInUpdateMapper.java | 29 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithMappingTargetInUpdateMapper.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl index 271ba58f9b..d8236140f5 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl @@ -41,7 +41,7 @@ <#-- a class is passed on for casting, see @TargetType --> <@includeModel object=inferTypeWhenEnum( ext.targetType ) raw=true/>.class<#t> <#elseif param.mappingTarget> - ${ext.targetBeanName}<#if ext.targetReadAccessorName??>.${ext.targetReadAccessorName}<#t> + <#if ext.targetBeanName??>${ext.targetBeanName}<#else>${param.variableName}<#if ext.targetReadAccessorName??>.${ext.targetReadAccessorName}<#t> <#elseif param.mappingContext> ${param.variableName}<#t> <#elseif param.sourceRHS??> diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java index 0fc791fb13..62fd79ae84 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java @@ -7,8 +7,8 @@ import java.util.Arrays; import java.util.Collections; - import org.junit.jupiter.api.extension.RegisterExtension; + import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -223,4 +223,19 @@ public void optionalLikeConditional() { } + @ProcessorTest + @WithClasses( { + ConditionalMethodWithMappingTargetInUpdateMapper.class + } ) + @IssueKey( "2758" ) + public void conditionalMethodWithMappingTarget() { + ConditionalMethodWithMappingTargetInUpdateMapper mapper = + ConditionalMethodWithMappingTargetInUpdateMapper.INSTANCE; + + BasicEmployee targetEmployee = new BasicEmployee(); + targetEmployee.setName( "CurrentName" ); + mapper.map( new BasicEmployeeDto( "ReplacementName" ), targetEmployee ); + + assertThat( targetEmployee.getName() ).isEqualTo( "CurrentName" ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithMappingTargetInUpdateMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithMappingTargetInUpdateMapper.java new file mode 100644 index 0000000000..12f4a00212 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithMappingTargetInUpdateMapper.java @@ -0,0 +1,29 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@Mapper( nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE ) +public interface ConditionalMethodWithMappingTargetInUpdateMapper { + + ConditionalMethodWithMappingTargetInUpdateMapper INSTANCE = + Mappers.getMapper( ConditionalMethodWithMappingTargetInUpdateMapper.class ); + + void map(BasicEmployeeDto employee, @MappingTarget BasicEmployee targetEmployee); + + @Condition + default boolean isNotBlankAndNotPresent(String value, @MappingTarget BasicEmployee targetEmployee) { + return value != null && !value.trim().isEmpty() && targetEmployee.getName() == null; + } +} From 0a69492983a34a5a41dd6968fa0c2f18f97484e8 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sat, 12 Mar 2022 18:02:01 +0100 Subject: [PATCH 244/597] #2755: use raw Type when calling a static method. (#2762) --- .../ap/internal/model/MethodReference.ftl | 2 +- .../selection/generics/GenericMapperBase.java | 48 +++++++++++++++++++ .../generics/GenericMapperBaseTest.java | 25 ++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericMapperBase.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericMapperBaseTest.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl index d8236140f5..6ccdf26871 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl @@ -15,7 +15,7 @@ <#if static><@includeModel object=providingParameter.type/><#else>${providingParameter.name}.<@methodCall/> <#-- method is referenced java8 static method in the mapper to implement (interface) --> <#elseif static> - <@includeModel object=definingType/>.<@methodCall/> + <@includeModel object=definingType raw=true/>.<@methodCall/> <#elseif constructor> new <@includeModel object=definingType/><#if (parameterBindings?size > 0)>( <@arguments/> )<#else>() <#elseif methodChaining> diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericMapperBase.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericMapperBase.java new file mode 100644 index 0000000000..025acba4ae --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericMapperBase.java @@ -0,0 +1,48 @@ +/* + * 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.selection.generics; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +public interface GenericMapperBase { + + T toDto(Map prop); + + @Named( "StringArrayToString" ) + static String stringArrayToString(Property property) { + return "converted"; + } +} + +class Property { + +} + +class ProjectDto { + private String productionSite; + + public void setProductionSite(String productionSite) { + this.productionSite = productionSite; + } + + public String getProductionSite() { + return productionSite; + } +} + +@Mapper +interface ProjectMapper extends GenericMapperBase { + ProjectMapper INSTANCE = Mappers.getMapper( ProjectMapper.class ); + + @Mapping( target = "productionSite", source = "productionSite", qualifiedByName = "StringArrayToString" ) + @Override + ProjectDto toDto(Map projectProp); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericMapperBaseTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericMapperBaseTest.java new file mode 100644 index 0000000000..1fe4930e2e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/generics/GenericMapperBaseTest.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.ap.test.selection.generics; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Ben Zegveld + */ +@WithClasses( { + GenericMapperBase.class +} ) +@IssueKey( "2755" ) +class GenericMapperBaseTest { + + @ProcessorTest + void generatesCompilableCode() { + } + +} From ad00adfa8659d22fca4fd725434e6d5971418692 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 12 Mar 2022 23:57:17 +0100 Subject: [PATCH 245/597] #2538 Allow using 2 step mappings with only one of the 2 methods being qualified --- .../source/selector/SelectionCriteria.java | 10 ++++- .../creation/MappingResolverImpl.java | 41 ++++++++++++++++-- .../mapstruct/ap/test/bugs/_2538/Group.java | 22 ++++++++++ .../ap/test/bugs/_2538/GroupDto.java | 22 ++++++++++ .../ap/test/bugs/_2538/Issue2538Test.java | 39 +++++++++++++++++ .../ap/test/bugs/_2538/TeamMapper.java | 42 +++++++++++++++++++ .../ap/test/bugs/_2538/TeamRole.java | 22 ++++++++++ .../ap/test/bugs/_2538/TeamRoleDto.java | 22 ++++++++++ 8 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/Group.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/GroupDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/Issue2538Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamRole.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamRoleDto.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java index b70e94b6d1..da8bae365a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.model.source.selector; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.lang.model.type.TypeMirror; @@ -26,6 +27,7 @@ public class SelectionCriteria { private final String targetPropertyName; private final TypeMirror qualifyingResultType; private final SourceRHS sourceRHS; + private boolean ignoreQualifiers = false; private Type type; private final boolean allowDirect; private final boolean allowConversion; @@ -87,12 +89,16 @@ public boolean isPresenceCheckRequired() { return type == Type.PRESENCE_CHECK; } + public void setIgnoreQualifiers(boolean ignoreQualifiers) { + this.ignoreQualifiers = ignoreQualifiers; + } + public List getQualifiers() { - return qualifiers; + return ignoreQualifiers ? Collections.emptyList() : qualifiers; } public List getQualifiedByNames() { - return qualifiedByNames; + return ignoreQualifiers ? Collections.emptyList() : qualifiedByNames; } public String getTargetPropertyName() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index b971341fc9..2e2a760bf4 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -712,6 +712,11 @@ public int hashCode() { } } + private enum BestMatchType { + IGNORE_QUALIFIERS_BEFORE_Y_CANDIDATES, + IGNORE_QUALIFIERS_AFTER_Y_CANDIDATES, + } + /** * Suppose mapping required from A to C and: *
      @@ -743,6 +748,17 @@ static Assignment getBestMatch(ResolvingAttempt att, Type sourceType, Type targe if ( mmAttempt.hasResult ) { return mmAttempt.result; } + if ( att.hasQualfiers() ) { + mmAttempt = mmAttempt.getBestMatchIgnoringQualifiersBeforeY( sourceType, targetType ); + if ( mmAttempt.hasResult ) { + return mmAttempt.result; + } + + mmAttempt = mmAttempt.getBestMatchIgnoringQualifiersAfterY( sourceType, targetType ); + if ( mmAttempt.hasResult ) { + return mmAttempt.result; + } + } MethodMethod mbAttempt = new MethodMethod<>( att, att.methods, att.builtIns, att::toMethodRef, att::toBuildInRef ) .getBestMatch( sourceType, targetType ); @@ -772,6 +788,18 @@ static Assignment getBestMatch(ResolvingAttempt att, Type sourceType, Type targe } private MethodMethod getBestMatch(Type sourceType, Type targetType) { + return getBestMatch( sourceType, targetType, null ); + } + + private MethodMethod getBestMatchIgnoringQualifiersBeforeY(Type sourceType, Type targetType) { + return getBestMatch( sourceType, targetType, BestMatchType.IGNORE_QUALIFIERS_BEFORE_Y_CANDIDATES ); + } + + private MethodMethod getBestMatchIgnoringQualifiersAfterY(Type sourceType, Type targetType) { + return getBestMatch( sourceType, targetType, BestMatchType.IGNORE_QUALIFIERS_AFTER_Y_CANDIDATES ); + } + + private MethodMethod getBestMatch(Type sourceType, Type targetType, BestMatchType matchType) { Set yCandidates = new HashSet<>(); Map, List>> xCandidates = new LinkedHashMap<>(); @@ -784,6 +812,9 @@ private MethodMethod getBestMatch(Type sourceType, Type targetType) { // sourceMethod or builtIn that fits the signature B to C. Only then there is a match. If we have a match // a nested method call can be called. so C = methodY( methodX (A) ) attempt.selectionCriteria.setPreferUpdateMapping( false ); + attempt.selectionCriteria.setIgnoreQualifiers( + matchType == BestMatchType.IGNORE_QUALIFIERS_BEFORE_Y_CANDIDATES ); + for ( T2 yCandidate : yMethods ) { Type ySourceType = yCandidate.getMappingSourceType(); ySourceType = ySourceType.resolveParameterToType( targetType, yCandidate.getResultType() ).getMatch(); @@ -796,13 +827,16 @@ private MethodMethod getBestMatch(Type sourceType, Type targetType) { } List> xMatches = attempt.getBestMatch( xMethods, sourceType, ySourceType ); if ( !xMatches.isEmpty() ) { - xMatches.stream().forEach( x -> xCandidates.put( x, new ArrayList<>() ) ); - final Type typeInTheMiddle = ySourceType; - xMatches.stream().forEach( x -> typesInTheMiddle.put( x, typeInTheMiddle ) ); + for ( SelectedMethod x : xMatches ) { + xCandidates.put( x, new ArrayList<>() ); + typesInTheMiddle.put( x, ySourceType ); + } yCandidates.add( yCandidate ); } } attempt.selectionCriteria.setPreferUpdateMapping( true ); + attempt.selectionCriteria.setIgnoreQualifiers( + matchType == BestMatchType.IGNORE_QUALIFIERS_AFTER_Y_CANDIDATES ); // collect all results List yCandidatesList = new ArrayList<>( yCandidates ); @@ -816,6 +850,7 @@ private MethodMethod getBestMatch(Type sourceType, Type targetType) { } } + attempt.selectionCriteria.setIgnoreQualifiers( false ); // no results left if ( xCandidates.isEmpty() ) { return this; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/Group.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/Group.java new file mode 100644 index 0000000000..4451431fee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/Group.java @@ -0,0 +1,22 @@ +/* + * 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._2538; + +/** + * @author Filip Hrisafov + */ +public class Group { + + private final String id; + + public Group(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/GroupDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/GroupDto.java new file mode 100644 index 0000000000..0d94c90815 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/GroupDto.java @@ -0,0 +1,22 @@ +/* + * 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._2538; + +/** + * @author Filip Hrisafov + */ +public class GroupDto { + + private final String id; + + public GroupDto(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/Issue2538Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/Issue2538Test.java new file mode 100644 index 0000000000..cf99e67f92 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/Issue2538Test.java @@ -0,0 +1,39 @@ +/* + * 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._2538; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Group.class, + GroupDto.class, + TeamMapper.class, + TeamRole.class, + TeamRoleDto.class, +}) +class Issue2538Test { + + @ProcessorTest + void shouldCorrectlyUseQualifiedMethodIn2StepMapping() { + TeamRole role = TeamMapper.INSTANCE.mapUsingFirstLookup( new TeamRoleDto( "test" ) ); + + assertThat( role ).isNotNull(); + assertThat( role.getGroup() ).isNotNull(); + assertThat( role.getGroup().getId() ).isEqualTo( "lookup-test" ); + + role = TeamMapper.INSTANCE.mapUsingSecondLookup( new TeamRoleDto( "test" ) ); + + assertThat( role ).isNotNull(); + assertThat( role.getGroup() ).isNotNull(); + assertThat( role.getGroup().getId() ).isEqualTo( "second-test" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamMapper.java new file mode 100644 index 0000000000..5615e7bc43 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamMapper.java @@ -0,0 +1,42 @@ +/* + * 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._2538; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface TeamMapper { + + TeamMapper INSTANCE = Mappers.getMapper( TeamMapper.class ); + + // This method is testing methodX(methodY(...)) where methodY is qualified + @Mapping(target = "group", source = "groupId", qualifiedByName = "firstLookup") + TeamRole mapUsingFirstLookup(TeamRoleDto in); + + // This method is testing methodX(methodY(...)) where methodX is qualified + @Mapping(target = "group", source = "groupId", qualifiedByName = "secondLookup") + TeamRole mapUsingSecondLookup(TeamRoleDto in); + + Group map(GroupDto in); + + @Named("firstLookup") + default GroupDto lookupGroup(String groupId) { + return groupId != null ? new GroupDto( "lookup-" + groupId ) : null; + } + + default GroupDto normalLookup(String groupId) { + return groupId != null ? new GroupDto( groupId ) : null; + } + + @Named("secondLookup") + default Group mapSecondLookup(GroupDto in) { + return in != null ? new Group( "second-" + in.getId() ) : null; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamRole.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamRole.java new file mode 100644 index 0000000000..f73700fa4d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamRole.java @@ -0,0 +1,22 @@ +/* + * 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._2538; + +/** + * @author Filip Hrisafov + */ +public class TeamRole { + + private final Group group; + + public TeamRole(Group group) { + this.group = group; + } + + public Group getGroup() { + return group; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamRoleDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamRoleDto.java new file mode 100644 index 0000000000..fac5a1a9ee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2538/TeamRoleDto.java @@ -0,0 +1,22 @@ +/* + * 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._2538; + +/** + * @author Filip Hrisafov + */ +public class TeamRoleDto { + + private final String groupId; + + public TeamRoleDto(String groupId) { + this.groupId = groupId; + } + + public String getGroupId() { + return groupId; + } +} From ab528678318cb0983f50767b8170ab4697ba4e75 Mon Sep 17 00:00:00 2001 From: Chris DeLashmutt Date: Sat, 19 Mar 2022 06:51:19 -0400 Subject: [PATCH 246/597] #2748 Support mapping map keys with invalid chars for methods --- .../mapstruct/ap/internal/util/Strings.java | 14 +++++- .../ap/internal/util/StringsTest.java | 3 ++ .../ap/test/bugs/_2748/Issue2748Mapper.java | 48 +++++++++++++++++++ .../ap/test/bugs/_2748/Issue2748Test.java | 36 ++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2748/Issue2748Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2748/Issue2748Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java index 42727ed816..88e0bdf6f6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Strings.java @@ -183,7 +183,19 @@ public static String sanitizeIdentifierName(String identifier) { if ( firstAlphabeticIndex < identifier.length()) { // If it is not consisted of only underscores - return identifier.substring( firstAlphabeticIndex ).replace( "[]", "Array" ); + String firstAlphaString = identifier.substring( firstAlphabeticIndex ).replace( "[]", "Array" ); + + StringBuilder sb = new StringBuilder( firstAlphaString.length() ); + for ( int i = 0; i < firstAlphaString.length(); i++ ) { + int codePoint = firstAlphaString.codePointAt( i ); + if ( Character.isJavaIdentifierPart( codePoint ) || codePoint == '.') { + sb.appendCodePoint( codePoint ); + } + else { + sb.append( '_' ); + } + } + return sb.toString(); } return identifier.replace( "[]", "Array" ); diff --git a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java index 687b63f793..524fd193aa 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/util/StringsTest.java @@ -72,6 +72,7 @@ public void testGetSaveVariableNameWithArrayExistingVariables() { assertThat( Strings.getSafeVariableName( "__Test" ) ).isEqualTo( "test" ); assertThat( Strings.getSafeVariableName( "_0Test" ) ).isEqualTo( "test" ); assertThat( Strings.getSafeVariableName( "_0123Test" ) ).isEqualTo( "test" ); + assertThat( Strings.getSafeVariableName( "bad/test" ) ).isEqualTo( "bad_test" ); } @Test @@ -94,6 +95,7 @@ public void testGetSaveVariableNameWithCollection() { assertThat( Strings.getSafeVariableName( "__0Test", Arrays.asList( "test" ) ) ).isEqualTo( "test1" ); assertThat( Strings.getSafeVariableName( "___0", new ArrayList<>() ) ).isEqualTo( "___0" ); assertThat( Strings.getSafeVariableName( "__0123456789Test", Arrays.asList( "test" ) ) ).isEqualTo( "test1" ); + assertThat( Strings.getSafeVariableName( "bad/test", Arrays.asList( "bad_test" ) ) ).isEqualTo( "bad_test1" ); } @Test @@ -110,6 +112,7 @@ public void testSanitizeIdentifierName() { assertThat( Strings.sanitizeIdentifierName( "_0int[]" ) ).isEqualTo( "intArray" ); assertThat( Strings.sanitizeIdentifierName( "__0int[]" ) ).isEqualTo( "intArray" ); assertThat( Strings.sanitizeIdentifierName( "___0" ) ).isEqualTo( "___0" ); + assertThat( Strings.sanitizeIdentifierName( "bad/test" ) ).isEqualTo( "bad_test" ); } @Test diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2748/Issue2748Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2748/Issue2748Mapper.java new file mode 100644 index 0000000000..0062667df4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2748/Issue2748Mapper.java @@ -0,0 +1,48 @@ +/* + * 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._2748; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2748Mapper { + + Issue2748Mapper INSTANCE = Mappers.getMapper( Issue2748Mapper.class ); + + @Mapping(target = "specificValue", source = "annotations.specific/value") + Target map(Source source); + + class Target { + private final String specificValue; + + public Target(String specificValue) { + this.specificValue = specificValue; + } + + public String getSpecificValue() { + return specificValue; + } + } + + class Source { + private final Map annotations; + + public Source(Map annotations) { + this.annotations = annotations; + } + + public Map getAnnotations() { + return annotations; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2748/Issue2748Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2748/Issue2748Test.java new file mode 100644 index 0000000000..4bda2cd43d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2748/Issue2748Test.java @@ -0,0 +1,36 @@ +/* + * 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._2748; + +import java.util.Collections; +import java.util.Map; + +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 Filip Hrisafov + */ +@IssueKey("2748") +@WithClasses( { + Issue2748Mapper.class +} ) +class Issue2748Test { + + @ProcessorTest + void shouldMapNonJavaIdentifier() { + Map annotations = Collections.singletonMap( "specific/value", "value" ); + Issue2748Mapper.Source source = new Issue2748Mapper.Source( annotations ); + + Issue2748Mapper.Target target = Issue2748Mapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getSpecificValue() ).isEqualTo( "value" ); + } +} From 190b486b7919a64e5fe4f80e6dc551554b0d6455 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 19 Mar 2022 12:09:30 +0100 Subject: [PATCH 247/597] Add users that have contributed post 1.5.0.Beta2 to copyright.txt --- copyright.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copyright.txt b/copyright.txt index 8008a66bb9..1b23a0e84a 100644 --- a/copyright.txt +++ b/copyright.txt @@ -8,6 +8,7 @@ Andrei Arlou - https://github.com/Captain1653 Andres Jose Sebastian Rincon Gonzalez - https://github.com/stianrincon Arne Seime - https://github.com/seime Christian Bandowski - https://github.com/chris922 +Chris DeLashmutt - https://github.com/cdelashmutt-pivotal Christian Kosmowski - https://github.com/ckosmowski Christian Schuster - https://github.com/chschu Christophe Labouisse - https://github.com/ggtools @@ -35,6 +36,7 @@ João Paulo Bassinello - https://github.com/jpbassinello Jonathan Kraska - https://github.com/jakraska Joshua Spoerri - https://github.com/spoerri Jude Niroshan - https://github.com/JudeNiroshan +Justyna Kubica-Ledzion - https://github.com/JKLedzion Kemal Özcan - https://github.com/yekeoe Kevin Grüneberg - https://github.com/kevcodez Lukas Lazar - https://github.com/LukeLaz @@ -63,4 +65,5 @@ Tobias Meggendorfer - https://github.com/incaseoftrouble Tillmann Gaida - https://github.com/Tillerino Timo Eckhardt - https://github.com/timoe Tomek Gubala - https://github.com/vgtworld +Valentin Kulesh - https://github.com/unshare Vincent Alexander Beelte - https://github.com/grandmasterpixel From 7e00af6ff451b15d125242af91df2864a0a58aea Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 21 Mar 2022 08:14:08 +0100 Subject: [PATCH 248/597] [maven-release-plugin] prepare release 1.5.0.RC1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index aaae7b5404..6c34822f96 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.RC1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 293043e6f9..2a412327da 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.RC1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 10c95cf3b5..4d16fc611f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.RC1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 337ad35e51..4769c58d25 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.RC1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 8850da641c..a46ea17a53 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.RC1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 4b48c19496..a1ec01d3a6 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.RC1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 4ca3d9b2c0..958d4bcafb 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.RC1 pom MapStruct Parent @@ -69,7 +69,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.0.RC1 diff --git a/pom.xml b/pom.xml index 28c35aea74..b89fd7d12e 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.RC1 parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.0.RC1 diff --git a/processor/pom.xml b/processor/pom.xml index ec3f20a241..369e8066d2 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.RC1 ../parent/pom.xml From 08a0313840307e96d17246509cbea99c4b048dec Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 21 Mar 2022 08:14:09 +0100 Subject: [PATCH 249/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 6c34822f96..aaae7b5404 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.RC1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 2a412327da..293043e6f9 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.RC1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 4d16fc611f..10c95cf3b5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.RC1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 4769c58d25..337ad35e51 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.RC1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index a46ea17a53..8850da641c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.RC1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index a1ec01d3a6..4b48c19496 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.RC1 + 1.5.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 958d4bcafb..4ca3d9b2c0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.0.RC1 + 1.5.0-SNAPSHOT pom MapStruct Parent @@ -69,7 +69,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.0.RC1 + HEAD diff --git a/pom.xml b/pom.xml index b89fd7d12e..28c35aea74 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.0.RC1 + 1.5.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.0.RC1 + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 369e8066d2..ec3f20a241 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.RC1 + 1.5.0-SNAPSHOT ../parent/pom.xml From 07eeea6bc9563e6964e9e573795d1ccb5ad34c87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 18:41:52 +0000 Subject: [PATCH 250/597] Bump spring-beans from 5.3.15 to 5.3.18 in /parent Bumps [spring-beans](https://github.com/spring-projects/spring-framework) from 5.3.15 to 5.3.18. - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v5.3.15...v5.3.18) --- updated-dependencies: - dependency-name: org.springframework:spring-beans dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 4ca3d9b2c0..1a9fddff92 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -25,7 +25,7 @@ 3.0.0-M3 3.0.0-M5 3.1.0 - 5.3.15 + 5.3.18 1.6.0 8.36.1 5.8.0-M1 From 2473c3eaaa812490b23dc32287b90bd36d539ecf Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sat, 2 Apr 2022 11:59:25 +0200 Subject: [PATCH 251/597] #2797: Add the nested type import types in the NestedPropertyMappingMethod * #2797: Reproduction scenario * Add the nested type import types in the NestedPropertyMappingMethod Co-authored-by: Ben Zegveld Co-authored-by: Filip Hrisafov --- .../model/NestedPropertyMappingMethod.java | 2 +- .../ap/test/bugs/_2797/ExampleDto.java | 31 +++++++++++++ .../ap/test/bugs/_2797/ExampleMapper.java | 23 ++++++++++ .../ap/test/bugs/_2797/Issue2797Test.java | 24 ++++++++++ .../ap/test/bugs/_2797/model/BasePerson.java | 44 +++++++++++++++++++ .../ap/test/bugs/_2797/model/Example.java | 16 +++++++ 6 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/ExampleDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/ExampleMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/Issue2797Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/model/BasePerson.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/model/Example.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java index 96004cb474..da73e1783f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java @@ -103,7 +103,7 @@ public List getPropertyEntries() { public Set getImportTypes() { Set types = super.getImportTypes(); for ( SafePropertyEntry propertyEntry : safePropertyEntries) { - types.add( propertyEntry.getType() ); + types.addAll( propertyEntry.getType().getImportTypes() ); if ( propertyEntry.getPresenceChecker() != null ) { types.addAll( propertyEntry.getPresenceChecker().getImportTypes() ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/ExampleDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/ExampleDto.java new file mode 100644 index 0000000000..eff9f26271 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/ExampleDto.java @@ -0,0 +1,31 @@ +/* + * 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._2797; + +/** + * @author Ben Zegveld + */ +public class ExampleDto { + + private String personFirstName; + private String personLastName; + + public String getPersonFirstName() { + return personFirstName; + } + + public String getPersonLastName() { + return personLastName; + } + + public void setPersonFirstName(String personFirstName) { + this.personFirstName = personFirstName; + } + + public void setPersonLastName(String personLastName) { + this.personLastName = personLastName; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/ExampleMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/ExampleMapper.java new file mode 100644 index 0000000000..eccac27256 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/ExampleMapper.java @@ -0,0 +1,23 @@ +/* + * 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._2797; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.bugs._2797.model.Example.Person; + +import static org.mapstruct.ReportingPolicy.ERROR; + +/** + * @author Ben Zegveld + */ +@Mapper(unmappedTargetPolicy = ERROR) +public interface ExampleMapper { + + @Mapping(target = "personFirstName", source = "names.first") + @Mapping(target = "personLastName", source = "names.last") + ExampleDto map(Person person); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/Issue2797Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/Issue2797Test.java new file mode 100644 index 0000000000..7f3dd5f3de --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/Issue2797Test.java @@ -0,0 +1,24 @@ +/* + * 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._2797; + +import org.mapstruct.ap.test.bugs._2797.model.BasePerson; +import org.mapstruct.ap.test.bugs._2797.model.Example; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Ben Zegveld + */ +@IssueKey( "2797" ) +@WithClasses( { ExampleDto.class, ExampleMapper.class, Example.class, BasePerson.class } ) +public class Issue2797Test { + + @ProcessorTest + void shouldCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/model/BasePerson.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/model/BasePerson.java new file mode 100644 index 0000000000..6b54e6ee31 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/model/BasePerson.java @@ -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._2797.model; + +/** + * @author Ben Zegveld + */ +public class BasePerson { + + private Names names; + + public Names getNames() { + return names; + } + + public void setNames(Names names) { + this.names = names; + } + + public static class Names { + + private String first; + private String last; + + public String getFirst() { + return first; + } + + public String getLast() { + return last; + } + + public void setFirst(String first) { + this.first = first; + } + + public void setLast(String last) { + this.last = last; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/model/Example.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/model/Example.java new file mode 100644 index 0000000000..4dfa40f695 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2797/model/Example.java @@ -0,0 +1,16 @@ +/* + * 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._2797.model; + +/** + * @author Ben Zegveld + */ +public class Example { + + public static class Person extends BasePerson { + + } +} From 03d44b5a872f796b79336429d8cde2ee7918910a Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sat, 2 Apr 2022 18:55:06 +0200 Subject: [PATCH 252/597] #2795: use 'includeModel' for the 'sourcePresenceCheckerReference' in the 'UpdateWrapper'. (#2796) * #2795: use 'includeModel' for the 'sourcePresenceCheckerReference' in the 'UpdateWrapper'. * Simplify the tests Co-authored-by: Ben Zegveld Co-authored-by: Filip Hrisafov --- .../model/assignment/UpdateWrapper.ftl | 2 +- .../ap/test/bugs/_2795/Issue2795Mapper.java | 30 +++++++++++++++++++ .../ap/test/bugs/_2795/Issue2795Test.java | 25 ++++++++++++++++ .../mapstruct/ap/test/bugs/_2795/Nested.java | 20 +++++++++++++ .../ap/test/bugs/_2795/NestedDto.java | 20 +++++++++++++ .../mapstruct/ap/test/bugs/_2795/Source.java | 22 ++++++++++++++ .../mapstruct/ap/test/bugs/_2795/Target.java | 20 +++++++++++++ 7 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Issue2795Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Issue2795Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Nested.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/NestedDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Target.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl index 58416fc63b..9cdd07c230 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl @@ -10,7 +10,7 @@ <@lib.handleExceptions> <#if includeSourceNullCheck> <@lib.sourceLocalVarAssignment/> - if ( <#if sourcePresenceCheckerReference?? >${sourcePresenceCheckerReference}<#else><#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference} != null ) { + if ( <#if sourcePresenceCheckerReference?? ><@includeModel object=sourcePresenceCheckerReference /><#else><#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference} != null ) { <@assignToExistingTarget/> <@lib.handleAssignment/>; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Issue2795Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Issue2795Mapper.java new file mode 100644 index 0000000000..41f29da920 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Issue2795Mapper.java @@ -0,0 +1,30 @@ +/* + * 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._2795; + +import java.util.Optional; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; + +@Mapper +public interface Issue2795Mapper { + + void update(Source update, @MappingTarget Target destination); + + void update(NestedDto update, @MappingTarget Nested destination); + + static T unwrap(Optional optional) { + return optional.orElse( null ); + } + + @Condition + static boolean isNotEmpty(Optional field) { + return field != null && field.isPresent(); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Issue2795Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Issue2795Test.java new file mode 100644 index 0000000000..4b7b599527 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Issue2795Test.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.ap.test.bugs._2795; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +@IssueKey("2795") +@WithClasses({ + Issue2795Mapper.class, + Nested.class, + NestedDto.class, + Target.class, + Source.class, +}) +public class Issue2795Test { + + @ProcessorTest + void shouldCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Nested.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Nested.java new file mode 100644 index 0000000000..5214c7d076 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Nested.java @@ -0,0 +1,20 @@ +/* + * 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._2795; + +public class Nested { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/NestedDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/NestedDto.java new file mode 100644 index 0000000000..1b27453435 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/NestedDto.java @@ -0,0 +1,20 @@ +/* + * 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._2795; + +public class NestedDto { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Source.java new file mode 100644 index 0000000000..927e2b09f5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Source.java @@ -0,0 +1,22 @@ +/* + * 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._2795; + +import java.util.Optional; + +public class Source { + + private Optional nested = Optional.empty(); + + public Optional getNested() { + return nested; + } + + public void setNested(Optional nested) { + this.nested = nested; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Target.java new file mode 100644 index 0000000000..ed69a6d48f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2795/Target.java @@ -0,0 +1,20 @@ +/* + * 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._2795; + +public class Target { + + private Nested nested; + + public Nested getNested() { + return nested; + } + + public void setNested(Nested nested) { + this.nested = nested; + } + +} From 66046177300c9c89bbec6e841a61208e6d08140a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 2 Apr 2022 14:51:26 +0200 Subject: [PATCH 253/597] #2794 Compile error when condition expression used with constant or expression --- .../ap/internal/model/source/MappingOptions.java | 6 ++++++ .../org/mapstruct/ap/internal/util/Message.java | 2 ++ .../expression/ConditionalExpressionTest.java | 13 +++++++++++++ .../ErroneousConditionExpressionMapper.java | 6 ++++++ 4 files changed, 27 insertions(+) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index cb496a3a31..e788b51f6e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -222,9 +222,15 @@ else if ( gem.constant().hasValue() && gem.defaultValue().hasValue() ) { else if ( gem.expression().hasValue() && gem.defaultExpression().hasValue() ) { message = Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } + else if ( gem.expression().hasValue() && gem.conditionExpression().hasValue() ) { + message = Message.PROPERTYMAPPING_EXPRESSION_AND_CONDITION_EXPRESSION_BOTH_DEFINED; + } else if ( gem.constant().hasValue() && gem.defaultExpression().hasValue() ) { message = Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } + else if ( gem.constant().hasValue() && gem.conditionExpression().hasValue() ) { + message = Message.PROPERTYMAPPING_CONSTANT_AND_CONDITION_EXPRESSION_BOTH_DEFINED; + } else if ( gem.defaultValue().hasValue() && gem.defaultExpression().hasValue() ) { message = Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 6d8fed1a95..4e1c0302fa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -59,7 +59,9 @@ public enum Message { PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_VALUE_BOTH_DEFINED( "Expression and default value are both defined in @Mapping, either define a defaultValue or an expression." ), PROPERTYMAPPING_CONSTANT_AND_DEFAULT_VALUE_BOTH_DEFINED( "Constant and default value are both defined in @Mapping, either define a defaultValue or a constant." ), PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_EXPRESSION_BOTH_DEFINED( "Expression and default expression are both defined in @Mapping, either define an expression or a default expression." ), + PROPERTYMAPPING_EXPRESSION_AND_CONDITION_EXPRESSION_BOTH_DEFINED( "Expression and condition expression are both defined in @Mapping, either define an expression or a condition expression." ), PROPERTYMAPPING_CONSTANT_AND_DEFAULT_EXPRESSION_BOTH_DEFINED( "Constant and default expression are both defined in @Mapping, either define a constant or a default expression." ), + PROPERTYMAPPING_CONSTANT_AND_CONDITION_EXPRESSION_BOTH_DEFINED( "Constant and condition expression are both defined in @Mapping, either define a constant or a condition expression." ), PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED( "Default value and default expression are both defined in @Mapping, either define a default value or a default expression." ), PROPERTYMAPPING_DEFAULT_VALUE_AND_NVPMS( "Default value and nullValuePropertyMappingStrategy are both defined in @Mapping, either define a defaultValue or an nullValuePropertyMappingStrategy." ), PROPERTYMAPPING_EXPRESSION_VALUE_AND_NVPMS( "Expression and nullValuePropertyMappingStrategy are both defined in @Mapping, either define an expression or an nullValuePropertyMappingStrategy." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java index 42219cfb6a..1ad36f7de1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ConditionalExpressionTest.java @@ -111,6 +111,7 @@ public void conditionalExpressionForSourceToTarget() { } + @IssueKey("2794") @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, @@ -119,6 +120,18 @@ public void conditionalExpressionForSourceToTarget() { kind = javax.tools.Diagnostic.Kind.ERROR, line = 19, message = "Value for condition expression must be given in the form \"java()\"." + ), + @Diagnostic(type = ErroneousConditionExpressionMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 22, + message = "Constant and condition expression are both defined in @Mapping," + + " either define a constant or a condition expression." + ), + @Diagnostic(type = ErroneousConditionExpressionMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 25, + message = "Expression and condition expression are both defined in @Mapping," + + " either define an expression or a condition expression." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ErroneousConditionExpressionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ErroneousConditionExpressionMapper.java index c74c552531..c8e5db243e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ErroneousConditionExpressionMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/expression/ErroneousConditionExpressionMapper.java @@ -18,4 +18,10 @@ public interface ErroneousConditionExpressionMapper { @Mapping(target = "name", conditionExpression = "!employee.getName().isEmpty()") BasicEmployee map(EmployeeDto employee); + + @Mapping(target = "name", conditionExpression = "java(true)", constant = "test") + BasicEmployee mapConstant(EmployeeDto employee); + + @Mapping(target = "name", conditionExpression = "java(true)", expression = "java(\"test\")") + BasicEmployee mapExpression(EmployeeDto employee); } From 437a70d6dfa238f3d0262d1bf90d88003152fbbf Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Fri, 8 Apr 2022 20:57:40 +0200 Subject: [PATCH 254/597] #2807: Include LifeCycleMethod importTypes in the list of importTypes. (#2808) Co-authored-by: Ben Zegveld --- .../ap/internal/model/MappingMethod.java | 21 +++++++++++---- .../ap/test/bugs/_2807/Issue2807Test.java | 27 +++++++++++++++++++ .../bugs/_2807/SpringLifeCycleMapper.java | 26 ++++++++++++++++++ .../ap/test/bugs/_2807/after/AfterMethod.java | 23 ++++++++++++++++ .../test/bugs/_2807/before/BeforeMethod.java | 21 +++++++++++++++ .../beforewithtarget/BeforeWithTarget.java | 23 ++++++++++++++++ 6 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/Issue2807Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/SpringLifeCycleMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/after/AfterMethod.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/before/BeforeMethod.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/beforewithtarget/BeforeWithTarget.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java index c7d329916f..5b9eda644b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java @@ -5,11 +5,9 @@ */ package org.mapstruct.ap.internal.model; -import static org.mapstruct.ap.internal.util.Strings.getSafeVariableName; -import static org.mapstruct.ap.internal.util.Strings.join; - import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -21,6 +19,9 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; +import static org.mapstruct.ap.internal.util.Strings.getSafeVariableName; +import static org.mapstruct.ap.internal.util.Strings.join; + /** * A method implemented or referenced by a {@link Mapper} class. * @@ -70,7 +71,7 @@ protected MappingMethod(Method method, List parameters, Collection parameters) { @@ -153,6 +154,16 @@ public Set getImportTypes() { types.addAll( type.getImportTypes() ); } + for ( LifecycleCallbackMethodReference reference : beforeMappingReferencesWithMappingTarget ) { + types.addAll( reference.getImportTypes() ); + } + for ( LifecycleCallbackMethodReference reference : beforeMappingReferencesWithoutMappingTarget ) { + types.addAll( reference.getImportTypes() ); + } + for ( LifecycleCallbackMethodReference reference : afterMappingReferences ) { + types.addAll( reference.getImportTypes() ); + } + return types; } @@ -178,7 +189,7 @@ public String toString() { private List filterMappingTarget(List methods, boolean mustHaveMappingTargetParameter) { if ( methods == null ) { - return null; + return Collections.emptyList(); } List result = diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/Issue2807Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/Issue2807Test.java new file mode 100644 index 0000000000..2cb96ce37e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/Issue2807Test.java @@ -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._2807; + +import org.mapstruct.ap.test.bugs._2807.after.AfterMethod; +import org.mapstruct.ap.test.bugs._2807.before.BeforeMethod; +import org.mapstruct.ap.test.bugs._2807.beforewithtarget.BeforeWithTarget; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; + +/** + * @author Ben Zegveld + */ +@IssueKey( "2807" ) +public class Issue2807Test { + + @ProcessorTest + @WithSpring + @WithClasses( { SpringLifeCycleMapper.class, BeforeMethod.class, BeforeWithTarget.class, AfterMethod.class } ) + void shouldCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/SpringLifeCycleMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/SpringLifeCycleMapper.java new file mode 100644 index 0000000000..daabe7f52a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/SpringLifeCycleMapper.java @@ -0,0 +1,26 @@ +/* + * 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._2807; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.ap.test.bugs._2807.after.AfterMethod; +import org.mapstruct.ap.test.bugs._2807.before.BeforeMethod; +import org.mapstruct.ap.test.bugs._2807.beforewithtarget.BeforeWithTarget; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@Mapper( componentModel = "spring", uses = { BeforeMethod.class, AfterMethod.class, + BeforeWithTarget.class }, unmappedTargetPolicy = ReportingPolicy.IGNORE ) +public interface SpringLifeCycleMapper { + SpringLifeCycleMapper INSTANCE = Mappers.getMapper( SpringLifeCycleMapper.class ); + + List map(List list); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/after/AfterMethod.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/after/AfterMethod.java new file mode 100644 index 0000000000..f7c7457348 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/after/AfterMethod.java @@ -0,0 +1,23 @@ +/* + * 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._2807.after; + +import java.util.List; + +import org.mapstruct.AfterMapping; +import org.mapstruct.MappingTarget; + +/** + * @author Ben Zegveld + */ +public class AfterMethod { + private AfterMethod() { + } + + @AfterMapping + public static void doNothing(@MappingTarget List source) { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/before/BeforeMethod.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/before/BeforeMethod.java new file mode 100644 index 0000000000..5252bee150 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/before/BeforeMethod.java @@ -0,0 +1,21 @@ +/* + * 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._2807.before; + +import org.mapstruct.BeforeMapping; + +/** + * @author Ben Zegveld + */ +public class BeforeMethod { + private BeforeMethod() { + } + + @BeforeMapping + public static void doNothing(Iterable source) { + return; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/beforewithtarget/BeforeWithTarget.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/beforewithtarget/BeforeWithTarget.java new file mode 100644 index 0000000000..69e62bdea1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/beforewithtarget/BeforeWithTarget.java @@ -0,0 +1,23 @@ +/* + * 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._2807.beforewithtarget; + +import java.util.List; + +import org.mapstruct.BeforeMapping; +import org.mapstruct.MappingTarget; + +/** + * @author Ben Zegveld + */ +public class BeforeWithTarget { + private BeforeWithTarget() { + } + + @BeforeMapping + public static void doNothingBeforeWithTarget(Iterable source, @MappingTarget List target) { + } +} From a4162809a400f297e3c8c51e03f1cd6f8fe8a90d Mon Sep 17 00:00:00 2001 From: Hao Zhang <452227361@qq.com> Date: Sat, 28 May 2022 17:37:21 +0800 Subject: [PATCH 255/597] Doc: correct the annotation processor version (#2859) The lombok-mapstruct-binding anotation procossor version given by document will result a compile problem, correct it by the example repository so that work fine --- .../chapter-14-third-party-api-integration.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc index c31424a0de..69bef46ea6 100644 --- a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc +++ b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc @@ -58,7 +58,7 @@ This resolves the compilation issues of Lombok and MapStruct modules. org.projectlombok lombok-mapstruct-binding - 0.1.0 + 0.2.0 ---- ==== @@ -121,7 +121,7 @@ The set up using Maven or Gradle does not differ from what is described in < org.projectlombok lombok-mapstruct-binding - 0.1.0 + 0.2.0 @@ -141,7 +141,7 @@ dependencies { implementation "org.mapstruct:mapstruct:${mapstructVersion}" implementation "org.projectlombok:lombok:1.18.16" - annotationProcessor "org.projectlombok:lombok-mapstruct-binding:0.1.0" + annotationProcessor "org.projectlombok:lombok-mapstruct-binding:0.2.0" annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" annotationProcessor "org.projectlombok:lombok:1.18.16" } From 9769f51756e1e3d3173a1b9026635757909b90ad Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 30 May 2022 21:09:21 +0200 Subject: [PATCH 256/597] #2851 Fix typo in readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 645f3ee992..c7f237ad06 100644 --- a/readme.md +++ b/readme.md @@ -34,7 +34,7 @@ Compared to mapping frameworks working at runtime, MapStruct offers the followin * mappings are incorrect (cannot find a proper mapping method or type conversion) * **Easily debuggable mapping code** (or editable by hand—e.g. in case of a bug in the generator) -To create a mapping between two types, declare a mapper class like this: +To create a mapping between two types, declare a mapper interface like this: ```java @Mapper From c945ccd628a46cda862532f38acce5b2b070c3f8 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 30 May 2022 21:20:09 +0200 Subject: [PATCH 257/597] #2835 Upgrade jacoco-maven-plugin to latest 0.8.8 to support Java 17 --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 1a9fddff92..4b5ea78486 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -545,7 +545,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.8 org.jvnet.jaxb2.maven2 From 0559c47c2174254a3a8fa8ec828ec9dd14d393c0 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Mon, 30 May 2022 21:51:57 +0200 Subject: [PATCH 258/597] #2739 Enhance documentation around SPI usage --- .../chapter-13-using-mapstruct-spi.asciidoc | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc index 51fec968bf..1095d41656 100644 --- a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc +++ b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc @@ -1,7 +1,18 @@ [[using-spi]] == Using the MapStruct SPI + +To use a custom SPI implementation, it must be located in a separate JAR file together with a file named after the SPI (e.g. `org.mapstruct.ap.spi.AccessorNamingStrategy`) in `META-INF/services/` with the fully qualified name of your custom implementation as content (e.g. `org.mapstruct.example.CustomAccessorNamingStrategy`). This JAR file needs to be added to the annotation processor classpath (i.e. add it next to the place where you added the mapstruct-processor jar). + + +[NOTE] +==== +It might also be necessary to add the jar to your IDE's annotation processor factory path. Otherwise you might get an error stating that it cannot be found, while a run using your build tool does succeed. +==== + === Custom Accessor Naming Strategy +SPI name: `org.mapstruct.ap.spi.AccessorNamingStrategy` + MapStruct offers the possibility to override the `AccessorNamingStrategy` via the Service Provider Interface (SPI). A nice example is the use of the fluent API on the source object `GolfPlayer` and `GolfPlayerDto` below. .Source object GolfPlayer with fluent API. @@ -121,14 +132,14 @@ public class CustomAccessorNamingStrategy extends DefaultAccessorNamingStrategy ==== The `CustomAccessorNamingStrategy` makes use of the `DefaultAccessorNamingStrategy` (also available in mapstruct-processor) and relies on that class to leave most of the default behaviour unchanged. -To use a custom SPI implementation, it must be located in a separate JAR file together with the file `META-INF/services/org.mapstruct.ap.spi.AccessorNamingStrategy` with the fully qualified name of your custom implementation as content (e.g. `org.mapstruct.example.CustomAccessorNamingStrategy`). This JAR file needs to be added to the annotation processor classpath (i.e. add it next to the place where you added the mapstruct-processor jar). - [TIP] Fore more details: The example above is present in our examples repository (https://github.com/mapstruct/mapstruct-examples). -[mapping-exclusion-provider] +[[mapping-exclusion-provider]] === Mapping Exclusion Provider +SPI name: `org.mapstruct.ap.spi.MappingExclusionProvider` + MapStruct offers the possibility to override the `MappingExclusionProvider` via the Service Provider Interface (SPI). A nice example is to not allow MapStruct to create an automatic sub-mapping for a certain type, i.e. MapStruct will not try to generate an automatic sub-mapping method for an excluded type. @@ -177,16 +188,12 @@ include::{processor-ap-test}/nestedbeans/exclusions/custom/CustomMappingExclusio ---- ==== -To use a custom SPI implementation, it must be located in a separate JAR file -together with the file `META-INF/services/org.mapstruct.ap.spi.MappingExclusionProvider` with the fully qualified name of your custom implementation as content -(e.g. `org.mapstruct.example.CustomMappingExclusionProvider`). -This JAR file needs to be added to the annotation processor classpath -(i.e. add it next to the place where you added the mapstruct-processor jar). - [[custom-builder-provider]] === Custom Builder Provider +SPI name: org.mapstruct.ap.spi.BuilderProvider + MapStruct offers the possibility to override the `DefaultProvider` via the Service Provider Interface (SPI). A nice example is to provide support for a custom builder strategy. @@ -202,6 +209,8 @@ include::{processor-ap-main}/spi/NoOpBuilderProvider.java[tag=documentation] [[custom-enum-naming-strategy]] === Custom Enum Naming Strategy +SPI name: `org.mapstruct.ap.spi.EnumMappingStrategy` + MapStruct offers the possibility to override the `EnumMappingStrategy` via the Service Provider Interface (SPI). This can be used when you have certain enums that follow some conventions within your organization. For example all enums which implement an interface named `CustomEnumMarker` are prefixed with `CUSTOM_` @@ -349,6 +358,8 @@ public class CheeseTypeMapperImpl implements CheeseTypeMapper { [[custom-enum-transformation-strategy]] === Custom Enum Transformation Strategy +SPI name: `org.mapstruct.ap.spi.EnumTransformationStrategy` + MapStruct offers the possibility to other transformations strategies by implementing `EnumTransformationStrategy` via the Service Provider Interface (SPI). A nice example is to provide support for a custom transformation strategy. From a1a0786cf2c25651eae795f3c2649e75b4b641f9 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 2 Jun 2022 22:14:42 +0200 Subject: [PATCH 259/597] #2846 Add test case showing that everything works as expected --- .../string/StringConversionTest.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java index 994b47957b..37d3c77b04 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/string/StringConversionTest.java @@ -69,6 +69,33 @@ public void shouldApplyStringConversions() { assertThat( target.getSb() ).isEqualTo( "SB" ); } + @IssueKey("2846") + @ProcessorTest + public void shouldNotApplyStringConversionsWhenNull() { + Source source = new Source(); + + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( "0" ); + assertThat( target.getBb() ).isNull(); + assertThat( target.getS() ).isEqualTo( "0" ); + assertThat( target.getSs() ).isNull(); + assertThat( target.getI() ).isEqualTo( "0" ); + assertThat( target.getIi() ).isNull(); + assertThat( target.getL() ).isEqualTo( "0" ); + assertThat( target.getLl() ).isNull(); + assertThat( target.getF() ).isEqualTo( "0.0" ); + assertThat( target.getFf() ).isNull(); + assertThat( target.getD() ).isEqualTo( "0.0" ); + assertThat( target.getDd() ).isNull(); + assertThat( target.getBool() ).isEqualTo( "false" ); + assertThat( target.getBoolBool() ).isNull(); + assertThat( target.getC() ).isEqualTo( String.valueOf( '\u0000' ) ); + assertThat( target.getCc() ).isNull(); + assertThat( target.getSb() ).isNull(); + } + @ProcessorTest public void shouldApplyReverseStringConversions() { Target target = new Target(); From efa11ba312bf601179289369b219c97ed9307f07 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 2 Jun 2022 23:11:41 +0200 Subject: [PATCH 260/597] [maven-release-plugin] prepare release 1.5.0.Final --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index aaae7b5404..8cc783f39a 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Final ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 293043e6f9..903558558f 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Final ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 10c95cf3b5..bf4e4387ae 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Final ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 337ad35e51..404001b0c3 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Final ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 8850da641c..7ca09c75f4 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Final ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 4b48c19496..e103296690 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Final ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 4b5ea78486..da625ee0dc 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Final pom MapStruct Parent @@ -69,7 +69,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.0.Final diff --git a/pom.xml b/pom.xml index 28c35aea74..811dda0bfb 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Final parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.0.Final diff --git a/processor/pom.xml b/processor/pom.xml index ec3f20a241..6feeff0796 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0-SNAPSHOT + 1.5.0.Final ../parent/pom.xml From 5efe5e291c0433cafa53bd76cb5f170529f5a60a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 2 Jun 2022 23:11:41 +0200 Subject: [PATCH 261/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 8cc783f39a..0bdec734cb 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 903558558f..160f963adf 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index bf4e4387ae..ac993c73d6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 404001b0c3..9ceeb505ea 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 7ca09c75f4..0a62a3ef3c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index e103296690..364faae643 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index da625ee0dc..8c6a55ebed 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Final + 1.6.0-SNAPSHOT pom MapStruct Parent @@ -69,7 +69,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.0.Final + HEAD diff --git a/pom.xml b/pom.xml index 811dda0bfb..25a5ead6bc 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Final + 1.6.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.0.Final + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 6feeff0796..6f2bb6495c 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.0.Final + 1.6.0-SNAPSHOT ../parent/pom.xml From 07265630241fa87cb2ec443219c134aac56457c5 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 2 Jun 2022 23:30:17 +0200 Subject: [PATCH 262/597] Update readme with latest released 1.5.0.Final release --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index c7f237ad06..c56eefb771 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.4.2.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.0.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.4.2.Final + 1.5.0.Final ... @@ -114,10 +114,10 @@ plugins { dependencies { ... - compile 'org.mapstruct:mapstruct:1.4.2.Final' + compile 'org.mapstruct:mapstruct:1.5.0.Final' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.0.Final' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.0.Final' // if you are using mapstruct in test code } ... ``` From 46b78bfe59c0473d3b92c08c73bbb782e03491bd Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 4 Jun 2022 21:52:59 +0200 Subject: [PATCH 263/597] #2867 Fix NPE when reporting message on parent mappers --- .../MapperAnnotatedFormattingMessenger.java | 11 +++++- .../test/bugs/_2867/Issue2867BaseMapper.java | 17 ++++++++ .../ap/test/bugs/_2867/Issue2867Mapper.java | 31 +++++++++++++++ .../ap/test/bugs/_2867/Issue2867Test.java | 39 +++++++++++++++++++ 4 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867BaseMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperAnnotatedFormattingMessenger.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperAnnotatedFormattingMessenger.java index eeb84570b0..d3384f0b4c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperAnnotatedFormattingMessenger.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperAnnotatedFormattingMessenger.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.internal.processor; +import java.util.Objects; import java.util.stream.Collectors; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; @@ -12,6 +13,7 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic.Kind; import org.mapstruct.ap.internal.util.FormattingMessager; @@ -97,7 +99,7 @@ private String constructMethod(Element e) { if ( e instanceof ExecutableElement ) { ExecutableElement ee = (ExecutableElement) e; StringBuilder method = new StringBuilder(); - method.append( typeUtils.asElement( ee.getReturnType() ).getSimpleName() ); + method.append( typeMirrorToString( ee.getReturnType() ) ); method.append( " " ); method.append( ee.getSimpleName() ); method.append( "(" ); @@ -112,7 +114,12 @@ private String constructMethod(Element e) { } private String parameterToString(VariableElement element) { - return typeUtils.asElement( element.asType() ).getSimpleName() + " " + element.getSimpleName(); + return typeMirrorToString( element.asType() ) + " " + element.getSimpleName(); + } + + private String typeMirrorToString(TypeMirror type) { + Element element = typeUtils.asElement( type ); + return element != null ? element.getSimpleName().toString() : Objects.toString( type ); } private Message determineDelegationMessage(Element e, Message msg) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867BaseMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867BaseMapper.java new file mode 100644 index 0000000000..8ebe1aa98d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867BaseMapper.java @@ -0,0 +1,17 @@ +/* + * 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._2867; + +import org.mapstruct.MappingTarget; + +/** + * @author Filip Hrisafov + */ +public interface Issue2867BaseMapper { + + void update(@MappingTarget T target, S source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867Mapper.java new file mode 100644 index 0000000000..77dfda0fc9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867Mapper.java @@ -0,0 +1,31 @@ +/* + * 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._2867; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2867Mapper extends Issue2867BaseMapper { + + class Target { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + class Source { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867Test.java new file mode 100644 index 0000000000..5f0e7ad763 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2867/Issue2867Test.java @@ -0,0 +1,39 @@ +/* + * 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._2867; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Issue2867BaseMapper.class, + Issue2867Mapper.class, +}) +@IssueKey("2867") +class Issue2867Test { + + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = @Diagnostic( + type = Issue2867Mapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 14, + message = "Unmapped target property: \"name\"." + + " Occured at 'void update(T target, S source)' in 'Issue2867BaseMapper'." + ) + ) + @ProcessorTest + void shouldCompile() { + + } +} From ec9288ce6693abc1d14687d315f10e7e7697dd5e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Jun 2022 08:42:55 +0200 Subject: [PATCH 264/597] [maven-release-plugin] prepare release 1.5.1.Final --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0bdec734cb..58ab41d726 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.1.Final ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 160f963adf..8ef899caaf 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.1.Final ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index ac993c73d6..527ace445d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.1.Final ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 9ceeb505ea..73a358a613 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.1.Final ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 0a62a3ef3c..d55ec5dae5 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.1.Final ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 364faae643..4881caebfd 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.1.Final ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 8c6a55ebed..48bb5c8cba 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.1.Final pom MapStruct Parent @@ -69,7 +69,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.1.Final diff --git a/pom.xml b/pom.xml index 25a5ead6bc..7a90e263b9 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.1.Final parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.1.Final diff --git a/processor/pom.xml b/processor/pom.xml index 6f2bb6495c..61fde05422 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.1.Final ../parent/pom.xml From 20e97714d4e2d357c96a5065b22223f2152a6978 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Jun 2022 08:42:56 +0200 Subject: [PATCH 265/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 58ab41d726..0bdec734cb 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.1.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 8ef899caaf..160f963adf 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.1.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 527ace445d..ac993c73d6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.1.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 73a358a613..9ceeb505ea 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.1.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index d55ec5dae5..0a62a3ef3c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.1.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 4881caebfd..364faae643 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.1.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 48bb5c8cba..8c6a55ebed 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.1.Final + 1.6.0-SNAPSHOT pom MapStruct Parent @@ -69,7 +69,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.1.Final + HEAD diff --git a/pom.xml b/pom.xml index 7a90e263b9..25a5ead6bc 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.1.Final + 1.6.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.1.Final + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 61fde05422..6f2bb6495c 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.1.Final + 1.6.0-SNAPSHOT ../parent/pom.xml From 4c9aa00369efbea00bc05b5631b5deea29baaced Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Jun 2022 08:53:10 +0200 Subject: [PATCH 266/597] Update readme with latest released 1.5.1.Final release --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index c56eefb771..e9ccc8d890 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.0.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.1.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.5.0.Final + 1.5.1.Final ... @@ -114,10 +114,10 @@ plugins { dependencies { ... - compile 'org.mapstruct:mapstruct:1.5.0.Final' + compile 'org.mapstruct:mapstruct:1.5.1.Final' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.0.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.0.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final' // if you are using mapstruct in test code } ... ``` From 9247c5d7fbc5302061be1e5cc5cca8a747320b14 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Jun 2022 12:15:26 +0200 Subject: [PATCH 267/597] #2870 Use codecov action v2 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6d80d2da89..387b004642 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,7 +39,7 @@ jobs: - name: 'Generate coverage report' run: ./mvnw jacoco:report - name: 'Upload coverage to Codecov' - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 - name: 'Publish Snapshots' if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'mapstruct/mapstruct' run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy From d7c0d15fe1ef3382471de8027f728f30fd456809 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Jun 2022 15:58:19 +0200 Subject: [PATCH 268/597] Change required Java version for running MapStruct in the readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index e9ccc8d890..0d6a93c781 100644 --- a/readme.md +++ b/readme.md @@ -130,7 +130,7 @@ To learn more about MapStruct, refer to the [project homepage](http://mapstruct. ## Building from Source -MapStruct uses Maven for its build. Java 8 is required for building MapStruct from source. To build the complete project, run +MapStruct uses Maven for its build. Java 11 is required for building MapStruct from source. To build the complete project, run mvn clean install From 05ae9922ea2bd6a6db5aac9358e8016adb5e82bb Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Jun 2022 17:24:49 +0200 Subject: [PATCH 269/597] Update GitHub actions Run tests with Java 18 Change actions/checkout to v3 Change actions/setup-java to v3 --- .github/workflows/java-ea.yml | 7 ++++--- .github/workflows/main.yml | 30 ++++++++++++++++++------------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index 76e24db3b1..3cc488ca7e 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -10,15 +10,16 @@ jobs: strategy: fail-fast: false matrix: - java: [18-ea, 19-ea] + java: [19-ea] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: 'Set up JDK' - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: ${{ matrix.java }} - name: 'Test' run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 387b004642..40999c680a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,15 +12,16 @@ jobs: strategy: fail-fast: false matrix: - java: [13, 16, 17] + java: [13, 17, 18] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: 'Set up JDK' - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: ${{ matrix.java }} - name: 'Test' run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true @@ -29,10 +30,11 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: 'Set up JDK 11' - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: 11 - name: 'Test' run: ./mvnw ${MAVEN_ARGS} install @@ -48,16 +50,18 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: 'Set up JDK 11 for building everything' - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: 11 - name: 'Install Processor' run: ./mvnw ${MAVEN_ARGS} -DskipTests install -pl processor -am - name: 'Set up JDK 8 for running integration tests' - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: 8 - name: 'Run integration tests' run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest @@ -65,10 +69,11 @@ jobs: name: 'Windows' runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: 'Set up JDK 11' - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: 11 - name: 'Test' run: ./mvnw %MAVEN_ARGS% install @@ -76,10 +81,11 @@ jobs: name: 'Mac OS' runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: 'Set up JDK 11' - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: 11 - name: 'Test' run: ./mvnw ${MAVEN_ARGS} install From 22ad9f636d9f786bc18febf1fb623a17145c33a5 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Tue, 14 Jun 2022 22:03:32 +0200 Subject: [PATCH 270/597] #2806 Try to stabilise some date conversion tests by locking them on reading the default timezone --- .../mapstruct/ap/test/conversion/date/DateConversionTest.java | 2 ++ .../java/org/mapstruct/ap/test/naming/VariableNamingTest.java | 2 ++ .../nestedmethodcall/NestedMappingMethodInvocationTest.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java index 83464f81a0..9ccf8e7e1f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.condition.EnabledOnJre; import org.junit.jupiter.api.condition.JRE; import org.junitpioneer.jupiter.DefaultLocale; +import org.junitpioneer.jupiter.ReadsDefaultTimeZone; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -35,6 +36,7 @@ }) @IssueKey("43") @DefaultLocale("de") +@ReadsDefaultTimeZone public class DateConversionTest { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/naming/VariableNamingTest.java b/processor/src/test/java/org/mapstruct/ap/test/naming/VariableNamingTest.java index 4b5c7982ea..a8e833ee06 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/naming/VariableNamingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/naming/VariableNamingTest.java @@ -12,6 +12,7 @@ import java.util.HashMap; import java.util.Map; +import org.junitpioneer.jupiter.ReadsDefaultTimeZone; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -26,6 +27,7 @@ */ @WithClasses({ SourceTargetMapper.class, While.class, Break.class, Source.class }) @IssueKey("53") +@ReadsDefaultTimeZone public class VariableNamingTest { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java index 8d600d8604..03c47f7a49 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedmethodcall/NestedMappingMethodInvocationTest.java @@ -17,6 +17,7 @@ import javax.xml.namespace.QName; import org.junitpioneer.jupiter.DefaultLocale; +import org.junitpioneer.jupiter.ReadsDefaultTimeZone; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -31,6 +32,7 @@ */ @IssueKey("134") @DefaultLocale("de") +@ReadsDefaultTimeZone public class NestedMappingMethodInvocationTest { public static final QName QNAME = new QName( "dont-care" ); From fa800926e7cbf6d1ed5b219ae79309036d2c0879 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 5 Jun 2022 18:16:00 +0200 Subject: [PATCH 271/597] #2837 Add support for text blocks in expressions --- .../itest/tests/MavenIntegrationTest.java | 7 +++ .../expressionTextBlocksTest/pom.xml | 22 ++++++++++ .../org/mapstruct/itest/textBlocks/Car.java | 22 ++++++++++ .../itest/textBlocks/CarAndWheelMapper.java | 41 +++++++++++++++++ .../mapstruct/itest/textBlocks/CarDto.java | 15 +++++++ .../itest/textBlocks/WheelPosition.java | 22 ++++++++++ .../itest/textBlocks/TextBlocksTest.java | 27 ++++++++++++ .../internal/model/source/MappingOptions.java | 2 +- .../java/JavaDefaultExpressionTest.java | 20 +++++++++ .../MultiLineDefaultExpressionMapper.java | 44 +++++++++++++++++++ .../expressions/java/JavaExpressionTest.java | 25 +++++++++++ .../java/MultiLineExpressionMapper.java | 37 ++++++++++++++++ 12 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 integrationtest/src/test/resources/expressionTextBlocksTest/pom.xml create mode 100644 integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/Car.java create mode 100644 integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/CarAndWheelMapper.java create mode 100644 integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/CarDto.java create mode 100644 integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/WheelPosition.java create mode 100644 integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/MultiLineDefaultExpressionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/MultiLineExpressionMapper.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 4f4182d5c7..7d63ccf774 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -122,6 +122,13 @@ void recordsTest() { void recordsCrossModuleTest() { } + @ProcessorTest(baseDir = "expressionTextBlocksTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC + }) + @EnabledForJreRange(min = JRE.JAVA_17) + void expressionTextBlocksTest() { + } + @ProcessorTest(baseDir = "kotlinDataTest", processorTypes = { ProcessorTest.ProcessorType.JAVAC }, forkJvm = true) diff --git a/integrationtest/src/test/resources/expressionTextBlocksTest/pom.xml b/integrationtest/src/test/resources/expressionTextBlocksTest/pom.xml new file mode 100644 index 0000000000..b70c48f9d0 --- /dev/null +++ b/integrationtest/src/test/resources/expressionTextBlocksTest/pom.xml @@ -0,0 +1,22 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + expressionTextBlocksTest + jar + + diff --git a/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/Car.java b/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/Car.java new file mode 100644 index 0000000000..28d4fd3cd5 --- /dev/null +++ b/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/Car.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.textBlocks; + +/** + * @author Filip Hrisafov + */ +public class Car { + + private WheelPosition wheelPosition; + + public WheelPosition getWheelPosition() { + return wheelPosition; + } + + public void setWheelPosition(WheelPosition wheelPosition) { + this.wheelPosition = wheelPosition; + } +} diff --git a/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/CarAndWheelMapper.java b/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/CarAndWheelMapper.java new file mode 100644 index 0000000000..23d665c6ff --- /dev/null +++ b/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/CarAndWheelMapper.java @@ -0,0 +1,41 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.textBlocks; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface CarAndWheelMapper { + + CarAndWheelMapper INSTANCE = Mappers.getMapper( CarAndWheelMapper.class ); + + @Mapping(target = "wheelPosition", + expression = + """ + java( + source.getWheelPosition() == null ? + null : + source.getWheelPosition().getPosition() + ) + """) + CarDto carDtoFromCar(Car source); + + @Mapping(target = "wheelPosition", + expression = """ + java( + source.wheelPosition() == null ? + null : + new WheelPosition(source.wheelPosition()) + ) + """) + Car carFromCarDto(CarDto source); +} diff --git a/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/CarDto.java b/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/CarDto.java new file mode 100644 index 0000000000..f4baa4d044 --- /dev/null +++ b/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/CarDto.java @@ -0,0 +1,15 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.textBlocks; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public record CarDto(String wheelPosition) { + +} diff --git a/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/WheelPosition.java b/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/WheelPosition.java new file mode 100644 index 0000000000..6effd485a3 --- /dev/null +++ b/integrationtest/src/test/resources/expressionTextBlocksTest/src/main/java/org/mapstruct/itest/textBlocks/WheelPosition.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.textBlocks; + +/** + * @author Filip Hrisafov + */ +public class WheelPosition { + + private final String position; + + public WheelPosition(String position) { + this.position = position; + } + + public String getPosition() { + return position; + } +} diff --git a/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java b/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java new file mode 100644 index 0000000000..42ed70b18e --- /dev/null +++ b/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java @@ -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.itest.textBlocks; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +public class TextBlocksTest { + + @Test + public void textBlockExpressionShouldWork() { + Car car = new Car(); + car.setWheelPosition( new WheelPosition( "left" ) ); + + CarDto carDto = CarAndWheelMapper.INSTANCE.carDtoFromCar(car); + + assertThat( carDto ).isNotNull(); + assertThat( carDto.wheelPosition() ) + .isEqualTo( "left" ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index e788b51f6e..5046ce8b29 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -37,7 +37,7 @@ */ public class MappingOptions extends DelegatingOptions { - private static final Pattern JAVA_EXPRESSION = Pattern.compile( "^java\\((.*)\\)$" ); + private static final Pattern JAVA_EXPRESSION = Pattern.compile( "^\\s*java\\((.*)\\)\\s*$", Pattern.DOTALL ); private final String sourceName; private final String constant; diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java index 3df90421b3..8f3a745e84 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/JavaDefaultExpressionTest.java @@ -5,6 +5,9 @@ */ package org.mapstruct.ap.test.source.defaultExpressions.java; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneOffset; import java.util.Date; import org.mapstruct.ap.testutil.ProcessorTest; @@ -46,6 +49,23 @@ public void testJavaDefaultExpressionWithNoValues() { assertThat( target.getSourceDate() ).isEqualTo( new Date( 30L ) ); } + @ProcessorTest + @WithClasses({ Source.class, Target.class, MultiLineDefaultExpressionMapper.class }) + public void testMultiLineJavaDefaultExpression() { + Source source = new Source(); + + Target target = MultiLineDefaultExpressionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getSourceId() ).isEqualTo( "test" ); + assertThat( target.getSourceDate() ) + .isEqualTo( Date.from( + LocalDate.of( 2022, Month.JUNE, 5 ) + .atTime( 17, 10 ) + .toInstant( ZoneOffset.UTC ) + ) ); + } + @ProcessorTest @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/MultiLineDefaultExpressionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/MultiLineDefaultExpressionMapper.java new file mode 100644 index 0000000000..ba27726377 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/defaultExpressions/java/MultiLineDefaultExpressionMapper.java @@ -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.source.defaultExpressions.java; + +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneOffset; +import java.util.Date; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(imports = { Date.class, LocalDate.class, ZoneOffset.class, Month.class }) +public interface MultiLineDefaultExpressionMapper { + + MultiLineDefaultExpressionMapper INSTANCE = Mappers.getMapper( MultiLineDefaultExpressionMapper.class ); + + @Mappings({ + @Mapping( + target = "sourceId", + source = "id", + defaultExpression = "java( new StringBuilder()\n.append( \"test\" )\n.toString() )" + ), + @Mapping( + target = "sourceDate", + source = "date", + defaultExpression = "java(" + + "Date.from(\n" + + "LocalDate.of( 2022, Month.JUNE, 5 )\n" + + ".atTime( 17, 10 )\n" + + ".toInstant( ZoneOffset.UTC )\n)" + + ")" + ) + }) + Target sourceToTarget(Source s); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java index be80644606..9a20ddcdbd 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/JavaExpressionTest.java @@ -132,6 +132,31 @@ public void testGetterOnly() throws ParseException { assertThat( target.getList() ).isEqualTo( Arrays.asList( "test2" ) ); } + @ProcessorTest + @WithClasses({ Source.class, Target.class, TimeAndFormat.class, MultiLineExpressionMapper.class }) + public void testMultiLineJavaExpressionInsertion() throws ParseException { + Source source = new Source(); + String format = "dd-MM-yyyy,hh:mm:ss"; + Date time = getTime( format, "09-01-2014,01:35:03" ); + + source.setFormat( format ); + source.setTime( time ); + + Target target = MultiLineExpressionMapper.INSTANCE.mapUsingMultiLineExpression( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getTimeAndFormat().getTime() ).isEqualTo( time ); + assertThat( target.getTimeAndFormat().getFormat() ).isEqualTo( format ); + assertThat( target.getAnotherProp() ).isNull(); + + target = MultiLineExpressionMapper.INSTANCE.mapUsingMultiLineExpressionWithLeadingSpaces( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getTimeAndFormat().getTime() ).isEqualTo( time ); + assertThat( target.getTimeAndFormat().getFormat() ).isEqualTo( format ); + assertThat( target.getAnotherProp() ).isNull(); + } + @IssueKey( "1851" ) @ProcessorTest @WithClasses({ diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/MultiLineExpressionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/MultiLineExpressionMapper.java new file mode 100644 index 0000000000..50d0b4b2cd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/MultiLineExpressionMapper.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.source.expressions.java; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.source.expressions.java.mapper.TimeAndFormat; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(imports = TimeAndFormat.class) +public interface MultiLineExpressionMapper { + + + MultiLineExpressionMapper INSTANCE = Mappers.getMapper( MultiLineExpressionMapper.class ); + + @Mappings({ + @Mapping(target = "timeAndFormat", expression = "java( new TimeAndFormat(\ns.getTime(),\ns.getFormat()\n ))"), + @Mapping(target = "anotherProp", ignore = true) + }) + Target mapUsingMultiLineExpression(Source s); + + @Mappings({ + @Mapping( + target = "timeAndFormat", + expression = " java( new TimeAndFormat(\ns.getTime(),\ns.getFormat()\n )) " + ), + @Mapping(target = "anotherProp", ignore = true) + }) + Target mapUsingMultiLineExpressionWithLeadingSpaces(Source s); +} From 98eb46aee9aec4135fed2bd0c2fbd0c621c22b08 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 18 Jun 2022 13:59:03 +0200 Subject: [PATCH 272/597] #2880 Fix missing import for array mapping methods Co-authored-by: Martin Kamp Jensen --- .../ap/internal/model/common/Type.java | 27 ++++++++++++++----- .../ap/test/bugs/_2880/Issue2880Mapper.java | 14 ++++++++++ .../ap/test/bugs/_2880/Issue2880Test.java | 25 +++++++++++++++++ .../mapstruct/ap/test/bugs/_2880/Outer.java | 16 +++++++++++ .../mapstruct/ap/test/bugs/_2880/Source.java | 21 +++++++++++++++ .../mapstruct/ap/test/bugs/_2880/Target.java | 21 +++++++++++++++ .../ap/test/bugs/_2880/TargetData.java | 13 +++++++++ 7 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Issue2880Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Issue2880Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Outer.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/TargetData.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index ba5f62254d..ce82fff621 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -181,8 +181,11 @@ public Type(TypeUtils typeUtils, ElementUtils elementUtils, TypeFactory typeFact this.loggingVerbose = loggingVerbose; - this.topLevelType = topLevelType( this.typeElement, this.typeFactory ); - this.nameWithTopLevelTypeName = nameWithTopLevelTypeName( this.typeElement ); + // The top level type for an array type is the top level type of the component type + TypeElement typeElementForTopLevel = + this.componentType == null ? this.typeElement : this.componentType.getTypeElement(); + this.topLevelType = topLevelType( typeElementForTopLevel, this.typeFactory ); + this.nameWithTopLevelTypeName = nameWithTopLevelTypeName( typeElementForTopLevel, this.name ); } //CHECKSTYLE:ON @@ -218,11 +221,21 @@ public String getName() { * (if the top level type is important, otherwise the fully-qualified name. */ public String createReferenceName() { - if ( isToBeImported() || shouldUseSimpleName() ) { + if ( isToBeImported() ) { + // isToBeImported() returns true for arrays. + // Therefore, we need to check the top level type when creating the reference + if ( isTopLevelTypeToBeImported() ) { + return nameWithTopLevelTypeName != null ? nameWithTopLevelTypeName : name; + } + + return name; + } + + if ( shouldUseSimpleName() ) { return name; } - if ( isTopLevelTypeToBeImported() && nameWithTopLevelTypeName != null ) { + if ( isTopLevelTypeToBeImported() && nameWithTopLevelTypeName != null) { return nameWithTopLevelTypeName; } @@ -1566,16 +1579,16 @@ private String trimSimpleClassName(String className) { return trimmedClassName; } - private static String nameWithTopLevelTypeName(TypeElement element) { + private static String nameWithTopLevelTypeName(TypeElement element, String name) { if ( element == null ) { return null; } if ( !element.getNestingKind().isNested() ) { - return element.getSimpleName().toString(); + return name; } Deque elements = new ArrayDeque<>(); - elements.addFirst( element.getSimpleName() ); + elements.addFirst( name ); Element parent = element.getEnclosingElement(); while ( parent != null && parent.getKind() != ElementKind.PACKAGE ) { elements.addFirst( parent.getSimpleName() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Issue2880Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Issue2880Mapper.java new file mode 100644 index 0000000000..218f6d384b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Issue2880Mapper.java @@ -0,0 +1,14 @@ +/* + * 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._2880; + +import org.mapstruct.Mapper; + +@Mapper +public interface Issue2880Mapper { + + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Issue2880Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Issue2880Test.java new file mode 100644 index 0000000000..e3d0f810c8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Issue2880Test.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.ap.test.bugs._2880; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +@IssueKey("2880") +@WithClasses({ + Issue2880Mapper.class, + Outer.class, + Source.class, + Target.class, + TargetData.class +}) +public class Issue2880Test { + + @ProcessorTest + void shouldCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Outer.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Outer.java new file mode 100644 index 0000000000..90b756137a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Outer.java @@ -0,0 +1,16 @@ +/* + * 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._2880; + +public class Outer { + + public static class SourceData { + + // CHECKSTYLE:OFF + public String value; + // CHECKSTYLE:ON + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Source.java new file mode 100644 index 0000000000..0ab3b81661 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Source.java @@ -0,0 +1,21 @@ +/* + * 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._2880; + +import java.util.List; + +public class Source { + + // CHECKSTYLE:OFF + public Outer.SourceData[] data1; + + public Outer.SourceData[] data2; + + public List data3; + + public List data4; + // CHECKSTYLE:ON +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Target.java new file mode 100644 index 0000000000..28c6b1cca1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/Target.java @@ -0,0 +1,21 @@ +/* + * 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._2880; + +import java.util.List; + +public class Target { + + // CHECKSTYLE:OFF + public TargetData[] data1; + + public List data2; + + public TargetData[] data3; + + public List data4; + // CHECKSTYLE:ON +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/TargetData.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/TargetData.java new file mode 100644 index 0000000000..28cca96601 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2880/TargetData.java @@ -0,0 +1,13 @@ +/* + * 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._2880; + +public class TargetData { + + // CHECKSTYLE:OFF + public String value; + // CHECKSTYLE:ON +} From 406ae3fc13a665fd52144dd476af371623b73a88 Mon Sep 17 00:00:00 2001 From: Sergei Portnov <3230150+prtnv@users.noreply.github.com> Date: Sat, 18 Jun 2022 19:47:07 +0300 Subject: [PATCH 273/597] #2891 Fix subclass mapping while superclass has non-empty constructor Co-authored-by: Filip Hrisafov --- .../ap/internal/model/BeanMappingMethod.java | 10 ++- .../ap/test/bugs/_2891/Issue2891Mapper.java | 76 +++++++++++++++++++ .../ap/test/bugs/_2891/Issue2891Test.java | 30 ++++++++ .../test/bugs/_2891/Issue2891MapperImpl.java | 61 +++++++++++++++ 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Test.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2891/Issue2891MapperImpl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 267702826b..d17718370d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -704,6 +704,14 @@ else if ( matchingFactoryMethods.size() == 1 ) { } private ConstructorAccessor getConstructorAccessor(Type type) { + if ( type.isAbstract() ) { + // We cannot construct abstract classes. + // Usually we won't reach here, + // but if SubclassMapping is used with SubclassExhaustiveStrategy#RUNTIME_EXCEPTION + // then we will still generate the code. + // We shouldn't generate anything for those abstract types + return null; + } if ( type.isRecord() ) { // If the type is a record then just get the record components and use then @@ -1783,7 +1791,7 @@ public boolean hasSubclassMappings() { } public boolean isAbstractReturnType() { - return getFactoryMethod() == null && !hasConstructorMappings() && returnTypeToConstruct != null + return getFactoryMethod() == null && returnTypeToConstruct != null && returnTypeToConstruct.isAbstract(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Mapper.java new file mode 100644 index 0000000000..205978aeb8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Mapper.java @@ -0,0 +1,76 @@ +/* + * 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._2891; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; + +/** + * @author Sergei Portnov + */ +@Mapper +public interface Issue2891Mapper { + + @BeanMapping(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION) + @SubclassMapping(source = Source1.class, target = Target1.class) + @SubclassMapping(source = Source2.class, target = Target2.class) + AbstractTarget map(AbstractSource source); + + abstract class AbstractTarget { + + private final String name; + + protected AbstractTarget(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + class Target1 extends AbstractTarget { + + protected Target1(String name) { + super( name ); + } + } + + class Target2 extends AbstractTarget { + + protected Target2(String name) { + super( name ); + } + } + + abstract class AbstractSource { + + private final String name; + + protected AbstractSource(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + class Source1 extends AbstractSource { + protected Source1(String name) { + super( name ); + } + } + + class Source2 extends AbstractSource { + + protected Source2(String name) { + super( name ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Test.java new file mode 100644 index 0000000000..c82d5aa63e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Test.java @@ -0,0 +1,30 @@ +/* + * 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._2891; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +/** + * @author Sergei Portnov + */ +@WithClasses({ + Issue2891Mapper.class +}) +@IssueKey("2891") +class Issue2891Test { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() + .addComparisonToFixtureFor( Issue2891Mapper.class ); + + @ProcessorTest + void shouldCompile() { + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2891/Issue2891MapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2891/Issue2891MapperImpl.java new file mode 100644 index 0000000000..64ea2dc834 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2891/Issue2891MapperImpl.java @@ -0,0 +1,61 @@ +/* + * 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._2891; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2022-06-18T14:48:32+0300", + comments = "version: , compiler: Eclipse JDT (Batch) 3.20.0.v20191203-2131, environment: Java 11.0.15.1 (BellSoft)" +) +public class Issue2891MapperImpl implements Issue2891Mapper { + + @Override + public AbstractTarget map(AbstractSource source) { + if ( source == null ) { + return null; + } + + if (source instanceof Source1) { + return source1ToTarget1( (Source1) source ); + } + else if (source instanceof Source2) { + return source2ToTarget2( (Source2) source ); + } + else { + throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for " + source.getClass()); + } + } + + protected Target1 source1ToTarget1(Source1 source1) { + if ( source1 == null ) { + return null; + } + + String name = null; + + name = source1.getName(); + + Target1 target1 = new Target1( name ); + + return target1; + } + + protected Target2 source2ToTarget2(Source2 source2) { + if ( source2 == null ) { + return null; + } + + String name = null; + + name = source2.getName(); + + Target2 target2 = new Target2( name ); + + return target2; + } +} From 19973ff818e20531cbd81e5048404fd48c3c7508 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 18 Jun 2022 19:01:16 +0200 Subject: [PATCH 274/597] [maven-release-plugin] prepare release 1.5.2.Final --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0bdec734cb..2979ad289d 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.2.Final ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 160f963adf..9bce0a907a 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.2.Final ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index ac993c73d6..b2dec2c1ea 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.2.Final ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 9ceeb505ea..2c2351640c 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.2.Final ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 0a62a3ef3c..0691b8b88a 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.2.Final ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 364faae643..0c2f866da0 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.2.Final ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 8c6a55ebed..335d917363 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.2.Final pom MapStruct Parent @@ -69,7 +69,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.2.Final diff --git a/pom.xml b/pom.xml index 25a5ead6bc..66b25f8d89 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.2.Final parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.5.2.Final diff --git a/processor/pom.xml b/processor/pom.xml index 6f2bb6495c..20e8150f91 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.5.2.Final ../parent/pom.xml From 1459aabfc3f1867d8e6ebbae641f1e38b6a1c0e2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 18 Jun 2022 19:01:18 +0200 Subject: [PATCH 275/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 2979ad289d..0bdec734cb 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.2.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 9bce0a907a..160f963adf 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.2.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index b2dec2c1ea..ac993c73d6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.2.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 2c2351640c..9ceeb505ea 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.2.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 0691b8b88a..0a62a3ef3c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.2.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 0c2f866da0..364faae643 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.2.Final + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 335d917363..8c6a55ebed 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.5.2.Final + 1.6.0-SNAPSHOT pom MapStruct Parent @@ -69,7 +69,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.2.Final + HEAD diff --git a/pom.xml b/pom.xml index 66b25f8d89..25a5ead6bc 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.5.2.Final + 1.6.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.5.2.Final + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 20e8150f91..6f2bb6495c 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.5.2.Final + 1.6.0-SNAPSHOT ../parent/pom.xml From 07d144ebd1167fd4549835db0799413943091d4a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 18 Jun 2022 19:11:39 +0200 Subject: [PATCH 276/597] Update readme with latest released 1.5.2.Final release --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index 0d6a93c781..e5811060a6 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.1.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.2.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.5.1.Final + 1.5.2.Final ... @@ -114,10 +114,10 @@ plugins { dependencies { ... - compile 'org.mapstruct:mapstruct:1.5.1.Final' + compile 'org.mapstruct:mapstruct:1.5.2.Final' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.2.Final' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.2.Final' // if you are using mapstruct in test code } ... ``` From 88745d151ec60c7a0c2b8b840189c2db06fe053f Mon Sep 17 00:00:00 2001 From: Ben Zegveld Date: Thu, 9 Jun 2022 22:36:47 +0200 Subject: [PATCH 277/597] #2882: target type is now correctly passed on through the MethodReferencePresenceCheck to the MethodReference. --- .../model/MethodReferencePresenceCheck.ftl | 3 ++- .../ap/internal/model/macro/CommonMacros.ftl | 3 ++- .../basic/ConditionalMappingTest.java | 6 +++++ .../ConditionalMethodWithTargetType.java | 27 +++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithTargetType.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl index 44ec296736..9a2837a02d 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl @@ -6,4 +6,5 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MethodReferencePresenceCheck" --> -<@includeModel object=methodReference/> \ No newline at end of file +<@includeModel object=methodReference + targetType=ext.targetType/> \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index 048402401b..6d05ddd131 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -15,7 +15,8 @@ --> <#macro handleSourceReferenceNullCheck> <#if sourcePresenceCheckerReference??> - if ( <@includeModel object=sourcePresenceCheckerReference /> ) { + if ( <@includeModel object=sourcePresenceCheckerReference + targetType=ext.targetType/> ) { <#nested> } <@elseDefaultAssignment/> diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java index 62fd79ae84..57a68a9526 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java @@ -50,6 +50,12 @@ public void conditionalMethodInMapper() { assertThat( employee.getName() ).isNull(); } + @IssueKey( "2882" ) + @ProcessorTest + @WithClasses( { ConditionalMethodWithTargetType.class } ) + public void conditionalMethodWithTargetTypeShouldCompile() { + } + @ProcessorTest @WithClasses({ ConditionalMethodAndBeanPresenceCheckMapper.class diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithTargetType.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithTargetType.java new file mode 100644 index 0000000000..5ec242d8ff --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodWithTargetType.java @@ -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.conditional.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.TargetType; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface ConditionalMethodWithTargetType { + + ConditionalMethodWithTargetType INSTANCE = Mappers.getMapper( ConditionalMethodWithTargetType.class ); + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition + default boolean isNotBlank(String value, @TargetType Class targetType) { + return value != null && !value.trim().isEmpty(); + } +} From de8c0c7070aabd10656c67088b5c7b52b92c954b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 18 Jun 2022 15:16:21 +0200 Subject: [PATCH 278/597] Use UTF-8 when compiling the tests The test infrastructure that we are using should use UTF-8 for generating the StandardJavaFileManager --- .../mapstruct/ap/testutil/runner/JdkCompilingExtension.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java index 2a0b6ba537..7b44a4733b 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java @@ -7,6 +7,7 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -52,7 +53,7 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe String additionalCompilerClasspath) { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector diagnostics = new DiagnosticCollector<>(); - StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null ); + StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, StandardCharsets.UTF_8 ); Iterable compilationUnits = fileManager.getJavaFileObjectsFromFiles( getSourceFiles( compilationRequest.getSourceClasses() ) ); From a2b4454a669759a9e423e2b85e803ef6d4ddf251 Mon Sep 17 00:00:00 2001 From: fml2 <534392+fml2@users.noreply.github.com> Date: Fri, 1 Jul 2022 17:18:46 +0200 Subject: [PATCH 279/597] fix(docs): No Lombok classes in the runtime --- .../asciidoc/chapter-14-third-party-api-integration.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc index 69bef46ea6..0f12d67e22 100644 --- a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc +++ b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc @@ -140,7 +140,7 @@ The set up using Maven or Gradle does not differ from what is described in < Date: Wed, 6 Jul 2022 14:39:21 +0200 Subject: [PATCH 280/597] #2922 Fix protobuf tests for M1 Macs --- .../src/test/resources/protobufBuilderTest/pom.xml | 6 +++--- parent/pom.xml | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/integrationtest/src/test/resources/protobufBuilderTest/pom.xml b/integrationtest/src/test/resources/protobufBuilderTest/pom.xml index ecbbf1d6d8..c0d8e2a81b 100644 --- a/integrationtest/src/test/resources/protobufBuilderTest/pom.xml +++ b/integrationtest/src/test/resources/protobufBuilderTest/pom.xml @@ -23,7 +23,7 @@ 1.6.0 - 0.5.1 + 0.6.1 @@ -55,10 +55,10 @@ compile-custom - com.google.protobuf:protoc:3.2.0:exe:${os.detected.classifier} + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} grpc-java - io.grpc:protoc-gen-grpc-java:1.2.0:exe:${os.detected.classifier} + io.grpc:protoc-gen-grpc-java:1.47.0:exe:${os.detected.classifier} diff --git a/parent/pom.xml b/parent/pom.xml index 8c6a55ebed..1c031a7b71 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -41,6 +41,7 @@ The processor module needs at least Java 11. --> 1.8 + 3.21.2 @@ -234,7 +235,7 @@ com.google.protobuf protobuf-java - 3.16.1 + ${protobuf.version} org.inferred From dd5ac3b637e20db014d89560e7467d09a65075e2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 8 Jul 2022 20:40:26 +0200 Subject: [PATCH 281/597] #2929 Improve documentation for `BeanMapping#ignoreByDefault` --- .../src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index d1c63c25a2..ab33666435 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -40,6 +40,7 @@ The property name as defined in the http://www.oracle.com/technetwork/java/javas [TIP] ==== By means of the `@BeanMapping(ignoreByDefault = true)` the default behavior will be *explicit mapping*, meaning that all mappings have to be specified by means of the `@Mapping` and no warnings will be issued on missing target properties. +This allows to ignore all fields, except the ones that are explicitly defined through `@Mapping`. ==== [TIP] ==== From 691488951027b0b016f52928e746677a39201900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cassius=20Vinicius=20de=20Magalh=C3=A3es?= Date: Thu, 14 Jul 2022 14:38:18 -0300 Subject: [PATCH 282/597] Update Chapter 11.2 - Inverse Mappings Clarification of the inverse mapping usage. --- .../chapter-11-reusing-mapping-configurations.asciidoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc b/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc index fb8c71a622..efacaedbe5 100644 --- a/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc +++ b/documentation/src/main/asciidoc/chapter-11-reusing-mapping-configurations.asciidoc @@ -46,6 +46,8 @@ In case of bi-directional mappings, e.g. from entity to DTO and from DTO to enti Use the annotation `@InheritInverseConfiguration` to indicate that a method shall inherit the inverse configuration of the corresponding reverse method. +In the example below, there is no need to write the inverse mapping manually. Think of a case where there are several mappings, so writing the inverse ones can be cumbersome and error prone. + .Inverse mapping method inheriting its configuration and ignoring some of them ==== [source, java, linenums] @@ -162,4 +164,4 @@ The attributes `@Mapper#mappingInheritanceStrategy()` / `@MapperConfig#mappingIn * `EXPLICIT` (default): the configuration will only be inherited, if the target mapping method is annotated with `@InheritConfiguration` and the source and target types are assignable to the corresponding types of the prototype method, all as described in <>. * `AUTO_INHERIT_FROM_CONFIG`: the configuration will be inherited automatically, if the source and target types of the target mapping method are assignable to the corresponding types of the prototype method. If multiple prototype methods match, the ambiguity must be resolved using `@InheritConfiguration(name = ...)` which will cause `AUTO_INHERIT_FROM_CONFIG` to be ignored. * `AUTO_INHERIT_REVERSE_FROM_CONFIG`: the inverse configuration will be inherited automatically, if the source and target types of the target mapping method are assignable to the corresponding types of the prototype method. If multiple prototype methods match, the ambiguity must be resolved using `@InheritInverseConfiguration(name = ...)` which will cause ``AUTO_INHERIT_REVERSE_FROM_CONFIG` to be ignored. -* `AUTO_INHERIT_ALL_FROM_CONFIG`: both the configuration and the inverse configuration will be inherited automatically. The same rules apply as for `AUTO_INHERIT_FROM_CONFIG` or `AUTO_INHERIT_REVERSE_FROM_CONFIG`. \ No newline at end of file +* `AUTO_INHERIT_ALL_FROM_CONFIG`: both the configuration and the inverse configuration will be inherited automatically. The same rules apply as for `AUTO_INHERIT_FROM_CONFIG` or `AUTO_INHERIT_REVERSE_FROM_CONFIG`. From 62e73464b2c3dd16c0c88a3d5bfebe7b710facec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Jul 2022 12:42:32 +0000 Subject: [PATCH 283/597] Bump kotlin-stdlib in /integrationtest/src/test/resources/kotlinDataTest Bumps [kotlin-stdlib](https://github.com/JetBrains/kotlin) from 1.3.70 to 1.6.0. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.6.0/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.3.70...v1.6.0) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin:kotlin-stdlib dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- integrationtest/src/test/resources/kotlinDataTest/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrationtest/src/test/resources/kotlinDataTest/pom.xml b/integrationtest/src/test/resources/kotlinDataTest/pom.xml index 8a43a04194..f86f92b9c4 100644 --- a/integrationtest/src/test/resources/kotlinDataTest/pom.xml +++ b/integrationtest/src/test/resources/kotlinDataTest/pom.xml @@ -22,7 +22,7 @@ jar - 1.3.70 + 1.6.0 From 8fa286fe4ce790cc2a65adff55c813bc9717437a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Iva=C4=8Di=C4=8D?= Date: Mon, 30 May 2022 12:33:47 +0200 Subject: [PATCH 284/597] #2688: Support accessing to the target property name --- .../main/java/org/mapstruct/Condition.java | 1 + .../org/mapstruct/TargetPropertyName.java | 25 ++ ...apter-10-advanced-mapping-options.asciidoc | 57 ++++ .../ap/internal/gem/GemGenerator.java | 2 + .../ap/internal/model/common/Parameter.java | 22 +- .../model/common/ParameterBinding.java | 28 +- .../internal/model/source/SourceMethod.java | 9 +- .../model/source/selector/TypeSelector.java | 16 +- .../ap/internal/model/MethodReference.ftl | 3 + .../model/MethodReferencePresenceCheck.ftl | 1 + .../ap/internal/model/PropertyMapping.ftl | 1 + .../ap/internal/model/TypeConversion.ftl | 1 + .../model/assignment/Java8FunctionWrapper.ftl | 1 + .../model/assignment/LocalVarWrapper.ftl | 1 + .../model/assignment/ReturnWrapper.ftl | 1 + .../ap/internal/model/macro/CommonMacros.ftl | 6 +- .../targetpropertyname/Address.java | 21 ++ .../targetpropertyname/AddressDto.java | 21 ++ ...ollectionMapperWithTargetPropertyName.java | 41 +++ ...onalMethodInMapperWithAllExceptTarget.java | 46 +++ ...nditionalMethodInMapperWithAllOptions.java | 52 ++++ ...lMethodInMapperWithTargetPropertyName.java | 32 ++ ...hodInUsesMapperWithTargetPropertyName.java | 36 +++ ...WithTargetPropertyNameInContextMapper.java | 89 ++++++ .../targetpropertyname/DomainModel.java | 15 + .../targetpropertyname/Employee.java | 99 ++++++ .../targetpropertyname/EmployeeDto.java | 98 ++++++ .../TargetPropertyNameTest.java | 281 ++++++++++++++++++ 28 files changed, 993 insertions(+), 13 deletions(-) create mode 100644 core/src/main/java/org/mapstruct/TargetPropertyName.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java diff --git a/core/src/main/java/org/mapstruct/Condition.java b/core/src/main/java/org/mapstruct/Condition.java index ec1bf06a88..148ec4879d 100644 --- a/core/src/main/java/org/mapstruct/Condition.java +++ b/core/src/main/java/org/mapstruct/Condition.java @@ -23,6 +23,7 @@ * e.g. the value given by calling {@code getName()} for the name property of the source bean *
    • The mapping source parameter
    • *
    • {@code @}{@link Context} parameter
    • + *
    • {@code @}{@link TargetPropertyName} parameter
    • *
    * * Note: The usage of this annotation is mandatory diff --git a/core/src/main/java/org/mapstruct/TargetPropertyName.java b/core/src/main/java/org/mapstruct/TargetPropertyName.java new file mode 100644 index 0000000000..17af966fa8 --- /dev/null +++ b/core/src/main/java/org/mapstruct/TargetPropertyName.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; + +/** + * This annotation marks a presence check method parameter as a property name parameter. + *

    + * This parameter enables conditional filtering based on target property name at run-time. + * Parameter must be of type {@link String} and can be present only in {@link Condition} method. + *

    + * @author Nikola Ivačič + * @since 1.6 + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.CLASS) +public @interface TargetPropertyName { +} diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 1cc19fcc3e..decee792d0 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -404,6 +404,61 @@ public class CarMapperImpl implements CarMapper { ---- ==== +Additionally `@TargetPropertyName` of type `java.lang.String` can be used in custom condition check method: + +.Mapper using custom condition check method with `@TargetPropertyName` +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface CarMapper { + + CarDto carToCarDto(Car car, @MappingTarget CarDto carDto); + + @Condition + default boolean isNotEmpty(String value, @TargetPropertyName String name) { + if ( name.equals( "owner" ) { + return value != null + && !value.isEmpty() + && !value.equals( value.toLowerCase() ); + } + return value != null && !value.isEmpty(); + } +} +---- +==== + +The generated mapper with `@TargetPropertyName` will look like: + +.Custom condition check in generated implementation +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class CarMapperImpl implements CarMapper { + + @Override + public CarDto carToCarDto(Car car, CarDto carDto) { + if ( car == null ) { + return carDto; + } + + if ( isNotEmpty( car.getOwner(), "owner" ) ) { + carDto.setOwner( car.getOwner() ); + } else { + carDto.setOwner( null ); + } + + // Mapping of other properties + + return carDto; + } +} +---- +==== + [IMPORTANT] ==== If there is a custom `@Condition` method applicable for the property it will have a precedence over a presence check method in the bean itself. @@ -412,6 +467,8 @@ If there is a custom `@Condition` method applicable for the property it will hav [NOTE] ==== Methods annotated with `@Condition` in addition to the value of the source property can also have the source parameter as an input. + +`@TargetPropertyName` parameter can only be used in `@Condition` methods. ==== <> is also valid for `@Condition` methods. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index e0491cbc4e..70098c952e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -30,6 +30,7 @@ import org.mapstruct.Qualifier; import org.mapstruct.SubclassMapping; import org.mapstruct.SubclassMappings; +import org.mapstruct.TargetPropertyName; import org.mapstruct.TargetType; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; @@ -52,6 +53,7 @@ @GemDefinition(SubclassMapping.class) @GemDefinition(SubclassMappings.class) @GemDefinition(TargetType.class) +@GemDefinition(TargetPropertyName.class) @GemDefinition(MappingTarget.class) @GemDefinition(DecoratedWith.class) @GemDefinition(MapperConfig.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java index dc62018be5..600c8ac337 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java @@ -15,6 +15,7 @@ import org.mapstruct.ap.internal.gem.ContextGem; import org.mapstruct.ap.internal.gem.MappingTargetGem; import org.mapstruct.ap.internal.gem.TargetTypeGem; +import org.mapstruct.ap.internal.gem.TargetPropertyNameGem; import org.mapstruct.ap.internal.util.Collections; /** @@ -31,6 +32,7 @@ public class Parameter extends ModelElement { private final boolean mappingTarget; private final boolean targetType; private final boolean mappingContext; + private final boolean targetPropertyName; private final boolean varArgs; @@ -42,10 +44,12 @@ private Parameter(Element element, Type type, boolean varArgs) { this.mappingTarget = MappingTargetGem.instanceOn( element ) != null; this.targetType = TargetTypeGem.instanceOn( element ) != null; this.mappingContext = ContextGem.instanceOn( element ) != null; + this.targetPropertyName = TargetPropertyNameGem.instanceOn( element ) != null; this.varArgs = varArgs; } private Parameter(String name, Type type, boolean mappingTarget, boolean targetType, boolean mappingContext, + boolean targetPropertyName, boolean varArgs) { this.element = null; this.name = name; @@ -54,11 +58,12 @@ private Parameter(String name, Type type, boolean mappingTarget, boolean targetT this.mappingTarget = mappingTarget; this.targetType = targetType; this.mappingContext = mappingContext; + this.targetPropertyName = targetPropertyName; this.varArgs = varArgs; } public Parameter(String name, Type type) { - this( name, type, false, false, false, false ); + this( name, type, false, false, false, false, false ); } public Element getElement() { @@ -94,6 +99,7 @@ private String format() { return ( mappingTarget ? "@MappingTarget " : "" ) + ( targetType ? "@TargetType " : "" ) + ( mappingContext ? "@Context " : "" ) + + ( targetPropertyName ? "@TargetPropertyName " : "" ) + "%s " + name; } @@ -110,6 +116,10 @@ public boolean isMappingContext() { return mappingContext; } + public boolean isTargetPropertyName() { + return targetPropertyName; + } + public boolean isVarArgs() { return varArgs; } @@ -154,6 +164,7 @@ public static Parameter forForgedMappingTarget(Type parameterType) { true, false, false, + false, false ); } @@ -195,8 +206,15 @@ public static Parameter getTargetTypeParameter(List parameters) { return parameters.stream().filter( Parameter::isTargetType ).findAny().orElse( null ); } + public static Parameter getTargetPropertyNameParameter(List parameters) { + return parameters.stream().filter( Parameter::isTargetPropertyName ).findAny().orElse( null ); + } + private static boolean isSourceParameter( Parameter parameter ) { - return !parameter.isMappingTarget() && !parameter.isTargetType() && !parameter.isMappingContext(); + return !parameter.isMappingTarget() && + !parameter.isTargetType() && + !parameter.isMappingContext() && + !parameter.isTargetPropertyName(); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java index 36e4d3e864..18274e5492 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java @@ -22,15 +22,17 @@ public class ParameterBinding { private final boolean targetType; private final boolean mappingTarget; private final boolean mappingContext; + private final boolean targetPropertyName; private final SourceRHS sourceRHS; private ParameterBinding(Type parameterType, String variableName, boolean mappingTarget, boolean targetType, - boolean mappingContext, SourceRHS sourceRHS) { + boolean mappingContext, boolean targetPropertyName, SourceRHS sourceRHS) { this.type = parameterType; this.variableName = variableName; this.targetType = targetType; this.mappingTarget = mappingTarget; this.mappingContext = mappingContext; + this.targetPropertyName = targetPropertyName; this.sourceRHS = sourceRHS; } @@ -62,6 +64,13 @@ public boolean isMappingContext() { return mappingContext; } + /** + * @return {@code true}, if the parameter being bound is a {@code @TargetPropertyName} parameter. + */ + public boolean isTargetPropertyName() { + return targetPropertyName; + } + /** * @return the type of the parameter that is bound */ @@ -99,6 +108,7 @@ public static ParameterBinding fromParameter(Parameter parameter) { parameter.isMappingTarget(), parameter.isTargetType(), parameter.isMappingContext(), + parameter.isTargetPropertyName(), null ); } @@ -118,6 +128,7 @@ public static ParameterBinding fromTypeAndName(Type parameterType, String parame false, false, false, + false, null ); } @@ -127,7 +138,14 @@ public static ParameterBinding fromTypeAndName(Type parameterType, String parame * @return a parameter binding representing a target type parameter */ public static ParameterBinding forTargetTypeBinding(Type classTypeOf) { - return new ParameterBinding( classTypeOf, null, false, true, false, null ); + return new ParameterBinding( classTypeOf, null, false, true, false, false, null ); + } + + /** + * @return a parameter binding representing a target property name parameter + */ + public static ParameterBinding forTargetPropertyNameBinding(Type classTypeOf) { + return new ParameterBinding( classTypeOf, null, false, false, false, true, null ); } /** @@ -135,7 +153,7 @@ public static ParameterBinding forTargetTypeBinding(Type classTypeOf) { * @return a parameter binding representing a mapping target parameter */ public static ParameterBinding forMappingTargetBinding(Type resultType) { - return new ParameterBinding( resultType, null, true, false, false, null ); + return new ParameterBinding( resultType, null, true, false, false, false, null ); } /** @@ -143,10 +161,10 @@ public static ParameterBinding forMappingTargetBinding(Type resultType) { * @return a parameter binding representing a mapping source type */ public static ParameterBinding forSourceTypeBinding(Type sourceType) { - return new ParameterBinding( sourceType, null, false, false, false, null ); + return new ParameterBinding( sourceType, null, false, false, false, false, null ); } public static ParameterBinding fromSourceRHS(SourceRHS sourceRHS) { - return new ParameterBinding( sourceRHS.getSourceType(), null, false, false, false, sourceRHS ); + return new ParameterBinding( sourceRHS.getSourceType(), null, false, false, false, false, sourceRHS ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 92180bca8a..37b57d9025 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -47,6 +47,7 @@ public class SourceMethod implements Method { private final List parameters; private final Parameter mappingTargetParameter; private final Parameter targetTypeParameter; + private final Parameter targetPropertyNameParameter; private final boolean isObjectFactory; private final boolean isPresenceCheck; private final Type returnType; @@ -248,6 +249,7 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters ); this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters ); + this.targetPropertyNameParameter = Parameter.getTargetPropertyNameParameter( parameters ); this.hasObjectFactoryAnnotation = ObjectFactoryGem.instanceOn( executable ) != null; this.isObjectFactory = determineIfIsObjectFactory(); this.isPresenceCheck = determineIfIsPresenceCheck(); @@ -263,8 +265,9 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) private boolean determineIfIsObjectFactory() { boolean hasNoSourceParameters = getSourceParameters().isEmpty(); boolean hasNoMappingTargetParam = getMappingTargetParameter() == null; + boolean hasNoTargetPropertyNameParam = getTargetPropertyNameParameter() == null; return !isLifecycleCallbackMethod() && !returnType.isVoid() - && hasNoMappingTargetParam + && hasNoMappingTargetParam && hasNoTargetPropertyNameParam && ( hasObjectFactoryAnnotation || hasNoSourceParameters ); } @@ -379,6 +382,10 @@ public Parameter getTargetTypeParameter() { return targetTypeParameter; } + public Parameter getTargetPropertyNameParameter() { + return targetPropertyNameParameter; + } + public boolean isIterableMapping() { if ( isIterableMapping == null ) { isIterableMapping = getSourceParameters().size() == 1 diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java index 74e518c84d..8acdd327c4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java @@ -96,7 +96,7 @@ private List getAvailableParameterBindingsFromMethod(Method me availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); } - addMappingTargetAndTargetTypeBindings( availableParams, targetType ); + addTargetRelevantBindings( availableParams, targetType ); return availableParams; } @@ -116,7 +116,7 @@ private List getAvailableParameterBindingsFromSourceTypes(List } } - addMappingTargetAndTargetTypeBindings( availableParams, targetType ); + addTargetRelevantBindings( availableParams, targetType ); return availableParams; } @@ -127,9 +127,10 @@ private List getAvailableParameterBindingsFromSourceTypes(List * @param availableParams Already available params, new entries will be added to this list * @param targetType Target type */ - private void addMappingTargetAndTargetTypeBindings(List availableParams, Type targetType) { + private void addTargetRelevantBindings(List availableParams, Type targetType) { boolean mappingTargetAvailable = false; boolean targetTypeAvailable = false; + boolean targetPropertyNameAvailable = false; // search available parameter bindings if mapping-target and/or target-type is available for ( ParameterBinding pb : availableParams ) { @@ -139,6 +140,9 @@ private void addMappingTargetAndTargetTypeBindings(List availa else if ( pb.isTargetType() ) { targetTypeAvailable = true; } + else if ( pb.isTargetPropertyName() ) { + targetPropertyNameAvailable = true; + } } if ( !mappingTargetAvailable ) { @@ -147,6 +151,9 @@ else if ( pb.isTargetType() ) { if ( !targetTypeAvailable ) { availableParams.add( ParameterBinding.forTargetTypeBinding( typeFactory.classTypeOf( targetType ) ) ); } + if ( !targetPropertyNameAvailable ) { + availableParams.add( ParameterBinding.forTargetPropertyNameBinding( typeFactory.getType( String.class ) ) ); + } } private SelectedMethod getMatchingParameterBinding(Type returnType, @@ -301,7 +308,8 @@ private static List findCandidateBindingsForParameter(List${ext.targetBeanName}<#else>${param.variableName}<#if ext.targetReadAccessorName??>.${ext.targetReadAccessorName}<#t> <#elseif param.mappingContext> ${param.variableName}<#t> + <#elseif param.targetPropertyName> + "${ext.targetPropertyName}"<#t> <#elseif param.sourceRHS??> <@_assignment assignmentToUse=param.sourceRHS/><#t> <#elseif assignment??> @@ -66,5 +68,6 @@ existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName targetWriteAccessorName=ext.targetWriteAccessorName + targetPropertyName=ext.targetPropertyName targetType=singleSourceParameterType/> \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl index 9a2837a02d..24f871e214 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl @@ -7,4 +7,5 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MethodReferencePresenceCheck" --> <@includeModel object=methodReference + targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl index 20e6f1734c..e6aef4cecd 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl @@ -11,5 +11,6 @@ existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=targetReadAccessorName targetWriteAccessorName=targetWriteAccessorName + targetPropertyName=name targetType=targetType defaultValueAssignment=defaultValueAssignment /> \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl index 370fe7c978..4a9d356ce4 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl @@ -14,6 +14,7 @@ ${openExpression}<@_assignment/>${closeExpression} existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName targetWriteAccessorName=ext.targetWriteAccessorName + targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.ftl index 2795dac7bc..ce1fcdcd6c 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.ftl @@ -34,5 +34,6 @@ existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName targetWriteAccessorName=ext.targetWriteAccessorName + targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.ftl index 53e987e476..97ea856ece 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.ftl @@ -25,5 +25,6 @@ existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName targetWriteAccessorName=ext.targetWriteAccessorName + targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.ftl index 9f46f3604f..9917dbacf8 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ReturnWrapper.ftl @@ -13,5 +13,6 @@ return <@_assignment/>; existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName targetWriteAccessorName=ext.targetWriteAccessorName + targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index 6d05ddd131..e5fb970605 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -16,6 +16,7 @@ <#macro handleSourceReferenceNullCheck> <#if sourcePresenceCheckerReference??> if ( <@includeModel object=sourcePresenceCheckerReference + targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> ) { <#nested> } @@ -58,7 +59,8 @@ --> <#macro handleLocalVarNullCheck needs_explicit_local_var> <#if sourcePresenceCheckerReference??> - if ( <@includeModel object=sourcePresenceCheckerReference /> ) { + if ( <@includeModel object=sourcePresenceCheckerReference + targetPropertyName=ext.targetPropertyName/> ) { <#if needs_explicit_local_var> <@includeModel object=nullCheckLocalVarType/> ${nullCheckLocalVarName} = <@lib.handleAssignment/>; <#nested> @@ -113,6 +115,7 @@ Performs a standard assignment. existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName targetWriteAccessorName=ext.targetWriteAccessorName + targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> <#-- @@ -124,6 +127,7 @@ Performs a default assignment with a default value. existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName targetWriteAccessorName=ext.targetWriteAccessorName + targetPropertyName=ext.targetPropertyName targetType=ext.targetType defaultValue=ext.defaultValue/> diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java new file mode 100644 index 0000000000..162ed118bb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java @@ -0,0 +1,21 @@ +/* + * 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.targetpropertyname; + +/** + * @author Nikola Ivačič + */ +public class Address implements DomainModel { + private String street; + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java new file mode 100644 index 0000000000..f4cbc71915 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java @@ -0,0 +1,21 @@ +/* + * 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.targetpropertyname; + +/** + * @author Nikola Ivačič + */ +public class AddressDto implements DomainModel { + private final String street; + + public AddressDto(String street) { + this.street = street; + } + + public String getStreet() { + return street; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java new file mode 100644 index 0000000000..71baa0942b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java @@ -0,0 +1,41 @@ +/* + * 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.targetpropertyname; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.TargetPropertyName; +import org.mapstruct.factory.Mappers; + +import java.util.Collection; + +/** + * @author Nikola Ivačič + */ +@Mapper +public interface ConditionalMethodForCollectionMapperWithTargetPropertyName { + + ConditionalMethodForCollectionMapperWithTargetPropertyName INSTANCE + = Mappers.getMapper( ConditionalMethodForCollectionMapperWithTargetPropertyName.class ); + + Employee map(EmployeeDto employee); + + @Condition + default boolean isNotEmpty(Collection collection, @TargetPropertyName String propName) { + if ( "addresses".equalsIgnoreCase( propName ) ) { + return false; + } + return collection != null && !collection.isEmpty(); + } + + @Condition + default boolean isNotBlank(String value, @TargetPropertyName String propName) { + if ( propName.equalsIgnoreCase( "lastName" ) ) { + return false; + } + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java new file mode 100644 index 0000000000..430303d06d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java @@ -0,0 +1,46 @@ +/* + * 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.targetpropertyname; + +import org.mapstruct.Condition; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.TargetPropertyName; +import org.mapstruct.factory.Mappers; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + */ +@Mapper +public interface ConditionalMethodInMapperWithAllExceptTarget { + + ConditionalMethodInMapperWithAllExceptTarget INSTANCE + = Mappers.getMapper( ConditionalMethodInMapperWithAllExceptTarget.class ); + + class PresenceUtils { + Set visited = new LinkedHashSet<>(); + Set visitedSources = new LinkedHashSet<>(); + } + + Employee map(EmployeeDto employee, @Context PresenceUtils utils); + + @Condition + default boolean isNotBlank(String value, + DomainModel source, + @TargetPropertyName String propName, + @Context PresenceUtils utils) { + utils.visited.add( propName ); + utils.visitedSources.add( source.getClass().getSimpleName() ); + if ( propName.equalsIgnoreCase( "firstName" ) ) { + return true; + } + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java new file mode 100644 index 0000000000..a18ba0db73 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java @@ -0,0 +1,52 @@ +/* + * 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.targetpropertyname; + +import org.mapstruct.Condition; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.TargetPropertyName; +import org.mapstruct.factory.Mappers; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + */ +@Mapper +public interface ConditionalMethodInMapperWithAllOptions { + + ConditionalMethodInMapperWithAllOptions INSTANCE + = Mappers.getMapper( ConditionalMethodInMapperWithAllOptions.class ); + + class PresenceUtils { + Set visited = new LinkedHashSet<>(); + Set visitedSources = new LinkedHashSet<>(); + Set visitedTargets = new LinkedHashSet<>(); + } + + void map(EmployeeDto employeeDto, + @MappingTarget Employee employee, + @Context PresenceUtils utils); + + @Condition + default boolean isNotBlank(String value, + DomainModel source, + @MappingTarget DomainModel target, + @TargetPropertyName String propName, + @Context PresenceUtils utils) { + utils.visited.add( propName ); + utils.visitedSources.add( source.getClass().getSimpleName() ); + utils.visitedTargets.add( target.getClass().getSimpleName() ); + if ( propName.equalsIgnoreCase( "lastName" ) ) { + return false; + } + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java new file mode 100644 index 0000000000..6d27bc9036 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java @@ -0,0 +1,32 @@ +/* + * 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.targetpropertyname; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.TargetPropertyName; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + */ +@Mapper +public interface ConditionalMethodInMapperWithTargetPropertyName { + + ConditionalMethodInMapperWithTargetPropertyName INSTANCE + = Mappers.getMapper( ConditionalMethodInMapperWithTargetPropertyName.class ); + + Employee map(EmployeeDto employee); + + @Condition + default boolean isNotBlank(String value, @TargetPropertyName String propName) { + if ( propName.equalsIgnoreCase( "lastName" ) ) { + return false; + } + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java new file mode 100644 index 0000000000..7c526884e9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java @@ -0,0 +1,36 @@ +/* + * 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.targetpropertyname; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.TargetPropertyName; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + */ +@Mapper(uses = ConditionalMethodInUsesMapperWithTargetPropertyName.PresenceUtils.class) +public interface ConditionalMethodInUsesMapperWithTargetPropertyName { + + ConditionalMethodInUsesMapperWithTargetPropertyName INSTANCE + = Mappers.getMapper( ConditionalMethodInUsesMapperWithTargetPropertyName.class ); + + Employee map(EmployeeDto employee); + + class PresenceUtils { + + @Condition + public boolean isNotBlank(String value, @TargetPropertyName String propName) { + if ( propName.equalsIgnoreCase( "lastName" ) ) { + return false; + } + return value != null && !value.trim().isEmpty(); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java new file mode 100644 index 0000000000..a7cde220c9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java @@ -0,0 +1,89 @@ +/* + * 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.targetpropertyname; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.Condition; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.TargetPropertyName; +import org.mapstruct.factory.Mappers; + +import java.util.Deque; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.Set; + +/** + * @author Nikola Ivačič + */ +@Mapper +public interface ConditionalMethodWithTargetPropertyNameInContextMapper { + + ConditionalMethodWithTargetPropertyNameInContextMapper INSTANCE + = Mappers.getMapper( ConditionalMethodWithTargetPropertyNameInContextMapper.class ); + + Employee map(EmployeeDto employee, @Context PresenceUtils utils); + + Address map(AddressDto addressDto, @Context PresenceUtils utils); + + class PresenceUtils { + Set visited = new LinkedHashSet<>(); + + @Condition + public boolean isNotBlank(String value, @TargetPropertyName String propName) { + visited.add( propName ); + return value != null && !value.trim().isEmpty(); + } + } + + Employee map(EmployeeDto employee, @Context PresenceUtilsAllProps utils); + + Address map(AddressDto addressDto, @Context PresenceUtilsAllProps utils); + + class PresenceUtilsAllProps { + Set visited = new LinkedHashSet<>(); + + @Condition + public boolean collect(@TargetPropertyName String propName) { + visited.add( propName ); + return true; + } + } + + Employee map(EmployeeDto employee, @Context PresenceUtilsAllPropsWithSource utils); + + Address map(AddressDto addressDto, @Context PresenceUtilsAllPropsWithSource utils); + + @BeforeMapping + default void before(DomainModel source, @Context PresenceUtilsAllPropsWithSource utils) { + String lastProp = utils.visitedSegments.peekLast(); + if ( lastProp != null && source != null ) { + utils.path.offerLast( lastProp ); + } + } + + @AfterMapping + default void after(@Context PresenceUtilsAllPropsWithSource utils) { + utils.path.pollLast(); + } + + class PresenceUtilsAllPropsWithSource { + Deque visitedSegments = new LinkedList<>(); + Deque visited = new LinkedList<>(); + Deque path = new LinkedList<>(); + + @Condition + public boolean collect(@TargetPropertyName String propName) { + visitedSegments.offerLast( propName ); + path.offerLast( propName ); + visited.offerLast( String.join( ".", path ) ); + path.pollLast(); + return true; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java new file mode 100644 index 0000000000..4d2c716a96 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java @@ -0,0 +1,15 @@ +/* + * 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.targetpropertyname; + +/** + * Target Property Name test entities + * + * @author Nikola Ivačič + */ +public interface DomainModel { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java new file mode 100644 index 0000000000..59ee3426e1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java @@ -0,0 +1,99 @@ +/* + * 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.targetpropertyname; + +import java.util.List; + +/** + * @author Nikola Ivačič + */ +public class Employee implements DomainModel { + + private String firstName; + private String lastName; + private String title; + private String country; + private boolean active; + private int age; + + private Employee boss; + + private Address primaryAddress; + + private List
    addresses; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Employee getBoss() { + return boss; + } + + public void setBoss(Employee boss) { + this.boss = boss; + } + + public Address getPrimaryAddress() { + return primaryAddress; + } + + public void setPrimaryAddress(Address primaryAddress) { + this.primaryAddress = primaryAddress; + } + + public List
    getAddresses() { + return addresses; + } + + public void setAddresses(List
    addresses) { + this.addresses = addresses; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java new file mode 100644 index 0000000000..5d81a9334b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java @@ -0,0 +1,98 @@ +/* + * 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.targetpropertyname; + +import java.util.List; + +/** + * @author Nikola Ivačič + */ +public class EmployeeDto implements DomainModel { + + private String firstName; + private String lastName; + private String title; + private String country; + private boolean active; + private int age; + + private EmployeeDto boss; + + private AddressDto primaryAddress; + private List addresses; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public EmployeeDto getBoss() { + return boss; + } + + public void setBoss(EmployeeDto boss) { + this.boss = boss; + } + + public AddressDto getPrimaryAddress() { + return primaryAddress; + } + + public void setPrimaryAddress(AddressDto primaryAddress) { + this.primaryAddress = primaryAddress; + } + + public List getAddresses() { + return addresses; + } + + public void setAddresses(List addresses) { + this.addresses = addresses; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java new file mode 100644 index 0000000000..3d96e66664 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java @@ -0,0 +1,281 @@ +/* + * 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.targetpropertyname; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + */ +@IssueKey("2051") +@WithClasses({ + Address.class, + AddressDto.class, + Employee.class, + EmployeeDto.class, + DomainModel.class +}) +public class TargetPropertyNameTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses({ + ConditionalMethodInMapperWithTargetPropertyName.class + }) + public void conditionalMethodInMapperWithTargetPropertyName() { + ConditionalMethodInMapperWithTargetPropertyName mapper + = ConditionalMethodInMapperWithTargetPropertyName.INSTANCE; + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setCountry( "US" ); + employeeDto.setAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto ); + assertThat( employee.getLastName() ).isNull(); + assertThat( employee.getFirstName() ).isNull(); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodForCollectionMapperWithTargetPropertyName.class + }) + public void conditionalMethodForCollectionMapperWithTargetPropertyName() { + ConditionalMethodForCollectionMapperWithTargetPropertyName mapper + = ConditionalMethodForCollectionMapperWithTargetPropertyName.INSTANCE; + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setCountry( "US" ); + employeeDto.setAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto ); + assertThat( employee.getLastName() ).isNull(); + assertThat( employee.getFirstName() ).isNull(); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ).isNull(); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodInUsesMapperWithTargetPropertyName.class + }) + public void conditionalMethodInUsesMapperWithTargetPropertyName() { + ConditionalMethodInUsesMapperWithTargetPropertyName mapper + = ConditionalMethodInUsesMapperWithTargetPropertyName.INSTANCE; + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setCountry( "US" ); + employeeDto.setAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto ); + assertThat( employee.getLastName() ).isNull(); + assertThat( employee.getFirstName() ).isNull(); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodInMapperWithAllOptions.class + }) + public void conditionalMethodInMapperWithAllOptions() { + ConditionalMethodInMapperWithAllOptions mapper + = ConditionalMethodInMapperWithAllOptions.INSTANCE; + + ConditionalMethodInMapperWithAllOptions.PresenceUtils utils = + new ConditionalMethodInMapperWithAllOptions.PresenceUtils(); + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setCountry( "US" ); + employeeDto.setAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = new Employee(); + mapper.map( employeeDto, employee, utils ); + assertThat( employee.getLastName() ).isNull(); + assertThat( employee.getFirstName() ).isNull(); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + assertThat( utils.visited ) + .containsExactlyInAnyOrder( "firstName", "lastName", "title", "country" ); + assertThat( utils.visitedSources ).containsExactly( "EmployeeDto" ); + assertThat( utils.visitedTargets ).containsExactly( "Employee" ); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodInMapperWithAllExceptTarget.class + }) + public void conditionalMethodInMapperWithAllExceptTarget() { + ConditionalMethodInMapperWithAllExceptTarget mapper + = ConditionalMethodInMapperWithAllExceptTarget.INSTANCE; + + ConditionalMethodInMapperWithAllExceptTarget.PresenceUtils utils = + new ConditionalMethodInMapperWithAllExceptTarget.PresenceUtils(); + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setCountry( "US" ); + employeeDto.setAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto, utils ); + assertThat( employee.getLastName() ).isEqualTo( "Testirovich" ); + assertThat( employee.getFirstName() ).isEqualTo( " " ); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + assertThat( utils.visited ) + .containsExactlyInAnyOrder( "firstName", "lastName", "title", "country", "street" ); + assertThat( utils.visitedSources ).containsExactlyInAnyOrder( "EmployeeDto", "AddressDto" ); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodWithTargetPropertyNameInContextMapper.class + }) + public void conditionalMethodWithTargetPropertyNameInUsesContextMapper() { + ConditionalMethodWithTargetPropertyNameInContextMapper mapper + = ConditionalMethodWithTargetPropertyNameInContextMapper.INSTANCE; + + ConditionalMethodWithTargetPropertyNameInContextMapper.PresenceUtils utils = + new ConditionalMethodWithTargetPropertyNameInContextMapper.PresenceUtils(); + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setLastName( " " ); + employeeDto.setCountry( "US" ); + employeeDto.setAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto, utils ); + assertThat( employee.getLastName() ).isNull(); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + assertThat( utils.visited ) + .containsExactlyInAnyOrder( "firstName", "lastName", "title", "country", "street" ); + + ConditionalMethodWithTargetPropertyNameInContextMapper.PresenceUtilsAllProps allPropsUtils = + new ConditionalMethodWithTargetPropertyNameInContextMapper.PresenceUtilsAllProps(); + + employeeDto = new EmployeeDto(); + employeeDto.setLastName( "Tester" ); + employeeDto.setCountry( "US" ); + employeeDto.setAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + employee = mapper.map( employeeDto, allPropsUtils ); + assertThat( employee.getLastName() ).isEqualTo( "Tester" ); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + assertThat( allPropsUtils.visited ) + .containsExactlyInAnyOrder( + "firstName", + "lastName", + "title", + "country", + "active", + "age", + "boss", + "primaryAddress", + "addresses", + "street" + ); + + ConditionalMethodWithTargetPropertyNameInContextMapper.PresenceUtilsAllPropsWithSource allPropsUtilsWithSource = + new ConditionalMethodWithTargetPropertyNameInContextMapper.PresenceUtilsAllPropsWithSource(); + + EmployeeDto bossEmployeeDto = new EmployeeDto(); + bossEmployeeDto.setLastName( "Boss Tester" ); + bossEmployeeDto.setCountry( "US" ); + bossEmployeeDto.setAddresses( Collections.singletonList( new AddressDto( + "Testing St. 10" ) ) ); + + employeeDto = new EmployeeDto(); + employeeDto.setLastName( "Tester" ); + employeeDto.setCountry( "US" ); + employeeDto.setBoss( bossEmployeeDto ); + employeeDto.setAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + employee = mapper.map( employeeDto, allPropsUtilsWithSource ); + assertThat( employee.getLastName() ).isEqualTo( "Tester" ); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ).isNotEmpty(); + assertThat( employee.getAddresses().get( 0 ).getStreet() ).isEqualTo( "Testing St. 6" ); + assertThat( employee.getBoss() ).isNotNull(); + assertThat( employee.getBoss().getCountry() ).isEqualTo( "US" ); + assertThat( employee.getBoss().getLastName() ).isEqualTo( "Boss Tester" ); + assertThat( employee.getBoss().getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 10" ); + assertThat( allPropsUtilsWithSource.visited ) + .containsExactly( + "firstName", + "lastName", + "title", + "country", + "active", + "age", + "boss", + "boss.firstName", + "boss.lastName", + "boss.title", + "boss.country", + "boss.active", + "boss.age", + "boss.boss", + "boss.primaryAddress", + "boss.addresses", + "boss.addresses.street", + "primaryAddress", + "addresses", + "addresses.street" + ); + } +} From 849085e02647d5d0cc63c9c2da98c8e959f4e6e2 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sat, 20 Aug 2022 12:59:38 +0200 Subject: [PATCH 285/597] #1574: Support for annotating the generated code with custom annotations Add new `@AnnotateWith` annotation. This annotation can be used to instruct the MapStruct processor to generate custom annotations in the generated code. --- .../main/java/org/mapstruct/AnnotateWith.java | 176 +++++ .../java/org/mapstruct/AnnotateWiths.java | 31 + .../src/main/java/org/mapstruct/NullEnum.java | 15 + .../chapter-3-defining-a-mapper.asciidoc | 40 ++ .../ap/internal/gem/GemGenerator.java | 5 + .../model/AdditionalAnnotationsBuilder.java | 602 ++++++++++++++++++ .../ap/internal/model/Annotation.java | 18 +- .../ap/internal/model/BeanMappingMethod.java | 15 + .../ap/internal/model/GeneratedType.java | 7 +- .../mapstruct/ap/internal/model/Mapper.java | 11 +- .../model/annotation/AnnotationElement.java | 128 ++++ .../EnumAnnotationElementHolder.java | 35 + .../processor/JakartaComponentProcessor.java | 7 +- .../processor/Jsr330ComponentProcessor.java | 8 +- .../processor/MapperCreationProcessor.java | 6 + .../processor/MethodRetrievalProcessor.java | 198 ++---- .../processor/SpringComponentProcessor.java | 8 +- .../mapstruct/ap/internal/util/Message.java | 14 + .../internal/util/RepeatableAnnotations.java | 120 ++++ .../ap/internal/model/Annotation.ftl | 2 +- .../ap/internal/model/BeanMappingMethod.ftl | 3 + .../model/annotation/AnnotationElement.ftl | 48 ++ .../EnumAnnotationElementHolder.ftl | 9 + .../test/annotatewith/AnnotateWithEnum.java | 10 + .../test/annotatewith/AnnotateWithTest.java | 548 ++++++++++++++++ .../AnnotationWithRequiredParameter.java | 19 + .../AnnotationWithoutElementNameMapper.java | 15 + .../annotatewith/ClassMetaAnnotation.java | 20 + .../test/annotatewith/CustomAnnotation.java | 19 + .../CustomAnnotationOnlyAnnotation.java | 18 + .../CustomAnnotationWithParams.java | 64 ++ .../CustomAnnotationWithParamsContainer.java | 19 + ...omAnnotationWithTwoAnnotationElements.java | 20 + .../CustomClassOnlyAnnotation.java | 18 + .../CustomMethodOnlyAnnotation.java | 18 + .../CustomNamedGenericClassMapper.java | 22 + .../test/annotatewith/CustomNamedMapper.java | 40 ++ .../CustomRepeatableAnnotation.java | 21 + .../CustomRepeatableAnnotationContainer.java | 19 + .../DeprecateAndCustomMapper.java | 19 + ...oneousMapperWithAnnotationOnlyOnClass.java | 18 + ...usMapperWithAnnotationOnlyOnInterface.java | 18 + .../ErroneousMapperWithClassOnMethod.java | 27 + .../ErroneousMapperWithMethodOnClass.java | 18 + .../ErroneousMapperWithMethodOnInterface.java | 18 + .../ErroneousMapperWithMissingEnumClass.java | 22 + .../ErroneousMapperWithMissingEnums.java | 21 + .../ErroneousMapperWithMissingParameter.java | 18 + .../ErroneousMapperWithNonExistantEnum.java | 22 + .../ErroneousMapperWithParameterRepeat.java | 22 + ...erWithRepeatOfNotRepeatableAnnotation.java | 19 + ...neousMapperWithTooManyParameterValues.java | 21 + .../ErroneousMapperWithUnknownParameter.java | 21 + .../ErroneousMapperWithWrongParameter.java | 45 ++ .../ErroneousMultipleArrayValuesMapper.java | 31 + ...MapperWithIdenticalAnnotationRepeated.java | 19 + ...apperWithMissingAnnotationElementName.java | 21 + .../MapperWithRepeatableAnnotation.java | 19 + .../annotatewith/MetaAnnotatedMapper.java | 14 + .../annotatewith/MethodMetaAnnotation.java | 20 + .../MultipleArrayValuesMapper.java | 31 + .../annotatewith/WrongAnnotateWithEnum.java | 10 + .../annotatewith/CustomNamedMapperImpl.java | 17 + .../MultipleArrayValuesMapperImpl.java | 18 + 64 files changed, 2774 insertions(+), 151 deletions(-) create mode 100644 core/src/main/java/org/mapstruct/AnnotateWith.java create mode 100644 core/src/main/java/org/mapstruct/AnnotateWiths.java create mode 100644 core/src/main/java/org/mapstruct/NullEnum.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/annotation/AnnotationElement.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/annotation/EnumAnnotationElementHolder.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/RepeatableAnnotations.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/annotation/AnnotationElement.ftl create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/annotation/EnumAnnotationElementHolder.ftl create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithEnum.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotationWithRequiredParameter.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotationWithoutElementNameMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ClassMetaAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationOnlyAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithParams.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithParamsContainer.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithTwoAnnotationElements.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomClassOnlyAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomMethodOnlyAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedGenericClassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomRepeatableAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomRepeatableAnnotationContainer.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/DeprecateAndCustomMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithAnnotationOnlyOnClass.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithAnnotationOnlyOnInterface.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithClassOnMethod.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMethodOnClass.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMethodOnInterface.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingEnumClass.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingEnums.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingParameter.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithNonExistantEnum.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithParameterRepeat.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithRepeatOfNotRepeatableAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithTooManyParameterValues.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithUnknownParameter.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithWrongParameter.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMultipleArrayValuesMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithIdenticalAnnotationRepeated.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithMissingAnnotationElementName.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithRepeatableAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/MetaAnnotatedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/MethodMetaAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/WrongAnnotateWithEnum.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/CustomNamedMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapperImpl.java diff --git a/core/src/main/java/org/mapstruct/AnnotateWith.java b/core/src/main/java/org/mapstruct/AnnotateWith.java new file mode 100644 index 0000000000..090a7bc0d8 --- /dev/null +++ b/core/src/main/java/org/mapstruct/AnnotateWith.java @@ -0,0 +1,176 @@ +/* + * 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.Annotation; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.CLASS; + +/** + * This can be used to have mapstruct generate additional annotations on classes/methods. + *

    + * Examples based on the spring framework annotations. + *

    + * Marking a class as `Lazy`: + * + *
    
    + * @AnnotateWith( value = Lazy.class )
    + * @Mapper
    + * public interface FooMapper {
    + *     // mapper code
    + * }
    + * 
    + * + * The following code would be generated: + * + *
    
    + * @Lazy
    + * public class FooMapperImpl implements FooMapper {
    + *     // mapper code
    + * }
    + * 
    + * Setting the profile on the generated implementation: + * + *
    
    + * @AnnotateWith( value = Profile.class, elements = @AnnotateWith.Element( strings = "prod" ) )
    + * @Mapper
    + * public interface FooMapper {
    + *     // mapper code
    + * }
    + * 
    + * + * The following code would be generated: + * + *
    
    + * @Profile( value = "prod" )
    + * public class FooMapperImpl implements FooMapper {
    + *     // mapper code
    + * }
    + * 
    + * + * @author Ben Zegveld + * @since 1.6 + */ +@Repeatable( AnnotateWiths.class ) +@Retention( CLASS ) +@Target( { TYPE, METHOD, ANNOTATION_TYPE } ) +public @interface AnnotateWith { + /** + * @return the annotation class that needs to be added. + */ + Class value(); + + /** + * @return the annotation elements that are to be applied to this annotation. + */ + Element[] elements() default {}; + + /** + * Used in combination with {@link AnnotateWith} to configure the annotation elements. Only 1 value type may be used + * within the same annotation at a time. For example mixing shorts and ints is not allowed. + * + * @author Ben Zegveld + * @since 1.6 + */ + @interface Element { + /** + * @return name of the annotation element. + */ + String name() default "value"; + + /** + * cannot be used in conjunction with other value fields. + * + * @return short value(s) for the annotation element. + */ + short[] shorts() default {}; + + /** + * cannot be used in conjunction with other value fields. + * + * @return byte value(s) for the annotation element. + */ + byte[] bytes() default {}; + + /** + * cannot be used in conjunction with other value fields. + * + * @return int value(s) for the annotation element. + */ + int[] ints() default {}; + + /** + * cannot be used in conjunction with other value fields. + * + * @return long value(s) for the annotation element. + */ + long[] longs() default {}; + + /** + * cannot be used in conjunction with other value fields. + * + * @return float value(s) for the annotation element. + */ + float[] floats() default {}; + + /** + * cannot be used in conjunction with other value fields. + * + * @return double value(s) for the annotation element. + */ + double[] doubles() default {}; + + /** + * cannot be used in conjunction with other value fields. + * + * @return char value(s) for the annotation element. + */ + char[] chars() default {}; + + /** + * cannot be used in conjunction with other value fields. + * + * @return boolean value(s) for the annotation element. + */ + boolean[] booleans() default {}; + + /** + * cannot be used in conjunction with other value fields. + * + * @return string value(s) for the annotation element. + */ + String[] strings() default {}; + + /** + * cannot be used in conjunction with other value fields. + * + * @return class value(s) for the annotation element. + */ + Class[] classes() default {}; + + /** + * only used in conjunction with the {@link #enums()} annotation element. + * + * @return the class of the enum. + */ + Class> enumClass() default NullEnum.class; + + /** + * cannot be used in conjunction with other value fields. {@link #enumClass()} is also required when using + * {@link #enums()} + * + * @return enum value(s) for the annotation element. + */ + String[] enums() default {}; + + } +} diff --git a/core/src/main/java/org/mapstruct/AnnotateWiths.java b/core/src/main/java/org/mapstruct/AnnotateWiths.java new file mode 100644 index 0000000000..5e86ad9a19 --- /dev/null +++ b/core/src/main/java/org/mapstruct/AnnotateWiths.java @@ -0,0 +1,31 @@ +/* + * 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.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.CLASS; + +/** + * This can be used to have mapstruct generate additional annotations on classes/methods. + * + * @author Ben Zegveld + * @since 1.6 + */ +@Retention( CLASS ) +@Target( { TYPE, METHOD } ) +public @interface AnnotateWiths { + + /** + * The configuration of the additional annotations. + * + * @return The configuration of the additional annotations. + */ + AnnotateWith[] value(); +} diff --git a/core/src/main/java/org/mapstruct/NullEnum.java b/core/src/main/java/org/mapstruct/NullEnum.java new file mode 100644 index 0000000000..ac39b3485d --- /dev/null +++ b/core/src/main/java/org/mapstruct/NullEnum.java @@ -0,0 +1,15 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct; + +/** + * To be used as a default value for enum class annotation elements. + * + * @author Ben Zegveld + * @since 1.6 + */ +enum NullEnum { +} diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index ab33666435..e4a77f5391 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -721,3 +721,43 @@ i.e. You can map from `Map` where for each property a conversio When a raw map or a map that does not have a String as a key is used, then a warning will be generated. The warning is not generated if the map itself is mapped into some other target property directly as is. ==== + +[[adding-annotations]] +=== Adding annotations + +Other frameworks sometimes requires you to add annotations to certain classes so that they can easily detect the mappers. +Using the `@AnnotateWith` annotation you can generate an annotation at the specified location. + +For example Apache Camel has a `@Converter` annotation which you can apply to generated mappers using the `@AnnotateWith` annotation. + +.AnnotateWith source example +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +@AnnotateWith( + value = Converter.class, + elements = @AnnotateWith.Element( name = "generateBulkLoader", booleans = true ) +) +public interface MyConverter { + @AnnotateWith( Converter.class ) + DomainObject map( DtoObject dto ); +} +---- +==== + +.AnnotateWith generated mapper +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Converter( generateBulkLoader = true ) +public class MyConverterImpl implements MyConverter { + @Converter + public DomainObject map( DtoObject dto ) { + // default mapping behaviour + } +} +---- +==== diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index 70098c952e..cbb59f4e57 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -9,6 +9,8 @@ import javax.xml.bind.annotation.XmlElementRef; import org.mapstruct.AfterMapping; +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWiths; import org.mapstruct.BeanMapping; import org.mapstruct.BeforeMapping; import org.mapstruct.Builder; @@ -43,6 +45,9 @@ * * @author Gunnar Morling */ +@GemDefinition(AnnotateWith.class) +@GemDefinition(AnnotateWith.Element.class) +@GemDefinition(AnnotateWiths.class) @GemDefinition(Mapper.class) @GemDefinition(Mapping.class) @GemDefinition(Mappings.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java new file mode 100644 index 0000000000..f28cdfef9c --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java @@ -0,0 +1,602 @@ +/* + * 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.internal.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.internal.gem.AnnotateWithGem; +import org.mapstruct.ap.internal.gem.AnnotateWithsGem; +import org.mapstruct.ap.internal.gem.ElementGem; +import org.mapstruct.ap.internal.model.annotation.AnnotationElement; +import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType; +import org.mapstruct.ap.internal.model.annotation.EnumAnnotationElementHolder; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.RepeatableAnnotations; +import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.spi.TypeHierarchyErroneousException; +import org.mapstruct.tools.gem.GemValue; + +import static javax.lang.model.util.ElementFilter.methodsIn; + +/** + * @author Ben Zegveld + * @since 1.5 + */ +public class AdditionalAnnotationsBuilder + extends RepeatableAnnotations { + private static final String ANNOTATE_WITH_FQN = "org.mapstruct.AnnotateWith"; + private static final String ANNOTATE_WITHS_FQN = "org.mapstruct.AnnotateWiths"; + + private TypeFactory typeFactory; + private FormattingMessager messager; + + public AdditionalAnnotationsBuilder(ElementUtils elementUtils, TypeFactory typeFactory, + FormattingMessager messager) { + super( elementUtils, ANNOTATE_WITH_FQN, ANNOTATE_WITHS_FQN ); + this.typeFactory = typeFactory; + this.messager = messager; + } + + @Override + protected AnnotateWithGem singularInstanceOn(Element element) { + return AnnotateWithGem.instanceOn( element ); + } + + @Override + protected AnnotateWithsGem multipleInstanceOn(Element element) { + return AnnotateWithsGem.instanceOn( element ); + } + + @Override + protected void addInstance(AnnotateWithGem gem, Element source, Set mappings) { + buildAnnotation( gem, source ).ifPresent( t -> addAndValidateMapping( mappings, source, gem, t ) ); + } + + @Override + protected void addInstances(AnnotateWithsGem gem, Element source, Set mappings) { + for ( AnnotateWithGem annotateWithGem : gem.value().get() ) { + buildAnnotation( + annotateWithGem, + source ).ifPresent( t -> addAndValidateMapping( mappings, source, annotateWithGem, t ) ); + } + } + + private void addAndValidateMapping(Set mappings, Element source, AnnotateWithGem gem, Annotation anno) { + if ( anno.getType().getTypeElement().getAnnotation( Repeatable.class ) == null ) { + if ( mappings.stream().anyMatch( existing -> existing.getType().equals( anno.getType() ) ) ) { + messager.printMessage( + source, + gem.mirror(), + Message.ANNOTATE_WITH_ANNOTATION_IS_NOT_REPEATABLE, + anno.getType().describe() ); + return; + } + } + if ( mappings.stream().anyMatch( existing -> { + return existing.getType().equals( anno.getType() ) + && existing.getProperties().equals( anno.getProperties() ); + } ) ) { + messager.printMessage( + source, + gem.mirror(), + Message.ANNOTATE_WITH_DUPLICATE, + anno.getType().describe() ); + return; + } + mappings.add( anno ); + } + + private Optional buildAnnotation(AnnotateWithGem annotationGem, Element element) { + Type annotationType = typeFactory.getType( getTypeMirror( annotationGem.value() ) ); + List eleGems = annotationGem.elements().get(); + if ( isValid( annotationType, eleGems, element, annotationGem.mirror() ) ) { + return Optional.of( new Annotation( annotationType, convertToProperties( eleGems ) ) ); + } + return Optional.empty(); + } + + private List convertToProperties(List eleGems) { + return eleGems.stream().map( gem -> convertToProperty( gem, typeFactory ) ).collect( Collectors.toList() ); + } + + private enum ConvertToProperty { + BOOLEAN( + AnnotationElementType.BOOLEAN, + (eleGem, typeFactory) -> eleGem.booleans().get(), + eleGem -> eleGem.booleans().hasValue() + ), + BYTE( + AnnotationElementType.BYTE, + (eleGem, typeFactory) -> eleGem.bytes().get(), + eleGem -> eleGem.bytes().hasValue() + ), + CHARACTER( + AnnotationElementType.CHARACTER, + (eleGem, typeFactory) -> eleGem.chars().get(), + eleGem -> eleGem.chars().hasValue() + ), + CLASSES( + AnnotationElementType.CLASS, + (eleGem, typeFactory) -> { + return eleGem.classes().get().stream().map( typeFactory::getType ).collect( Collectors.toList() ); + }, + eleGem -> eleGem.classes().hasValue() + ), + DOUBLE( + AnnotationElementType.DOUBLE, + (eleGem, typeFactory) -> eleGem.doubles().get(), + eleGem -> eleGem.doubles().hasValue() + ), + ENUM( + AnnotationElementType.ENUM, + (eleGem, typeFactory) -> { + List values = new ArrayList<>(); + for ( String enumName : eleGem.enums().get() ) { + Type type = typeFactory.getType( eleGem.enumClass().get() ); + values.add( new EnumAnnotationElementHolder( type, enumName ) ); + } + return values; + }, + eleGem -> eleGem.enums().hasValue() && eleGem.enumClass().hasValue() + ), + FLOAT( + AnnotationElementType.FLOAT, + (eleGem, typeFactory) -> eleGem.floats().get(), + eleGem -> eleGem.floats().hasValue() + ), + INT( + AnnotationElementType.INTEGER, + (eleGem, typeFactory) -> eleGem.ints().get(), + eleGem -> eleGem.ints().hasValue() + ), + LONG( + AnnotationElementType.LONG, + (eleGem, typeFactory) -> eleGem.longs().get(), + eleGem -> eleGem.longs().hasValue() + ), + SHORT( + AnnotationElementType.SHORT, + (eleGem, typeFactory) -> eleGem.shorts().get(), + eleGem -> eleGem.shorts().hasValue() + ), + STRING( + AnnotationElementType.STRING, + (eleGem, typeFactory) -> eleGem.strings().get(), + eleGem -> eleGem.strings().hasValue() + ); + + private final AnnotationElementType type; + private final BiFunction> factory; + private final Predicate usabilityChecker; + + ConvertToProperty(AnnotationElementType type, + BiFunction> factory, + Predicate usabilityChecker) { + this.type = type; + this.factory = factory; + this.usabilityChecker = usabilityChecker; + } + + AnnotationElement toProperty(ElementGem eleGem, TypeFactory typeFactory) { + return new AnnotationElement( + type, + eleGem.name().get(), + factory.apply( eleGem, typeFactory ) + ); + } + + boolean isUsable(ElementGem eleGem) { + return usabilityChecker.test( eleGem ); + } + } + + private AnnotationElement convertToProperty(ElementGem eleGem, TypeFactory typeFactory) { + for ( ConvertToProperty convertToJava : ConvertToProperty.values() ) { + if ( convertToJava.isUsable( eleGem ) ) { + return convertToJava.toProperty( eleGem, typeFactory ); + } + } + return null; + } + + private boolean isValid(Type annotationType, List eleGems, Element element, + AnnotationMirror annotationMirror) { + boolean isValid = true; + if ( !annotationIsAllowed( annotationType, element, annotationMirror ) ) { + isValid = false; + } + + List annotationElements = methodsIn( annotationType.getTypeElement() + .getEnclosedElements() ); + if ( !allRequiredElementsArePresent( annotationType, annotationElements, eleGems, element, + annotationMirror ) ) { + isValid = false; + } + if ( !allElementsAreKnownInAnnotation( annotationType, annotationElements, eleGems, element ) ) { + isValid = false; + } + if ( !allElementsAreOfCorrectType( annotationType, annotationElements, eleGems, element ) ) { + isValid = false; + } + if ( !enumConstructionIsCorrectlyUsed( eleGems, element ) ) { + isValid = false; + } + if ( !allElementsAreUnique( eleGems, element ) ) { + isValid = false; + } + return isValid; + } + + private boolean allElementsAreUnique(List eleGems, Element element) { + boolean isValid = true; + List checkedElements = new ArrayList<>(); + for ( ElementGem elementGem : eleGems ) { + String elementName = elementGem.name().get(); + if ( checkedElements.contains( elementName ) ) { + isValid = false; + messager + .printMessage( + element, + elementGem.mirror(), + Message.ANNOTATE_WITH_DUPLICATE_PARAMETER, + elementName ); + } + else { + checkedElements.add( elementName ); + } + } + return isValid; + } + + private boolean enumConstructionIsCorrectlyUsed(List eleGems, Element element) { + boolean isValid = true; + for ( ElementGem elementGem : eleGems ) { + if ( elementGem.enums().hasValue() ) { + if ( elementGem.enumClass().getValue() == null ) { + isValid = false; + messager + .printMessage( + element, + elementGem.mirror(), + Message.ANNOTATE_WITH_ENUM_CLASS_NOT_DEFINED ); + } + else { + Type type = typeFactory.getType( getTypeMirror( elementGem.enumClass() ) ); + if ( type.isEnumType() ) { + List enumConstants = type.getEnumConstants(); + for ( String enumName : elementGem.enums().get() ) { + if ( !enumConstants.contains( enumName ) ) { + isValid = false; + messager + .printMessage( + element, + elementGem.mirror(), + elementGem.enums().getAnnotationValue(), + Message.ANNOTATE_WITH_ENUM_VALUE_DOES_NOT_EXIST, + type.describe(), + enumName ); + } + } + } + } + } + else if ( elementGem.enumClass().getValue() != null ) { + isValid = false; + messager.printMessage( element, elementGem.mirror(), Message.ANNOTATE_WITH_ENUMS_NOT_DEFINED ); + } + } + return isValid; + } + + private boolean annotationIsAllowed(Type annotationType, Element element, AnnotationMirror annotationMirror) { + Target target = annotationType.getTypeElement().getAnnotation( Target.class ); + if ( target == null ) { + return true; + } + + Set annotationTargets = Stream.of( target.value() ) + // The eclipse compiler returns null for some values + // Therefore, we filter out null values + .filter( Objects::nonNull ) + .collect( Collectors.toCollection( () -> EnumSet.noneOf( ElementType.class ) ) ); + + boolean isValid = true; + if ( isTypeTarget( element ) && !annotationTargets.contains( ElementType.TYPE ) ) { + isValid = false; + messager.printMessage( + element, + annotationMirror, + Message.ANNOTATE_WITH_NOT_ALLOWED_ON_CLASS, + annotationType.describe() + ); + } + if ( isMethodTarget( element ) && !annotationTargets.contains( ElementType.METHOD ) ) { + isValid = false; + messager.printMessage( + element, + annotationMirror, + Message.ANNOTATE_WITH_NOT_ALLOWED_ON_METHODS, + annotationType.describe() + ); + } + return isValid; + } + + private boolean isTypeTarget(Element element) { + return element.getKind().isInterface() || element.getKind().isClass(); + } + + private boolean isMethodTarget(Element element) { + return element.getKind() == ElementKind.METHOD; + } + + private boolean allElementsAreKnownInAnnotation(Type annotationType, List annotationParameters, + List eleGems, Element element) { + Set allowedAnnotationParameters = annotationParameters.stream() + .map( ee -> ee.getSimpleName().toString() ) + .collect( Collectors.toSet() ); + boolean isValid = true; + for ( ElementGem eleGem : eleGems ) { + if ( eleGem.name().isValid() + && !allowedAnnotationParameters.contains( eleGem.name().get() ) ) { + isValid = false; + messager + .printMessage( + element, + eleGem.mirror(), + eleGem.name().getAnnotationValue(), + Message.ANNOTATE_WITH_UNKNOWN_PARAMETER, + eleGem.name().get(), + annotationType.describe(), + Strings.getMostSimilarWord( eleGem.name().get(), allowedAnnotationParameters ) + ); + } + } + return isValid; + } + + private boolean allRequiredElementsArePresent(Type annotationType, List annotationParameters, + List elements, Element element, + AnnotationMirror annotationMirror) { + + boolean valid = true; + for ( ExecutableElement annotationParameter : annotationParameters ) { + if ( annotationParameter.getDefaultValue() == null ) { + // Mandatory parameter, must be present in the elements + String parameterName = annotationParameter.getSimpleName().toString(); + boolean elementGemDefined = false; + for ( ElementGem elementGem : elements ) { + if ( elementGem.isValid() && elementGem.name().get().equals( parameterName ) ) { + elementGemDefined = true; + break; + } + } + + if ( !elementGemDefined ) { + valid = false; + messager + .printMessage( + element, + annotationMirror, + Message.ANNOTATE_WITH_MISSING_REQUIRED_PARAMETER, + parameterName, + annotationType.describe() + ); + } + } + } + + + return valid; + } + + private boolean allElementsAreOfCorrectType(Type annotationType, List annotationParameters, + List elements, + Element element) { + Map annotationParametersByName = + annotationParameters.stream() + .collect( Collectors.toMap( ee -> ee.getSimpleName().toString(), Function.identity() ) ); + boolean isValid = true; + for ( ElementGem eleGem : elements ) { + Type annotationParameterType = getAnnotationParameterType( annotationParametersByName, eleGem ); + Type annotationParameterTypeSingular = getNonArrayType( annotationParameterType ); + if ( annotationParameterTypeSingular == null ) { + continue; + } + if ( hasTooManyDifferentTypes( eleGem ) ) { + isValid = false; + messager.printMessage( + element, + eleGem.mirror(), + eleGem.name().getAnnotationValue(), + Message.ANNOTATE_WITH_TOO_MANY_VALUE_TYPES, + eleGem.name().get(), + annotationParameterType.describe(), + annotationType.describe() + ); + } + else { + Map elementTypes = getParameterTypes( eleGem ); + Set reportedSizeError = new HashSet<>(); + for ( Type eleGemType : elementTypes.keySet() ) { + if ( !sameTypeOrAssignableClass( annotationParameterTypeSingular, eleGemType ) ) { + isValid = false; + messager.printMessage( + element, + eleGem.mirror(), + eleGem.name().getAnnotationValue(), + Message.ANNOTATE_WITH_WRONG_PARAMETER, + eleGem.name().get(), + eleGemType.describe(), + annotationParameterType.describe(), + annotationType.describe() + ); + } + else if ( !annotationParameterType.isArrayType() + && elementTypes.get( eleGemType ) > 1 + && !reportedSizeError.contains( eleGem ) ) { + isValid = false; + messager.printMessage( + element, + eleGem.mirror(), + Message.ANNOTATE_WITH_PARAMETER_ARRAY_NOT_EXPECTED, + eleGem.name().get(), + annotationType.describe() + ); + reportedSizeError.add( eleGem ); + } + } + } + } + return isValid; + } + + private boolean hasTooManyDifferentTypes( ElementGem eleGem ) { + return Arrays.stream( ConvertToProperty.values() ) + .filter( anotationElement -> anotationElement.isUsable( eleGem ) ) + .count() > 1; + } + + private Type getNonArrayType(Type annotationParameterType) { + if ( annotationParameterType == null ) { + return null; + } + if ( annotationParameterType.isArrayType() ) { + return annotationParameterType.getComponentType(); + } + + return annotationParameterType; + } + + private boolean sameTypeOrAssignableClass(Type annotationParameterType, Type eleGemType) { + return annotationParameterType.equals( eleGemType ) + || eleGemType.isAssignableTo( getTypeBound( annotationParameterType ) ); + } + + private Type getTypeBound(Type annotationParameterType) { + List typeParameters = annotationParameterType.getTypeParameters(); + if ( typeParameters.size() != 1 ) { + return annotationParameterType; + } + return typeParameters.get( 0 ).getTypeBound(); + } + + private Map getParameterTypes(ElementGem eleGem) { + Map suppliedParameterTypes = new HashMap<>(); + if ( eleGem.booleans().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( boolean.class ), + eleGem.booleans().get().size() ); + } + if ( eleGem.bytes().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( byte.class ), + eleGem.bytes().get().size() ); + } + if ( eleGem.chars().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( char.class ), + eleGem.chars().get().size() ); + } + if ( eleGem.classes().hasValue() ) { + for ( TypeMirror mirror : eleGem.classes().get() ) { + suppliedParameterTypes.put( + typeFactory.getType( typeMirrorFromAnnotation( mirror ) ), + eleGem.classes().get().size() + ); + } + } + if ( eleGem.doubles().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( double.class ), + eleGem.doubles().get().size() ); + } + if ( eleGem.floats().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( float.class ), + eleGem.floats().get().size() ); + } + if ( eleGem.ints().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( int.class ), + eleGem.ints().get().size() ); + } + if ( eleGem.longs().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( long.class ), + eleGem.longs().get().size() ); + } + if ( eleGem.shorts().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( short.class ), + eleGem.shorts().get().size() ); + } + if ( eleGem.strings().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( String.class ), + eleGem.strings().get().size() ); + } + if ( eleGem.enums().hasValue() && eleGem.enumClass().hasValue() ) { + suppliedParameterTypes.put( + typeFactory.getType( getTypeMirror( eleGem.enumClass() ) ), + eleGem.enums().get().size() ); + } + return suppliedParameterTypes; + } + + private Type getAnnotationParameterType(Map annotationParameters, + ElementGem element) { + if ( annotationParameters.containsKey( element.name().get() ) ) { + return typeFactory.getType( annotationParameters.get( element.name().get() ).getReturnType() ); + } + else { + return null; + } + } + + private TypeMirror getTypeMirror(GemValue gemValue) { + return typeMirrorFromAnnotation( gemValue.getValue() ); + } + + private TypeMirror typeMirrorFromAnnotation(TypeMirror typeMirror) { + if ( typeMirror == null ) { + // When a class used in an annotation is created by another annotation processor + // then javac will not return correct TypeMirror with TypeKind#ERROR, but rather a string "" + // the gem tools would return a null TypeMirror in that case. + // Therefore, throw TypeHierarchyErroneousException so we can postpone the generation of the mapper + throw new TypeHierarchyErroneousException( typeMirror ); + } + + return typeMirror; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Annotation.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Annotation.java index 405958ccdf..866012c51a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Annotation.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Annotation.java @@ -6,9 +6,11 @@ package org.mapstruct.ap.internal.model; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.annotation.AnnotationElement; import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Type; @@ -21,16 +23,13 @@ public class Annotation extends ModelElement { private final Type type; - /** - * List of annotation attributes. Quite simplistic, but it's sufficient for now. - */ - private List properties; + private List properties; public Annotation(Type type) { this( type, Collections.emptyList() ); } - public Annotation(Type type, List properties) { + public Annotation(Type type, List properties) { this.type = type; this.properties = properties; } @@ -41,10 +40,15 @@ public Type getType() { @Override public Set getImportTypes() { - return Collections.singleton( type ); + Set types = new HashSet<>(); + for ( AnnotationElement prop : properties ) { + types.addAll( prop.getImportTypes() ); + } + types.add( type ); + return types; } - public List getProperties() { + public List getProperties() { return properties; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index d17718370d..e748c173af 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -85,6 +85,7 @@ */ public class BeanMappingMethod extends NormalTypeMappingMethod { + private final List annotations; private final List propertyMappings; private final Map> mappingsByParameter; private final Map> constructorMappingsByParameter; @@ -112,6 +113,7 @@ public static class Builder extends AbstractMappingMethodBuilder unprocessedSourceParameters = new HashSet<>(); private final Set existingVariableNames = new HashSet<>(); private final Map> unprocessedDefinedTargets = new LinkedHashMap<>(); + private final List annotations = new ArrayList<>(); private MappingReferences mappingReferences; private MethodReference factoryMethod; @@ -214,6 +216,12 @@ else if ( !method.isUpdateMethod() ) { // If the return type cannot be constructed then no need to try to create mappings return null; } + AdditionalAnnotationsBuilder additionalAnnotationsBuilder = + new AdditionalAnnotationsBuilder( + ctx.getElementUtils(), + ctx.getTypeFactory(), + ctx.getMessager() ); + annotations.addAll( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); /* the type that needs to be used in the mapping process as target */ Type resultTypeToMap = returnTypeToConstruct == null ? method.getResultType() : returnTypeToConstruct; @@ -362,6 +370,7 @@ else if ( !method.isUpdateMethod() ) { return new BeanMappingMethod( method, + annotations, existingVariableNames, propertyMappings, factoryMethod, @@ -1701,6 +1710,7 @@ private ConstructorAccessor( //CHECKSTYLE:OFF private BeanMappingMethod(Method method, + List annotations, Collection existingVariableNames, List propertyMappings, MethodReference factoryMethod, @@ -1722,6 +1732,7 @@ private BeanMappingMethod(Method method, ); //CHECKSTYLE:ON + this.annotations = annotations; this.propertyMappings = propertyMappings; this.returnTypeBuilder = returnTypeBuilder; this.finalizerMethod = finalizerMethod; @@ -1760,6 +1771,10 @@ else if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) { this.subclassMappings = subclassMappings; } + public List getAnnotations() { + return annotations; + } + public List getConstantMappings() { return constantMappings; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java index 134ab081dc..f8f65c927f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java @@ -10,15 +10,14 @@ import java.util.List; import java.util.SortedSet; import java.util.TreeSet; - import javax.lang.model.type.TypeKind; -import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.option.Options; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.version.VersionInformation; @@ -220,7 +219,9 @@ public SortedSet getImportTypes() { } for ( Annotation annotation : annotations ) { - addIfImportRequired( importedTypes, annotation.getType() ); + for ( Type type : annotation.getImportTypes() ) { + addIfImportRequired( importedTypes, type ); + } } for ( Type extraImport : extraImportedTypes ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java index fa15cd8ed0..9b7729e8fa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Set; import java.util.SortedSet; - import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; @@ -43,6 +42,7 @@ public static class Builder extends GeneratedTypeBuilder { private String implPackage; private boolean customPackage; private boolean suppressGeneratorTimestamp; + private Set customAnnotations; public Builder() { super( Builder.class ); @@ -63,6 +63,11 @@ public Builder constructorFragments(Set fragment return this; } + public Builder additionalAnnotations(Set customAnnotations) { + this.customAnnotations = customAnnotations; + return this; + } + public Builder decorator(Decorator decorator) { this.decorator = decorator; return this; @@ -105,6 +110,7 @@ public Mapper build() { definitionType, customPackage, customName, + customAnnotations, methods, options, versionInformation, @@ -126,7 +132,7 @@ public Mapper build() { @SuppressWarnings( "checkstyle:parameternumber" ) private Mapper(TypeFactory typeFactory, String packageName, String name, Type mapperDefinitionType, - boolean customPackage, boolean customImplName, + boolean customPackage, boolean customImplName, Set customAnnotations, List methods, Options options, VersionInformation versionInformation, boolean suppressGeneratorTimestamp, Accessibility accessibility, List fields, Constructor constructor, @@ -148,6 +154,7 @@ private Mapper(TypeFactory typeFactory, String packageName, String name, ); this.customPackage = customPackage; this.customImplName = customImplName; + customAnnotations.forEach( this::addAnnotation ); this.decorator = decorator; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/AnnotationElement.java b/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/AnnotationElement.java new file mode 100644 index 0000000000..6ecde886bf --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/AnnotationElement.java @@ -0,0 +1,128 @@ +/* + * 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.internal.model.annotation; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Ben Zegveld + */ +public class AnnotationElement extends ModelElement { + public enum AnnotationElementType { + BOOLEAN, BYTE, CHARACTER, CLASS, DOUBLE, ENUM, FLOAT, INTEGER, LONG, SHORT, STRING + } + + private final String elementName; + private final List values; + private final AnnotationElementType type; + + public AnnotationElement(AnnotationElementType type, List values) { + this( type, null, values ); + } + + public AnnotationElement(AnnotationElementType type, String elementName, List values) { + this.type = type; + this.elementName = elementName; + this.values = values; + } + + public String getElementName() { + return elementName; + } + + public List getValues() { + return values; + } + + @Override + public Set getImportTypes() { + Set importTypes = null; + for ( Object value : values ) { + if ( value instanceof ModelElement ) { + if ( importTypes == null ) { + importTypes = new HashSet<>(); + } + importTypes.addAll( ( (ModelElement) value ).getImportTypes() ); + } + } + + return importTypes == null ? Collections.emptySet() : importTypes; + } + + public boolean isBoolean() { + return type == AnnotationElementType.BOOLEAN; + } + + public boolean isByte() { + return type == AnnotationElementType.BYTE; + } + + public boolean isCharacter() { + return type == AnnotationElementType.CHARACTER; + } + + public boolean isClass() { + return type == AnnotationElementType.CLASS; + } + + public boolean isDouble() { + return type == AnnotationElementType.DOUBLE; + } + + public boolean isEnum() { + return type == AnnotationElementType.ENUM; + } + + public boolean isFloat() { + return type == AnnotationElementType.FLOAT; + } + + public boolean isInteger() { + return type == AnnotationElementType.INTEGER; + } + + public boolean isLong() { + return type == AnnotationElementType.LONG; + } + + public boolean isShort() { + return type == AnnotationElementType.SHORT; + } + + public boolean isString() { + return type == AnnotationElementType.STRING; + } + + @Override + public int hashCode() { + return Objects.hash( elementName, type, values ); + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + AnnotationElement other = (AnnotationElement) obj; + return Objects.equals( elementName, other.elementName ) + && type == other.type + && Objects.equals( values, other.values ); + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/EnumAnnotationElementHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/EnumAnnotationElementHolder.java new file mode 100644 index 0000000000..de87f32525 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/EnumAnnotationElementHolder.java @@ -0,0 +1,35 @@ +/* + * 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.internal.model.annotation; + +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.Type; + +public class EnumAnnotationElementHolder extends ModelElement { + + private final Type enumClass; + private final String name; + + public EnumAnnotationElementHolder(Type enumClass, String name) { + this.enumClass = enumClass; + this.name = name; + } + + public Type getEnumClass() { + return enumClass; + } + + public String getName() { + return name; + } + + @Override + public Set getImportTypes() { + return enumClass.getImportTypes(); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java index 5161ea689c..999c0a51df 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java @@ -12,6 +12,8 @@ import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Mapper; +import org.mapstruct.ap.internal.model.annotation.AnnotationElement; +import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType; /** * A {@link ModelElementProcessor} which converts the given {@link Mapper} @@ -67,7 +69,10 @@ private Annotation named() { private Annotation namedDelegate(Mapper mapper) { return new Annotation( getTypeFactory().getType( "jakarta.inject.Named" ), - Collections.singletonList( '"' + mapper.getPackageName() + "." + mapper.getName() + '"' ) + Collections.singletonList( + new AnnotationElement( AnnotationElementType.STRING, + Collections.singletonList( mapper.getPackageName() + "." + mapper.getName() ) + ) ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java index 6a6cc16580..00ba72a079 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java @@ -12,6 +12,8 @@ import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Mapper; +import org.mapstruct.ap.internal.model.annotation.AnnotationElement; +import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.util.AnnotationProcessingException; @@ -70,7 +72,11 @@ private Annotation named() { private Annotation namedDelegate(Mapper mapper) { return new Annotation( getType( "Named" ), - Collections.singletonList( '"' + mapper.getPackageName() + "." + mapper.getName() + '"' ) + Collections.singletonList( + new AnnotationElement( + AnnotationElementType.STRING, + Collections.singletonList( mapper.getPackageName() + "." + mapper.getName() ) + ) ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index d8acb10398..c05d2d18bd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -31,6 +31,7 @@ import org.mapstruct.ap.internal.gem.MapperGem; import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.model.AdditionalAnnotationsBuilder; import org.mapstruct.ap.internal.model.BeanMappingMethod; import org.mapstruct.ap.internal.model.ContainerMappingMethod; import org.mapstruct.ap.internal.model.ContainerMappingMethodBuilder; @@ -93,6 +94,8 @@ public class MapperCreationProcessor implements ModelElementProcessor sourceModel) { this.elementUtils = context.getElementUtils(); @@ -103,6 +106,8 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, L this.versionInformation = context.getVersionInformation(); this.typeFactory = context.getTypeFactory(); this.accessorNaming = context.getAccessorNaming(); + additionalAnnotationsBuilder = + new AdditionalAnnotationsBuilder( elementUtils, typeFactory, messager ); MapperOptions mapperOptions = MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() ); List mapperReferences = initReferencedMappers( mapperTypeElement, mapperOptions ); @@ -206,6 +211,7 @@ private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List< .implName( mapperOptions.implementationName() ) .implPackage( mapperOptions.implementationPackage() ) .suppressGeneratorTimestamp( mapperOptions.suppressTimestampInGenerated() ) + .additionalAnnotations( additionalAnnotationsBuilder.getProcessedAnnotations( element ) ) .build(); if ( !mappingContext.getForgedMethodsUnderCreation().isEmpty() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 201f1a1f22..bda33b6a92 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -8,13 +8,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; @@ -54,9 +51,9 @@ import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.RepeatableAnnotations; import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.spi.EnumTransformationStrategy; -import org.mapstruct.tools.gem.Gem; /** * A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s @@ -68,8 +65,6 @@ */ public class MethodRetrievalProcessor implements ModelElementProcessor> { - private static final String JAVA_LANG_ANNOTATION_PGK = "java.lang.annotation"; - private static final String ORG_MAPSTRUCT_PKG = "org.mapstruct"; private static final String MAPPING_FQN = "org.mapstruct.Mapping"; private static final String MAPPINGS_FQN = "org.mapstruct.Mappings"; private static final String SUB_CLASS_MAPPING_FQN = "org.mapstruct.SubclassMapping"; @@ -280,8 +275,7 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType, typeUtils, typeFactory ); - RepeatableMappings repeatableMappings = new RepeatableMappings(); - Set mappingOptions = repeatableMappings.getMappings( method, beanMappingOptions ); + Set mappingOptions = getMappings( method, beanMappingOptions ); IterableMappingOptions iterableMappingOptions = IterableMappingOptions.fromGem( IterableMappingGem.instanceOn( method ), @@ -593,7 +587,7 @@ private boolean isStreamTypeOrIterableFromJavaStdLib(Type type) { * @return The mappings for the given method, keyed by target property name */ private Set getMappings(ExecutableElement method, BeanMappingOptions beanMapping) { - return new RepeatableMappings().getMappings( method, beanMapping ); + return new RepeatableMappings( beanMapping ).getProcessedAnnotations( method ); } /** @@ -607,173 +601,107 @@ private Set getMappings(ExecutableElement method, BeanMappingOpt private Set getSubclassMappings(List sourceParameters, Type resultType, ExecutableElement method, BeanMappingOptions beanMapping, SubclassValidator validator) { - return new RepeatableSubclassMappings( sourceParameters, resultType, validator ) - .getMappings( method, beanMapping ); + return new RepeatableSubclassMappings( beanMapping, sourceParameters, resultType, validator ) + .getProcessedAnnotations( method ); } - private class RepeatableMappings extends RepeatableMappingAnnotations { - RepeatableMappings() { - super( MAPPING_FQN, MAPPINGS_FQN ); + private class RepeatableMappings extends RepeatableAnnotations { + private BeanMappingOptions beanMappingOptions; + + RepeatableMappings(BeanMappingOptions beanMappingOptions) { + super( elementUtils, MAPPING_FQN, MAPPINGS_FQN ); + this.beanMappingOptions = beanMappingOptions; } @Override - MappingGem singularInstanceOn(Element element) { + protected MappingGem singularInstanceOn(Element element) { return MappingGem.instanceOn( element ); } @Override - MappingsGem multipleInstanceOn(Element element) { + protected MappingsGem multipleInstanceOn(Element element) { return MappingsGem.instanceOn( element ); } @Override - void addInstance(MappingGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, - Set mappings) { - MappingOptions.addInstance( gem, method, beanMappingOptions, messager, typeUtils, mappings ); + protected void addInstance(MappingGem gem, Element method, Set mappings) { + MappingOptions.addInstance( + gem, + (ExecutableElement) method, + beanMappingOptions, + messager, + typeUtils, + mappings ); } @Override - void addInstances(MappingsGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, - Set mappings) { - MappingOptions.addInstances( gem, method, beanMappingOptions, messager, typeUtils, mappings ); + protected void addInstances(MappingsGem gem, Element method, Set mappings) { + MappingOptions.addInstances( + gem, + (ExecutableElement) method, + beanMappingOptions, + messager, + typeUtils, + mappings ); } } private class RepeatableSubclassMappings - extends RepeatableMappingAnnotations { + extends RepeatableAnnotations { private final List sourceParameters; private final Type resultType; private SubclassValidator validator; + private BeanMappingOptions beanMappingOptions; - RepeatableSubclassMappings(List sourceParameters, Type resultType, SubclassValidator validator) { - super( SUB_CLASS_MAPPING_FQN, SUB_CLASS_MAPPINGS_FQN ); + RepeatableSubclassMappings(BeanMappingOptions beanMappingOptions, List sourceParameters, + Type resultType, SubclassValidator validator) { + super( elementUtils, SUB_CLASS_MAPPING_FQN, SUB_CLASS_MAPPINGS_FQN ); + this.beanMappingOptions = beanMappingOptions; this.sourceParameters = sourceParameters; this.resultType = resultType; this.validator = validator; } @Override - SubclassMappingGem singularInstanceOn(Element element) { + protected SubclassMappingGem singularInstanceOn(Element element) { return SubclassMappingGem.instanceOn( element ); } @Override - SubclassMappingsGem multipleInstanceOn(Element element) { + protected SubclassMappingsGem multipleInstanceOn(Element element) { return SubclassMappingsGem.instanceOn( element ); } @Override - void addInstance(SubclassMappingGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, - Set mappings) { - SubclassMappingOptions - .addInstance( - gem, - method, - beanMappingOptions, - messager, - typeUtils, - mappings, - sourceParameters, - resultType, - validator ); + protected void addInstance(SubclassMappingGem gem, + Element method, + Set mappings) { + SubclassMappingOptions.addInstance( + gem, + (ExecutableElement) method, + beanMappingOptions, + messager, + typeUtils, + mappings, + sourceParameters, + resultType, + validator ); } @Override - void addInstances(SubclassMappingsGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, - Set mappings) { - SubclassMappingOptions - .addInstances( - gem, - method, - beanMappingOptions, - messager, - typeUtils, - mappings, - sourceParameters, - resultType, - validator ); - } - } - - private abstract class RepeatableMappingAnnotations { - - private final String singularFqn; - private final String multipleFqn; - - RepeatableMappingAnnotations(String singularFqn, String multipleFqn) { - this.singularFqn = singularFqn; - this.multipleFqn = multipleFqn; - } - - abstract SINGULAR singularInstanceOn(Element element); - - abstract MULTIPLE multipleInstanceOn(Element element); - - abstract void addInstance(SINGULAR gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, - Set mappings); - - abstract void addInstances(MULTIPLE gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, - Set mappings); - - /** - * Retrieves the mappings configured via {@code @Mapping} from the given method. - * - * @param method The method of interest - * @param beanMapping options coming from bean mapping method - * @return The mappings for the given method, keyed by target property name - */ - public Set getMappings(ExecutableElement method, BeanMappingOptions beanMapping) { - return getMappings( method, method, beanMapping, new LinkedHashSet<>(), new HashSet<>() ); - } - - /** - * Retrieves the mappings configured via {@code @Mapping} from the given method. - * - * @param method The method of interest - * @param element Element of interest: method, or (meta) annotation - * @param beanMapping options coming from bean mapping method - * @param mappingOptions LinkedSet of mappings found so far - * @return The mappings for the given method, keyed by target property name - */ - private Set getMappings(ExecutableElement method, Element element, - BeanMappingOptions beanMapping, LinkedHashSet mappingOptions, - Set handledElements) { - - for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { - Element lElement = annotationMirror.getAnnotationType().asElement(); - if ( isAnnotation( lElement, singularFqn ) ) { - // although getInstanceOn does a search on annotation mirrors, the order is preserved - SINGULAR mapping = singularInstanceOn( element ); - addInstance( mapping, method, beanMapping, mappingOptions ); - } - else if ( isAnnotation( lElement, multipleFqn ) ) { - // although getInstanceOn does a search on annotation mirrors, the order is preserved - MULTIPLE mappings = multipleInstanceOn( element ); - addInstances( mappings, method, beanMapping, mappingOptions ); - } - else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) - && !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG ) - && !handledElements.contains( lElement ) ) { - // recur over annotation mirrors - handledElements.add( lElement ); - getMappings( method, lElement, beanMapping, mappingOptions, handledElements ); - } - } - return mappingOptions; - } - - private boolean isAnnotationInPackage(Element element, String packageFQN) { - if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { - return packageFQN.equals( elementUtils.getPackageOf( element ).getQualifiedName().toString() ); - } - return false; - } - - private boolean isAnnotation(Element element, String annotationFQN) { - if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { - return annotationFQN.equals( ( (TypeElement) element ).getQualifiedName().toString() ); - } - return false; + protected void addInstances(SubclassMappingsGem gem, + Element method, + Set mappings) { + SubclassMappingOptions.addInstances( + gem, + (ExecutableElement) method, + beanMappingOptions, + messager, + typeUtils, + mappings, + sourceParameters, + resultType, + validator ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java index f7aa9b4fea..4efc537c3d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java @@ -13,6 +13,8 @@ import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Mapper; +import org.mapstruct.ap.internal.model.annotation.AnnotationElement; +import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType; /** * A {@link ModelElementProcessor} which converts the given {@link Mapper} @@ -75,7 +77,11 @@ private Annotation autowired() { private Annotation qualifierDelegate() { return new Annotation( getTypeFactory().getType( "org.springframework.beans.factory.annotation.Qualifier" ), - Collections.singletonList( "\"delegate\"" ) ); + Collections.singletonList( + new AnnotationElement( + AnnotationElementType.STRING, + Collections.singletonList( "delegate" ) + ) ) ); } private Annotation primary() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 4e1c0302fa..92951ad521 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -198,6 +198,20 @@ public enum Message { MAPTOBEANMAPPING_WRONG_KEY_TYPE( "The Map parameter \"%s\" cannot be used for property mapping. It must be typed with Map but it was typed with %s.", Diagnostic.Kind.WARNING ), MAPTOBEANMAPPING_RAW_MAP( "The Map parameter \"%s\" cannot be used for property mapping. It must be typed with Map but it was raw.", Diagnostic.Kind.WARNING ), + + ANNOTATE_WITH_MISSING_REQUIRED_PARAMETER( "Parameter \"%s\" is required for annotation \"%s\"." ), + ANNOTATE_WITH_UNKNOWN_PARAMETER( "Unknown parameter \"%s\" for annotation \"%s\". Did you mean \"%s\"?" ), + ANNOTATE_WITH_DUPLICATE_PARAMETER( "Parameter \"%s\" must not be defined more than once." ), + ANNOTATE_WITH_WRONG_PARAMETER( "Parameter \"%s\" is not of type \"%s\" but of type \"%s\" for annotation \"%s\"." ), + ANNOTATE_WITH_TOO_MANY_VALUE_TYPES( "Parameter \"%s\" has too many value types supplied, type \"%s\" is expected for annotation \"%s\"." ), + ANNOTATE_WITH_PARAMETER_ARRAY_NOT_EXPECTED( "Parameter \"%s\" does not accept multiple values for annotation \"%s\"." ), + ANNOTATE_WITH_NOT_ALLOWED_ON_CLASS( "Annotation \"%s\" is not allowed on classes." ), + ANNOTATE_WITH_NOT_ALLOWED_ON_METHODS( "Annotation \"%s\" is not allowed on methods." ), + ANNOTATE_WITH_ENUM_VALUE_DOES_NOT_EXIST( "Enum \"%s\" does not have value \"%s\"." ), + ANNOTATE_WITH_ENUM_CLASS_NOT_DEFINED( "enumClass needs to be defined when using enums." ), + ANNOTATE_WITH_ENUMS_NOT_DEFINED( "enums needs to be defined when using enumClass." ), + ANNOTATE_WITH_ANNOTATION_IS_NOT_REPEATABLE( "Annotation \"%s\" is not repeatable." ), + ANNOTATE_WITH_DUPLICATE( "Annotation \"%s\" is already present with the same elements configuration.", Diagnostic.Kind.WARNING ), ; // CHECKSTYLE:ON diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/RepeatableAnnotations.java b/processor/src/main/java/org/mapstruct/ap/internal/util/RepeatableAnnotations.java new file mode 100644 index 0000000000..accae77718 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/RepeatableAnnotations.java @@ -0,0 +1,120 @@ +/* + * 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.internal.util; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; + +import org.mapstruct.tools.gem.Gem; + +/** + * @author Ben Zegveld + */ +public abstract class RepeatableAnnotations { + private static final String JAVA_LANG_ANNOTATION_PGK = "java.lang.annotation"; + private static final String ORG_MAPSTRUCT_PKG = "org.mapstruct"; + + private ElementUtils elementUtils; + private final String singularFqn; + private final String multipleFqn; + + protected RepeatableAnnotations(ElementUtils elementUtils, String singularFqn, String multipleFqn) { + this.elementUtils = elementUtils; + this.singularFqn = singularFqn; + this.multipleFqn = multipleFqn; + } + + /** + * @param element the element on which the Gem needs to be found + * @return the Gem found on the element. + */ + protected abstract SINGULAR singularInstanceOn(Element element); + + /** + * @param element the element on which the Gems needs to be found + * @return the Gems found on the element. + */ + protected abstract MULTIPLE multipleInstanceOn(Element element); + + /** + * @param gem the annotation gem to be processed + * @param source the source element where the request originated from + * @param mappings the collection of completed processing + */ + protected abstract void addInstance(SINGULAR gem, Element source, Set mappings); + + /** + * @param gems the annotation gems to be processed + * @param source the source element where the request originated from + * @param mappings the collection of completed processing + */ + protected abstract void addInstances(MULTIPLE gems, Element source, Set mappings); + + /** + * Retrieves the processed annotations. + * + * @param source The source element of interest + * @return The processed annotations for the given element + */ + public Set getProcessedAnnotations(Element source) { + return getMappings( source, source, new LinkedHashSet<>(), new HashSet<>() ); + } + + /** + * Retrieves the processed annotations. + * + * @param source The source element of interest + * @param element Element of interest: method, or (meta) annotation + * @param mappingOptions LinkedSet of mappings found so far + * @param handledElements The collection of already handled elements to handle recursion correctly. + * @return The processed annotations for the given element + */ + private Set getMappings(Element source, Element element, + LinkedHashSet mappingOptions, + Set handledElements) { + + for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { + Element lElement = annotationMirror.getAnnotationType().asElement(); + if ( isAnnotation( lElement, singularFqn ) ) { + // although getInstanceOn does a search on annotation mirrors, the order is preserved + SINGULAR mapping = singularInstanceOn( element ); + addInstance( mapping, source, mappingOptions ); + } + else if ( isAnnotation( lElement, multipleFqn ) ) { + // although getInstanceOn does a search on annotation mirrors, the order is preserved + MULTIPLE mappings = multipleInstanceOn( element ); + addInstances( mappings, source, mappingOptions ); + } + else if ( !isAnnotationInPackage( lElement, JAVA_LANG_ANNOTATION_PGK ) + && !isAnnotationInPackage( lElement, ORG_MAPSTRUCT_PKG ) + && !handledElements.contains( lElement ) ) { + // recur over annotation mirrors + handledElements.add( lElement ); + getMappings( source, lElement, mappingOptions, handledElements ); + } + } + return mappingOptions; + } + + private boolean isAnnotationInPackage(Element element, String packageFQN) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return packageFQN.equals( elementUtils.getPackageOf( element ).getQualifiedName().toString() ); + } + return false; + } + + private boolean isAnnotation(Element element, String annotationFQN) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return annotationFQN.equals( ( (TypeElement) element ).getQualifiedName().toString() ); + } + return false; + } +} diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl index 4391bcb975..eb67d732fc 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl @@ -6,4 +6,4 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.Annotation" --> -@<@includeModel object=type/><#if (properties?size > 0) >(<#list properties as property>${property}<#if property_has_next>, ) \ No newline at end of file +@<@includeModel object=type/><#if (properties?size > 0) >(<#list properties as property><@includeModel object=property/><#if property_has_next>, ) \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 81e3b61fd7..1b402a57f0 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -6,6 +6,9 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.BeanMappingMethod" --> +<#list annotations as annotation> + <#nt><@includeModel object=annotation/> + <#if overridden>@Override <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#assign targetType = resultType /> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/annotation/AnnotationElement.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/annotation/AnnotationElement.ftl new file mode 100644 index 0000000000..21c1977f58 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/annotation/AnnotationElement.ftl @@ -0,0 +1,48 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.annotation.AnnotationElement" --> +<@compress single_line=true> + <#if elementName??> + ${elementName} = + + <#if (values?size > 1) > + { + + <#-- rt and lt tags below are for formatting the arrays so that there are no spaces before ',' --> + <#list values as value> + <#if boolean> + ${value?c}<#rt> + <#elseif byte> + ${value}<#rt> + <#elseif character> + '${value}'<#rt> + <#elseif class> + <@includeModel object=value raw=true/>.class<#rt> + <#elseif double> + ${value?c}<#rt> + <#elseif enum> + <@includeModel object=value/><#rt> + <#elseif float> + ${value?c}f<#rt> + <#elseif integer> + ${value?c}<#rt> + <#elseif long> + ${value?c}L<#rt> + <#elseif short> + ${value?c}<#rt> + <#elseif string> + "${value}"<#rt> + + <#if value_has_next> + , <#lt> + + + <#if (values?size > 1) > + } + + \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/annotation/EnumAnnotationElementHolder.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/annotation/EnumAnnotationElementHolder.ftl new file mode 100644 index 0000000000..145b933156 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/annotation/EnumAnnotationElementHolder.ftl @@ -0,0 +1,9 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.annotation.EnumAnnotationElementHolder" --> +<@includeModel object=enumClass raw=true/>.${name}<#rt> \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithEnum.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithEnum.java new file mode 100644 index 0000000000..b2627fd8ce --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithEnum.java @@ -0,0 +1,10 @@ +/* + * 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.annotatewith; + +public enum AnnotateWithEnum { + EXISTING, OTHER_EXISTING +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java new file mode 100644 index 0000000000..c2055b4c5c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java @@ -0,0 +1,548 @@ +/* + * 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.annotatewith; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.GeneratedSource; +import org.mapstruct.factory.Mappers; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Ben Zegveld + */ +@IssueKey("1574") +@WithClasses(AnnotateWithEnum.class) +public class AnnotateWithTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses({ DeprecateAndCustomMapper.class, CustomAnnotation.class }) + public void mapperBecomesDeprecatedAndGetsCustomAnnotation() { + DeprecateAndCustomMapper mapper = Mappers.getMapper( DeprecateAndCustomMapper.class ); + + assertThat( mapper.getClass() ).hasAnnotations( Deprecated.class, CustomAnnotation.class ); + } + + @ProcessorTest + @WithClasses( { CustomNamedMapper.class, CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class } ) + public void annotationWithValue() { + generatedSource.addComparisonToFixtureFor( CustomNamedMapper.class ); + } + + @ProcessorTest + @WithClasses( { MultipleArrayValuesMapper.class, CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class } ) + public void annotationWithMultipleValues() { + generatedSource.addComparisonToFixtureFor( MultipleArrayValuesMapper.class ); + } + + @ProcessorTest + @WithClasses( { CustomNamedGenericClassMapper.class, CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class } ) + public void annotationWithCorrectGenericClassValue() { + CustomNamedGenericClassMapper mapper = Mappers.getMapper( CustomNamedGenericClassMapper.class ); + + CustomAnnotationWithParams annotation = mapper.getClass().getAnnotation( CustomAnnotationWithParams.class ); + assertThat( annotation ).isNotNull(); + assertThat( annotation.stringParam() ).isEqualTo( "test" ); + assertThat( annotation.genericTypedClass() ).isEqualTo( Mapper.class ); + } + + @ProcessorTest + @WithClasses( { AnnotationWithoutElementNameMapper.class, CustomAnnotation.class } ) + public void annotateWithoutElementName() { + generatedSource + .forMapper( AnnotationWithoutElementNameMapper.class ) + .content() + .contains( "@CustomAnnotation(value = \"value\")" ); + } + + @ProcessorTest + @WithClasses({ MetaAnnotatedMapper.class, ClassMetaAnnotation.class, CustomClassOnlyAnnotation.class }) + public void metaAnnotationWorks() { + MetaAnnotatedMapper mapper = Mappers.getMapper( MetaAnnotatedMapper.class ); + + assertThat( mapper.getClass() ).hasAnnotation( CustomClassOnlyAnnotation.class ); + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithMissingParameter.class, + line = 15, + message = "Parameter \"required\" is required for annotation \"AnnotationWithRequiredParameter\"." + ) + } + ) + @WithClasses({ ErroneousMapperWithMissingParameter.class, AnnotationWithRequiredParameter.class }) + public void erroneousMapperWithMissingParameter() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithMethodOnInterface.class, + line = 15, + message = "Annotation \"CustomMethodOnlyAnnotation\" is not allowed on classes." + ) + } + ) + @WithClasses({ ErroneousMapperWithMethodOnInterface.class, CustomMethodOnlyAnnotation.class }) + public void erroneousMapperWithMethodOnInterface() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithMethodOnClass.class, + line = 15, + message = "Annotation \"CustomMethodOnlyAnnotation\" is not allowed on classes." + ) + } + ) + @WithClasses({ ErroneousMapperWithMethodOnClass.class, CustomMethodOnlyAnnotation.class }) + public void erroneousMapperWithMethodOnClass() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithAnnotationOnlyOnInterface.class, + line = 15, + message = "Annotation \"CustomAnnotationOnlyAnnotation\" is not allowed on classes." + ) + } + ) + @WithClasses({ ErroneousMapperWithAnnotationOnlyOnInterface.class, CustomAnnotationOnlyAnnotation.class }) + public void erroneousMapperWithAnnotationOnlyOnInterface() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithAnnotationOnlyOnClass.class, + line = 15, + message = "Annotation \"CustomAnnotationOnlyAnnotation\" is not allowed on classes." + ) + } + ) + @WithClasses({ ErroneousMapperWithAnnotationOnlyOnClass.class, CustomAnnotationOnlyAnnotation.class }) + public void erroneousMapperWithAnnotationOnlyOnClass() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithClassOnMethod.class, + line = 17, + message = "Annotation \"CustomClassOnlyAnnotation\" is not allowed on methods." + ) + } + ) + @WithClasses({ ErroneousMapperWithClassOnMethod.class, CustomClassOnlyAnnotation.class }) + public void erroneousMapperWithClassOnMethod() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithUnknownParameter.class, + line = 17, + message = "Unknown parameter \"unknownParameter\" for annotation \"CustomAnnotation\"." + + " Did you mean \"value\"?" + ) + } + ) + @WithClasses({ ErroneousMapperWithUnknownParameter.class, CustomAnnotation.class }) + public void erroneousMapperWithUnknownParameter() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithNonExistantEnum.class, + line = 17, + message = "Enum \"AnnotateWithEnum\" does not have value \"NON_EXISTANT\"." + ) + } + ) + @WithClasses( { ErroneousMapperWithNonExistantEnum.class, CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class } ) + public void erroneousMapperWithNonExistantEnum() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithTooManyParameterValues.class, + line = 17, + message = "Parameter \"stringParam\" has too many value types supplied, type \"String\" is expected" + + " for annotation \"CustomAnnotationWithParams\"." + ) + } + ) + @WithClasses( { ErroneousMapperWithTooManyParameterValues.class, CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class } ) + public void erroneousMapperWithTooManyParameterValues() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 16, + alternativeLine = 43, + message = "Parameter \"stringParam\" is not of type \"boolean\" but of type \"String\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 18, + alternativeLine = 43, + message = "Parameter \"stringParam\" is not of type \"byte\" but of type \"String\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 20, + alternativeLine = 43, + message = "Parameter \"stringParam\" is not of type \"char\" but of type \"String\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 22, + alternativeLine = 43, + message = "Parameter \"stringParam\" is not of type \"CustomAnnotationWithParams\"" + + " but of type \"String\" for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 24, + alternativeLine = 43, + message = "Parameter \"stringParam\" is not of type \"double\" but of type \"String\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 26, + alternativeLine = 43, + message = "Parameter \"stringParam\" is not of type \"float\" but of type \"String\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 28, + alternativeLine = 43, + message = "Parameter \"stringParam\" is not of type \"int\" but of type \"String\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 30, + alternativeLine = 43, + message = "Parameter \"stringParam\" is not of type \"long\" but of type \"String\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 32, + alternativeLine = 43, + message = "Parameter \"stringParam\" is not of type \"short\" but of type \"String\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 35, + alternativeLine = 43, + message = "Parameter \"genericTypedClass\" is not of type \"String\" " + + "but of type \"Class\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 36, + alternativeLine = 43, + message = "Parameter \"enumParam\" is not of type \"WrongAnnotateWithEnum\" " + + "but of type \"AnnotateWithEnum\" for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 40, + alternativeLine = 43, + message = "Parameter \"genericTypedClass\" is not of type \"ErroneousMapperWithWrongParameter\" " + + "but of type \"Class\" " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithWrongParameter.class, + line = 42, + alternativeLine = 43, + message = "Parameter \"value\" is not of type \"boolean\" " + + "but of type \"String\" for annotation \"CustomAnnotation\"." + ) + } + ) + @WithClasses({ + ErroneousMapperWithWrongParameter.class, CustomAnnotationWithParams.class, + CustomAnnotationWithParamsContainer.class, WrongAnnotateWithEnum.class, CustomAnnotation.class + }) + public void erroneousMapperWithWrongParameter() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 17, + alternativeLine = 43, + message = "Parameter \"stringParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 18, + alternativeLine = 43, + message = "Parameter \"booleanParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 19, + alternativeLine = 32, + message = "Parameter \"byteParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 20, + alternativeLine = 32, + message = "Parameter \"charParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 21, + alternativeLine = 32, + message = "Parameter \"doubleParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 22, + alternativeLine = 32, + message = "Parameter \"floatParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 23, + alternativeLine = 32, + message = "Parameter \"intParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 24, + alternativeLine = 32, + message = "Parameter \"longParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 25, + alternativeLine = 32, + message = "Parameter \"shortParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 26, + alternativeLine = 32, + message = "Parameter \"genericTypedClass\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMultipleArrayValuesMapper.class, + line = 27, + alternativeLine = 32, + message = "Parameter \"enumParam\" does not accept multiple values " + + "for annotation \"CustomAnnotationWithParams\"." + ) + } + ) + @WithClasses( { ErroneousMultipleArrayValuesMapper.class, CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class } ) + public void erroneousMapperUsingMultipleValuesInsteadOfSingle() { + } + + @ProcessorTest + @WithClasses( { MapperWithMissingAnnotationElementName.class, + CustomAnnotationWithTwoAnnotationElements.class } ) + public void mapperWithMissingAnnotationElementNameShouldCompile() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithMissingEnumClass.class, + line = 17, + message = "enumClass needs to be defined when using enums." + ) + } + ) + @WithClasses( { ErroneousMapperWithMissingEnumClass.class, CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class } ) + public void erroneousMapperWithMissingEnumClass() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithMissingEnums.class, + line = 17, + message = "enums needs to be defined when using enumClass." + ) + } + ) + @WithClasses( { ErroneousMapperWithMissingEnums.class, CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class } ) + public void erroneousMapperWithMissingEnums() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithRepeatOfNotRepeatableAnnotation.class, + line = 16, + alternativeLine = 17, + message = "Annotation \"CustomAnnotation\" is not repeatable." + ) + } + ) + @WithClasses( { ErroneousMapperWithRepeatOfNotRepeatableAnnotation.class, CustomAnnotation.class } ) + public void erroneousMapperWithRepeatOfNotRepeatableAnnotation() { + } + + @ProcessorTest + @WithClasses( { MapperWithRepeatableAnnotation.class, CustomRepeatableAnnotation.class, + CustomRepeatableAnnotationContainer.class } ) + public void mapperWithRepeatableAnnotationShouldCompile() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousMapperWithParameterRepeat.class, + line = 18, + message = "Parameter \"stringParam\" must not be defined more than once." + ) + } + ) + @WithClasses( { ErroneousMapperWithParameterRepeat.class, CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class } ) + public void erroneousMapperWithParameterRepeat() { + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.WARNING, + type = MapperWithIdenticalAnnotationRepeated.class, + line = 16, + alternativeLine = 17, + message = "Annotation \"CustomRepeatableAnnotation\" is already present " + + "with the same elements configuration." + ) + } + ) + @WithClasses( { MapperWithIdenticalAnnotationRepeated.class, CustomRepeatableAnnotation.class, + CustomRepeatableAnnotationContainer.class } ) + public void mapperWithIdenticalAnnotationRepeated() { + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotationWithRequiredParameter.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotationWithRequiredParameter.java new file mode 100644 index 0000000000..183b865928 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotationWithRequiredParameter.java @@ -0,0 +1,19 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( { TYPE, METHOD } ) +public @interface AnnotationWithRequiredParameter { + String required(); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotationWithoutElementNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotationWithoutElementNameMapper.java new file mode 100644 index 0000000000..8bb83ba562 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotationWithoutElementNameMapper.java @@ -0,0 +1,15 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +@Mapper +@AnnotateWith( value = CustomAnnotation.class, elements = @AnnotateWith.Element( strings = "value" ) ) +public interface AnnotationWithoutElementNameMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ClassMetaAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ClassMetaAnnotation.java new file mode 100644 index 0000000000..057cb384fb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ClassMetaAnnotation.java @@ -0,0 +1,20 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mapstruct.AnnotateWith; + +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.TYPE } ) +@AnnotateWith( CustomClassOnlyAnnotation.class ) +public @interface ClassMetaAnnotation { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotation.java new file mode 100644 index 0000000000..71ee6f12e3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotation.java @@ -0,0 +1,19 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( { TYPE, METHOD } ) +public @interface CustomAnnotation { + String value() default ""; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationOnlyAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationOnlyAnnotation.java new file mode 100644 index 0000000000..52d3e386a0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationOnlyAnnotation.java @@ -0,0 +1,18 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +@Target({ ANNOTATION_TYPE }) +public @interface CustomAnnotationOnlyAnnotation { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithParams.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithParams.java new file mode 100644 index 0000000000..bc25302fc1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithParams.java @@ -0,0 +1,64 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( { METHOD, TYPE } ) +@Repeatable( CustomAnnotationWithParamsContainer.class ) +public @interface CustomAnnotationWithParams { + String stringParam(); + + Class genericTypedClass() default CustomAnnotationWithParams.class; + + AnnotateWithEnum enumParam() default AnnotateWithEnum.EXISTING; + + byte byteParam() default 0x00; + + char charParam() default 'a'; + + double doubleParam() default 0.0; + + float floatParam() default 0.0f; + + int intParam() default 0; + + long longParam() default 0L; + + short shortParam() default 0; + + boolean booleanParam() default false; + + short[] shortArray() default {}; + + byte[] byteArray() default {}; + + int[] intArray() default {}; + + long[] longArray() default {}; + + float[] floatArray() default {}; + + double[] doubleArray() default {}; + + char[] charArray() default {}; + + boolean[] booleanArray() default {}; + + String[] stringArray() default {}; + + Class[] classArray() default {}; + + AnnotateWithEnum[] enumArray() default {}; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithParamsContainer.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithParamsContainer.java new file mode 100644 index 0000000000..51ed62885c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithParamsContainer.java @@ -0,0 +1,19 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( { TYPE, METHOD } ) +public @interface CustomAnnotationWithParamsContainer { + CustomAnnotationWithParams[] value() default {}; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithTwoAnnotationElements.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithTwoAnnotationElements.java new file mode 100644 index 0000000000..fb8c5c69f8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomAnnotationWithTwoAnnotationElements.java @@ -0,0 +1,20 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( { TYPE, METHOD } ) +public @interface CustomAnnotationWithTwoAnnotationElements { + String value() default ""; + boolean namedAnnotationElement() default false; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomClassOnlyAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomClassOnlyAnnotation.java new file mode 100644 index 0000000000..858c8fb52f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomClassOnlyAnnotation.java @@ -0,0 +1,18 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( { TYPE } ) +public @interface CustomClassOnlyAnnotation { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomMethodOnlyAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomMethodOnlyAnnotation.java new file mode 100644 index 0000000000..1dfe8e734e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomMethodOnlyAnnotation.java @@ -0,0 +1,18 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( { METHOD } ) +public @interface CustomMethodOnlyAnnotation { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedGenericClassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedGenericClassMapper.java new file mode 100644 index 0000000000..a320d56bba --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedGenericClassMapper.java @@ -0,0 +1,22 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, elements = { + @Element( name = "stringParam", strings = "test" ), + @Element( name = "genericTypedClass", classes = Mapper.class ) +} ) +public interface CustomNamedGenericClassMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedMapper.java new file mode 100644 index 0000000000..32241ab4ab --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedMapper.java @@ -0,0 +1,40 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, elements = { + @Element( name = "stringArray", strings = "test" ), + @Element( name = "stringParam", strings = "test" ), + @Element( name = "booleanArray", booleans = true ), + @Element( name = "booleanParam", booleans = true ), + @Element( name = "byteArray", bytes = 0x10 ), + @Element( name = "byteParam", bytes = 0x13 ), + @Element( name = "charArray", chars = 'd' ), + @Element( name = "charParam", chars = 'a' ), + @Element( name = "enumArray", enumClass = AnnotateWithEnum.class, enums = "EXISTING" ), + @Element( name = "enumParam", enumClass = AnnotateWithEnum.class, enums = "EXISTING" ), + @Element( name = "doubleArray", doubles = 0.3 ), + @Element( name = "doubleParam", doubles = 1.2 ), + @Element( name = "floatArray", floats = 0.3f ), + @Element( name = "floatParam", floats = 1.2f ), + @Element( name = "intArray", ints = 3 ), + @Element( name = "intParam", ints = 1 ), + @Element( name = "longArray", longs = 3L ), + @Element( name = "longParam", longs = 1L ), + @Element( name = "shortArray", shorts = 3 ), + @Element( name = "shortParam", shorts = 1 ) +} ) +public interface CustomNamedMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomRepeatableAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomRepeatableAnnotation.java new file mode 100644 index 0000000000..d7c0c85107 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomRepeatableAnnotation.java @@ -0,0 +1,21 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( { TYPE, METHOD } ) +@Repeatable( CustomRepeatableAnnotationContainer.class ) +public @interface CustomRepeatableAnnotation { + String value() default ""; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomRepeatableAnnotationContainer.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomRepeatableAnnotationContainer.java new file mode 100644 index 0000000000..8de3a8bf0a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomRepeatableAnnotationContainer.java @@ -0,0 +1,19 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( { TYPE, METHOD } ) +public @interface CustomRepeatableAnnotationContainer { + CustomRepeatableAnnotation[] value() default {}; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/DeprecateAndCustomMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/DeprecateAndCustomMapper.java new file mode 100644 index 0000000000..809ad5702a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/DeprecateAndCustomMapper.java @@ -0,0 +1,19 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( Deprecated.class ) +@AnnotateWith( CustomAnnotation.class ) +public interface DeprecateAndCustomMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithAnnotationOnlyOnClass.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithAnnotationOnlyOnClass.java new file mode 100644 index 0000000000..d617e8d0fc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithAnnotationOnlyOnClass.java @@ -0,0 +1,18 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +@AnnotateWith( value = CustomAnnotationOnlyAnnotation.class ) +public abstract class ErroneousMapperWithAnnotationOnlyOnClass { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithAnnotationOnlyOnInterface.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithAnnotationOnlyOnInterface.java new file mode 100644 index 0000000000..4e914b30a3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithAnnotationOnlyOnInterface.java @@ -0,0 +1,18 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +@AnnotateWith( value = CustomAnnotationOnlyAnnotation.class ) +public interface ErroneousMapperWithAnnotationOnlyOnInterface { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithClassOnMethod.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithClassOnMethod.java new file mode 100644 index 0000000000..d3d53c910a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithClassOnMethod.java @@ -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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface ErroneousMapperWithClassOnMethod { + + @AnnotateWith( value = CustomClassOnlyAnnotation.class ) + Target toString(Source value); + + class Source { + + } + + class Target { + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMethodOnClass.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMethodOnClass.java new file mode 100644 index 0000000000..262880d6f5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMethodOnClass.java @@ -0,0 +1,18 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomMethodOnlyAnnotation.class ) +public abstract class ErroneousMapperWithMethodOnClass { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMethodOnInterface.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMethodOnInterface.java new file mode 100644 index 0000000000..75f9039093 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMethodOnInterface.java @@ -0,0 +1,18 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomMethodOnlyAnnotation.class ) +public interface ErroneousMapperWithMethodOnInterface { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingEnumClass.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingEnumClass.java new file mode 100644 index 0000000000..6905a363f2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingEnumClass.java @@ -0,0 +1,22 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = { @Element( name = "enumParam", enums = "EXISTING" ), + @Element( name = "stringParam", strings = "required" ) } +) +public interface ErroneousMapperWithMissingEnumClass { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingEnums.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingEnums.java new file mode 100644 index 0000000000..fc7096d290 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingEnums.java @@ -0,0 +1,21 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @Element( name = "stringParam", strings = "required", enumClass = AnnotateWithEnum.class ) +) +public interface ErroneousMapperWithMissingEnums { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingParameter.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingParameter.java new file mode 100644 index 0000000000..40ffa53094 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithMissingParameter.java @@ -0,0 +1,18 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( AnnotationWithRequiredParameter.class ) +public interface ErroneousMapperWithMissingParameter { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithNonExistantEnum.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithNonExistantEnum.java new file mode 100644 index 0000000000..1ed85b990d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithNonExistantEnum.java @@ -0,0 +1,22 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = { @Element( name = "enumParam", enumClass = AnnotateWithEnum.class, enums = "NON_EXISTANT" ), + @Element( name = "stringParam", strings = "required" ) } +) +public interface ErroneousMapperWithNonExistantEnum { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithParameterRepeat.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithParameterRepeat.java new file mode 100644 index 0000000000..d75c7a4ee4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithParameterRepeat.java @@ -0,0 +1,22 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, elements = { + @Element( name = "stringParam", strings = "test" ), + @Element( name = "stringParam", strings = "otherValue" ) +} ) +public interface ErroneousMapperWithParameterRepeat { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithRepeatOfNotRepeatableAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithRepeatOfNotRepeatableAnnotation.java new file mode 100644 index 0000000000..be9596ea46 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithRepeatOfNotRepeatableAnnotation.java @@ -0,0 +1,19 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotation.class ) +@AnnotateWith( value = CustomAnnotation.class ) +public interface ErroneousMapperWithRepeatOfNotRepeatableAnnotation { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithTooManyParameterValues.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithTooManyParameterValues.java new file mode 100644 index 0000000000..4b6c20c39b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithTooManyParameterValues.java @@ -0,0 +1,21 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @Element( name = "stringParam", booleans = true, strings = "test" ) +) +public interface ErroneousMapperWithTooManyParameterValues { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithUnknownParameter.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithUnknownParameter.java new file mode 100644 index 0000000000..c705dc88f7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithUnknownParameter.java @@ -0,0 +1,21 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotation.class, + elements = @Element( name = "unknownParameter", strings = "unknown" ) +) +public interface ErroneousMapperWithUnknownParameter { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithWrongParameter.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithWrongParameter.java new file mode 100644 index 0000000000..3251122b22 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithWrongParameter.java @@ -0,0 +1,45 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @AnnotateWith.Element( name = "stringParam", booleans = true ) ) +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @AnnotateWith.Element( name = "stringParam", bytes = 0x12 ) ) +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @AnnotateWith.Element( name = "stringParam", chars = 'a' ) ) +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @AnnotateWith.Element( name = "stringParam", classes = CustomAnnotationWithParams.class ) ) +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @AnnotateWith.Element( name = "stringParam", doubles = 12.34 ) ) +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @AnnotateWith.Element( name = "stringParam", floats = 12.34f ) ) +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @AnnotateWith.Element( name = "stringParam", ints = 1234 ) ) +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @AnnotateWith.Element( name = "stringParam", longs = 1234L ) ) +@AnnotateWith( value = CustomAnnotationWithParams.class, + elements = @AnnotateWith.Element( name = "stringParam", shorts = 12 ) ) +@AnnotateWith( value = CustomAnnotationWithParams.class, elements = { + @AnnotateWith.Element( name = "stringParam", strings = "correctValue" ), + @AnnotateWith.Element( name = "genericTypedClass", strings = "wrong" ), + @AnnotateWith.Element( name = "enumParam", enumClass = WrongAnnotateWithEnum.class, enums = "EXISTING" ) +} ) +@AnnotateWith( value = CustomAnnotationWithParams.class, elements = { + @AnnotateWith.Element( name = "stringParam", strings = "correctValue" ), + @AnnotateWith.Element( name = "genericTypedClass", classes = ErroneousMapperWithWrongParameter.class ) +} ) +@AnnotateWith( value = CustomAnnotation.class, elements = @AnnotateWith.Element( booleans = true ) ) +public interface ErroneousMapperWithWrongParameter { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMultipleArrayValuesMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMultipleArrayValuesMapper.java new file mode 100644 index 0000000000..f6bf7629cd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMultipleArrayValuesMapper.java @@ -0,0 +1,31 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, elements = { + @Element( name = "stringParam", strings = { "test1", "test2" } ), + @Element( name = "booleanParam", booleans = { false, true } ), + @Element( name = "byteParam", bytes = { 0x08, 0x1f } ), + @Element( name = "charParam", chars = { 'b', 'c' } ), + @Element( name = "doubleParam", doubles = { 1.2, 3.4 } ), + @Element( name = "floatParam", floats = { 1.2f, 3.4f } ), + @Element( name = "intParam", ints = { 12, 34 } ), + @Element( name = "longParam", longs = { 12L, 34L } ), + @Element( name = "shortParam", shorts = { 12, 34 } ), + @Element( name = "genericTypedClass", classes = { Mapper.class, CustomAnnotationWithParams.class } ), + @Element( name = "enumParam", enumClass = AnnotateWithEnum.class, enums = { "EXISTING", "OTHER_EXISTING" } ) +} ) +public interface ErroneousMultipleArrayValuesMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithIdenticalAnnotationRepeated.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithIdenticalAnnotationRepeated.java new file mode 100644 index 0000000000..0959ee305a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithIdenticalAnnotationRepeated.java @@ -0,0 +1,19 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomRepeatableAnnotation.class, elements = @AnnotateWith.Element( strings = "identical" ) ) +@AnnotateWith( value = CustomRepeatableAnnotation.class, elements = @AnnotateWith.Element( strings = "identical" ) ) +public interface MapperWithIdenticalAnnotationRepeated { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithMissingAnnotationElementName.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithMissingAnnotationElementName.java new file mode 100644 index 0000000000..ed9523c545 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithMissingAnnotationElementName.java @@ -0,0 +1,21 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithTwoAnnotationElements.class, elements = { + @AnnotateWith.Element( strings = "unnamed annotation element" ), + @AnnotateWith.Element( name = "namedAnnotationElement", booleans = false ) +} ) +public abstract class MapperWithMissingAnnotationElementName { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithRepeatableAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithRepeatableAnnotation.java new file mode 100644 index 0000000000..b66ec68261 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MapperWithRepeatableAnnotation.java @@ -0,0 +1,19 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomRepeatableAnnotation.class ) +@AnnotateWith( value = CustomRepeatableAnnotation.class, elements = @AnnotateWith.Element( strings = "different" ) ) +public interface MapperWithRepeatableAnnotation { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MetaAnnotatedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MetaAnnotatedMapper.java new file mode 100644 index 0000000000..2dddb17f83 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MetaAnnotatedMapper.java @@ -0,0 +1,14 @@ +/* + * 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.annotatewith; + +import org.mapstruct.Mapper; + +@ClassMetaAnnotation +@Mapper +public interface MetaAnnotatedMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MethodMetaAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MethodMetaAnnotation.java new file mode 100644 index 0000000000..a93d54c283 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MethodMetaAnnotation.java @@ -0,0 +1,20 @@ +/* + * 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.annotatewith; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mapstruct.AnnotateWith; + +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.METHOD } ) +@AnnotateWith( CustomMethodOnlyAnnotation.class ) +public @interface MethodMetaAnnotation { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapper.java new file mode 100644 index 0000000000..2ccdbb0ea2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapper.java @@ -0,0 +1,31 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.AnnotateWith.Element; +import org.mapstruct.Mapper; + +/** + * @author Ben Zegveld + */ +@Mapper +@AnnotateWith( value = CustomAnnotationWithParams.class, elements = { + @Element( name = "stringArray", strings = { "test1", "test2" } ), + @Element( name = "booleanArray", booleans = { false, true } ), + @Element( name = "byteArray", bytes = { 0x08, 0x1f } ), + @Element( name = "charArray", chars = { 'b', 'c' } ), + @Element( name = "doubleArray", doubles = { 1.2, 3.4 } ), + @Element( name = "floatArray", floats = { 1.2f, 3.4f } ), + @Element( name = "intArray", ints = { 12, 34 } ), + @Element( name = "longArray", longs = { 12L, 34L } ), + @Element( name = "shortArray", shorts = { 12, 34 } ), + @Element( name = "classArray", classes = { Mapper.class, CustomAnnotationWithParams.class } ), + @Element( name = "stringParam", strings = "required parameter" ) +} ) +public interface MultipleArrayValuesMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/WrongAnnotateWithEnum.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/WrongAnnotateWithEnum.java new file mode 100644 index 0000000000..677a6cc6d5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/WrongAnnotateWithEnum.java @@ -0,0 +1,10 @@ +/* + * 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.annotatewith; + +public enum WrongAnnotateWithEnum { + EXISTING +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/CustomNamedMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/CustomNamedMapperImpl.java new file mode 100644 index 0000000000..cb01cfec85 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/CustomNamedMapperImpl.java @@ -0,0 +1,17 @@ +/* + * 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.annotatewith; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2022-06-06T16:48:18+0200", + comments = "version: , compiler: javac, environment: Java 11.0.12 (Azul Systems, Inc.)" +) +@CustomAnnotationWithParams(stringArray = "test", stringParam = "test", booleanArray = true, booleanParam = true, byteArray = 16, byteParam = 19, charArray = 'd', charParam = 'a', enumArray = AnnotateWithEnum.EXISTING, enumParam = AnnotateWithEnum.EXISTING, doubleArray = 0.3, doubleParam = 1.2, floatArray = 0.300000011920929f, floatParam = 1.2000000476837158f, intArray = 3, intParam = 1, longArray = 3L, longParam = 1L, shortArray = 3, shortParam = 1) +public class CustomNamedMapperImpl implements CustomNamedMapper { +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapperImpl.java new file mode 100644 index 0000000000..fe9bb93f13 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapperImpl.java @@ -0,0 +1,18 @@ +/* + * 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.annotatewith; + +import javax.annotation.processing.Generated; +import org.mapstruct.Mapper; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2022-06-06T16:48:23+0200", + comments = "version: , compiler: javac, environment: Java 11.0.12 (Azul Systems, Inc.)" +) +@CustomAnnotationWithParams(stringArray = { "test1", "test2" }, booleanArray = { false, true }, byteArray = { 8, 31 }, charArray = { 'b', 'c' }, doubleArray = { 1.2, 3.4 }, floatArray = { 1.2000000476837158f, 3.4000000953674316f }, intArray = { 12, 34 }, longArray = { 12L, 34L }, shortArray = { 12, 34 }, classArray = { Mapper.class, CustomAnnotationWithParams.class }, stringParam = "required parameter") +public class MultipleArrayValuesMapperImpl implements MultipleArrayValuesMapper { +} From 17997ef617e6ba5d9b9c70e7e5e8484cd82dbc53 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sat, 20 Aug 2022 13:01:47 +0200 Subject: [PATCH 286/597] #2901: Fix `@TargetType` annotation on a `@Condition` annotated method for a Collection value --- .../ap/internal/model/macro/CommonMacros.ftl | 3 ++- ...itionWithTargetTypeOnCollectionMapper.java | 23 +++++++++++++++++++ .../ap/test/bugs/_2901/Issue2901Test.java | 22 ++++++++++++++++++ .../mapstruct/ap/test/bugs/_2901/Source.java | 21 +++++++++++++++++ .../mapstruct/ap/test/bugs/_2901/Target.java | 21 +++++++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/ConditionWithTargetTypeOnCollectionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Issue2901Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Target.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index e5fb970605..bce28ebe19 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -60,7 +60,8 @@ <#macro handleLocalVarNullCheck needs_explicit_local_var> <#if sourcePresenceCheckerReference??> if ( <@includeModel object=sourcePresenceCheckerReference - targetPropertyName=ext.targetPropertyName/> ) { + targetType=ext.targetType + targetPropertyName=ext.targetPropertyName /> ) { <#if needs_explicit_local_var> <@includeModel object=nullCheckLocalVarType/> ${nullCheckLocalVarName} = <@lib.handleAssignment/>; <#nested> diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/ConditionWithTargetTypeOnCollectionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/ConditionWithTargetTypeOnCollectionMapper.java new file mode 100644 index 0000000000..7e833cfc53 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/ConditionWithTargetTypeOnCollectionMapper.java @@ -0,0 +1,23 @@ +/* + * 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._2901; + +import java.util.List; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.TargetType; + +@Mapper +public interface ConditionWithTargetTypeOnCollectionMapper { + + Target map(Source source); + + @Condition + default boolean check(List test, @TargetType Class type) { + return type.isInstance( test ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Issue2901Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Issue2901Test.java new file mode 100644 index 0000000000..3ae2c3db86 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Issue2901Test.java @@ -0,0 +1,22 @@ +/* + * 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._2901; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Ben Zegveld + */ +@IssueKey( "2901" ) +class Issue2901Test { + + @ProcessorTest + @WithClasses( { Source.class, Target.class, ConditionWithTargetTypeOnCollectionMapper.class } ) + void shouldCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Source.java new file mode 100644 index 0000000000..4eb50d58fb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Source.java @@ -0,0 +1,21 @@ +/* + * 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._2901; + +import java.util.List; + +public class Source { + + private List field; + + public List getField() { + return field; + } + + public void setField(List field) { + this.field = field; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Target.java new file mode 100644 index 0000000000..b4c5299f35 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2901/Target.java @@ -0,0 +1,21 @@ +/* + * 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._2901; + +import java.util.List; + +public class Target { + + private List field; + + public List getField() { + return field; + } + + public void setField(List field) { + this.field = field; + } +} From 46900cabde211f885521b9b57d1d3de3f07a849a Mon Sep 17 00:00:00 2001 From: Prasanth Omanakuttan Date: Sat, 20 Aug 2022 17:07:43 +0530 Subject: [PATCH 287/597] Update Typos in javadoc (#2958) --- .../main/java/org/mapstruct/InheritInverseConfiguration.java | 4 ++-- .../org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java | 2 +- .../mapstruct/ap/spi/FreeBuilderAccessorNamingStrategy.java | 4 ++-- .../mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java b/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java index ac582a5dfa..b659b7f37a 100644 --- a/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java +++ b/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java @@ -81,8 +81,8 @@ public @interface InheritInverseConfiguration { /** - * The name of the inverse mapping method to inherit the mappings from. Needs only to be specified in case more than - * one inverse method with matching source and target type exists. + * The name of the inverse mapping method to inherit the mappings from. Needs to be specified only in case more than + * one inverse method exists with a matching source and target type exists. * * @return The name of the inverse mapping method to inherit the mappings from. */ diff --git a/processor/src/main/java/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java b/processor/src/main/java/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java index 8ac41395bf..6b9e079523 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java @@ -15,7 +15,7 @@ *

    * This contract will be queried by MapStruct when examining types referenced by mappers to be generated, most notably * the source and target types of mapping methods. If at least one AST-modifying processor announces further changes to - * such type, the generation of the affected mapper(s) will be deferred to a future round in the annnotation processing + * such type, the generation of the affected mapper(s) will be deferred to a future round in the annotation processing * cycle. *

    * Implementations are discovered via the service loader, i.e. a JAR providing an AST-modifying processor needs to diff --git a/processor/src/main/java/org/mapstruct/ap/spi/FreeBuilderAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/FreeBuilderAccessorNamingStrategy.java index 92fef3a1be..dc14e20de6 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/FreeBuilderAccessorNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/FreeBuilderAccessorNamingStrategy.java @@ -20,8 +20,8 @@ *

  • {@code mergeFrom(Target.Builder)}
  • * *

    - * When the JavaBean convention is not used with FreeBuilder then the getters are non standard and MapStruct - * won't recognize them. Therefore one needs to use the JavaBean convention in which the fluent setters + * When the JavaBean convention is not used with FreeBuilder then the getters are non-standard and MapStruct + * won't recognize them. Therefore, one needs to use the JavaBean convention in which the fluent setters * start with {@code set}. * * @author Filip Hrisafov diff --git a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java index 69c66cdd28..100798e063 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java @@ -10,9 +10,9 @@ import org.mapstruct.util.Experimental; /** - * Accesor naming strategy for Immutables. + * Accessor naming strategy for Immutables. * The generated Immutables also have a from that works as a copy. Our default strategy considers this method - * as a setter with a name {@code from}. Therefore we are ignoring it. + * as a setter with a name {@code from}. Therefore, we are ignoring it. * * @author Filip Hrisafov */ From 54321d6e663d656a0d33bbddb3a7e030f8faa74c Mon Sep 17 00:00:00 2001 From: Hakan Date: Sat, 20 Aug 2022 15:23:32 +0200 Subject: [PATCH 288/597] #2839 Keep thrown types when creating a new ForgedMethod with the same arguments This fixes a compilation error when mapping fields with the same type due to not wrapping in a `try-catch` block --- .../ap/internal/model/ForgedMethod.java | 2 +- .../org/mapstruct/ap/test/bugs/_2839/Car.java | 36 ++++++++++++ .../mapstruct/ap/test/bugs/_2839/CarDto.java | 36 ++++++++++++ .../ap/test/bugs/_2839/CarMapper.java | 25 +++++++++ .../org/mapstruct/ap/test/bugs/_2839/Id.java | 28 ++++++++++ .../test/bugs/_2839/Issue2839Exception.java | 16 ++++++ .../ap/test/bugs/_2839/Issue2839Test.java | 56 +++++++++++++++++++ 7 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Car.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/CarDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/CarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Id.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Issue2839Exception.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Issue2839Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java index a1f5b091ce..a33bc7520e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java @@ -196,7 +196,7 @@ private ForgedMethod(String name, Type sourceType, Type returnType, List(); + this.thrownTypes = forgedMethod.thrownTypes; this.history = forgedMethod.history; this.sourceParameters = Parameter.getSourceParameters( parameters ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Car.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Car.java new file mode 100644 index 0000000000..9419223a42 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Car.java @@ -0,0 +1,36 @@ +/* + * 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._2839; + +import java.util.List; + +/** + * @author Hakan Özkan + */ +public final class Car { + + private final Id id; + private final List seatIds; + private final List tireIds; + + public Car(Id id, List seatIds, List tireIds) { + this.id = id; + this.seatIds = seatIds; + this.tireIds = tireIds; + } + + public Id getId() { + return id; + } + + public List getSeatIds() { + return seatIds; + } + + public List getTireIds() { + return tireIds; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/CarDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/CarDto.java new file mode 100644 index 0000000000..68741ebc92 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/CarDto.java @@ -0,0 +1,36 @@ +/* + * 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._2839; + +import java.util.List; + +/** + * @author Hakan Özkan + */ +public final class CarDto { + + private final String id; + private final List seatIds; + private final List tireIds; + + public CarDto(String id, List seatIds, List tireIds) { + this.id = id; + this.seatIds = seatIds; + this.tireIds = tireIds; + } + + public String getId() { + return id; + } + + public List getSeatIds() { + return seatIds; + } + + public List getTireIds() { + return tireIds; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/CarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/CarMapper.java new file mode 100644 index 0000000000..e535935d21 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/CarMapper.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.ap.test.bugs._2839; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Hakan Özkan + */ +@Mapper +public abstract class CarMapper { + + public static final CarMapper MAPPER = Mappers.getMapper( CarMapper.class ); + + public abstract Car toEntity(CarDto dto); + + protected Id mapId(String id) throws Issue2839Exception { + throw new Issue2839Exception("For id " + id); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Id.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Id.java new file mode 100644 index 0000000000..5bb9a29dd7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Id.java @@ -0,0 +1,28 @@ +/* + * 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._2839; + +import java.util.UUID; + +/** + * @author Hakan Özkan + */ +public class Id { + + private final UUID id; + + public Id() { + this.id = UUID.randomUUID(); + } + + public Id(UUID id) { + this.id = id; + } + + public UUID getId() { + return id; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Issue2839Exception.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Issue2839Exception.java new file mode 100644 index 0000000000..91f02014d6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Issue2839Exception.java @@ -0,0 +1,16 @@ +/* + * 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._2839; + +/** + * @author Hakan Özkan + */ +public class Issue2839Exception extends Exception { + + public Issue2839Exception(String message) { + super( message ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Issue2839Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Issue2839Test.java new file mode 100644 index 0000000000..ed61c39a63 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2839/Issue2839Test.java @@ -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._2839; + +import java.util.Collections; + +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.assertThatThrownBy; + +/** + * @author Hakan Özkan + */ +@IssueKey("2839") +@WithClasses({ + Car.class, + CarDto.class, + CarMapper.class, + Id.class, + Issue2839Exception.class, +}) +public class Issue2839Test { + + @ProcessorTest + void shouldCompile() { + CarDto car1 = new CarDto( + "carId", + Collections.singletonList( "seatId" ), + Collections.singletonList( "tireId" ) + ); + assertThatThrownBy( () -> CarMapper.MAPPER.toEntity( car1 ) ) + .isExactlyInstanceOf( RuntimeException.class ) + .getCause() + .isInstanceOf( Issue2839Exception.class ) + .hasMessage( "For id seatId" ); + + CarDto car2 = new CarDto( "carId", Collections.emptyList(), Collections.singletonList( "tireId" ) ); + assertThatThrownBy( () -> CarMapper.MAPPER.toEntity( car2 ) ) + .isExactlyInstanceOf( RuntimeException.class ) + .getCause() + .isInstanceOf( Issue2839Exception.class ) + .hasMessage( "For id tireId" ); + + CarDto car3 = new CarDto( "carId", Collections.emptyList(), Collections.emptyList() ); + assertThatThrownBy( () -> CarMapper.MAPPER.toEntity( car3 ) ) + .isExactlyInstanceOf( RuntimeException.class ) + .getCause() + .isInstanceOf( Issue2839Exception.class ) + .hasMessage( "For id carId" ); + } +} From 4118a446306ab3d413285ccf1e0c709aa55de4c2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Aug 2022 10:56:16 +0200 Subject: [PATCH 289/597] #2974 Fix typos in documentation Closes #2974 --- .../main/asciidoc/chapter-10-advanced-mapping-options.asciidoc | 2 +- documentation/src/main/asciidoc/chapter-2-set-up.asciidoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index decee792d0..9c1b2243eb 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -292,7 +292,7 @@ The source presence checker name can be changed in the MapStruct service provide [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 +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 `NullValueCheckStrategy` to avoid addition of `null` to the target collection or map. ==== diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index d95983e840..48b352bd94 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -130,7 +130,7 @@ You can find a complete example in the https://github.com/mapstruct/mapstruct-ex The MapStruct code generator can be configured using _annotation processor options_. -When invoking javac directly, these options are passed to the compiler in the form _-Akey=value_. When using MapStruct via Maven, any processor options can be passed using an `options` element within the configuration of the Maven processor plug-in like this: +When invoking javac directly, these options are passed to the compiler in the form _-Akey=value_. When using MapStruct via Maven, any processor options can be passed using `compilerArgs` within the configuration of the Maven processor plug-in like this: .Maven configuration ==== From 3e0c62ac36e6adbf689031a62be3b96457348740 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Aug 2022 11:26:42 +0200 Subject: [PATCH 290/597] Publish snapshots when on main --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 40999c680a..54cf0b1076 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,7 +43,7 @@ jobs: - name: 'Upload coverage to Codecov' uses: codecov/codecov-action@v2 - name: 'Publish Snapshots' - if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'mapstruct/mapstruct' + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mapstruct/mapstruct' run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy linux-jdk-8: name: 'Linux JDK 8' From ef4c26b075896dd564e63235ae98dbaedb7d512f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 24 Aug 2022 18:36:43 +0200 Subject: [PATCH 291/597] #2949 Do not inverse inherit BeanMapping#ignoreUnmappedSourceProperties --- .../model/source/BeanMappingOptions.java | 11 ++-- .../ap/test/bugs/_2949/Issue2949Mapper.java | 59 +++++++++++++++++++ .../ap/test/bugs/_2949/Issue2949Test.java | 35 +++++++++++ 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2949/Issue2949Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2949/Issue2949Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index cc03878362..60aac32e54 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -34,6 +34,7 @@ public class BeanMappingOptions extends DelegatingOptions { private final SelectionParameters selectionParameters; + private final List ignoreUnmappedSourceProperties; private final BeanMappingGem beanMapping; /** @@ -46,6 +47,7 @@ public class BeanMappingOptions extends DelegatingOptions { public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) { BeanMappingOptions options = new BeanMappingOptions( SelectionParameters.forInheritance( beanMapping.selectionParameters ), + Collections.emptyList(), beanMapping.beanMapping, beanMapping ); @@ -57,7 +59,7 @@ public static BeanMappingOptions getInstanceOn(BeanMappingGem beanMapping, Mappe TypeUtils typeUtils, TypeFactory typeFactory ) { if ( beanMapping == null || !isConsistent( beanMapping, method, messager ) ) { - BeanMappingOptions options = new BeanMappingOptions( null, null, mapperOptions ); + BeanMappingOptions options = new BeanMappingOptions( null, Collections.emptyList(), null, mapperOptions ); return options; } @@ -77,6 +79,7 @@ public static BeanMappingOptions getInstanceOn(BeanMappingGem beanMapping, Mappe //TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support? BeanMappingOptions options = new BeanMappingOptions( selectionParameters, + beanMapping.ignoreUnmappedSourceProperties().get(), beanMapping, mapperOptions ); @@ -104,10 +107,12 @@ private static boolean isConsistent(BeanMappingGem gem, ExecutableElement method } private BeanMappingOptions(SelectionParameters selectionParameters, + List ignoreUnmappedSourceProperties, BeanMappingGem beanMapping, DelegatingOptions next) { super( next ); this.selectionParameters = selectionParameters; + this.ignoreUnmappedSourceProperties = ignoreUnmappedSourceProperties; this.beanMapping = beanMapping; } @@ -188,9 +193,7 @@ public boolean isignoreByDefault() { } public List getIgnoreUnmappedSourceProperties() { - return Optional.ofNullable( beanMapping ).map( BeanMappingGem::ignoreUnmappedSourceProperties ) - .map( GemValue::get ) - .orElse( Collections.emptyList() ); + return ignoreUnmappedSourceProperties; } public AnnotationMirror getMirror() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2949/Issue2949Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2949/Issue2949Mapper.java new file mode 100644 index 0000000000..553563066e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2949/Issue2949Mapper.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.bugs._2949; + +import org.mapstruct.BeanMapping; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface Issue2949Mapper { + + Issue2949Mapper INSTANCE = Mappers.getMapper( Issue2949Mapper.class ); + + @Mapping( target = "property1", ignore = true) + @InheritInverseConfiguration + Source toSource(Target target); + + @BeanMapping(ignoreUnmappedSourceProperties = { "property1" }) + Target toTarget(Source source); + + class Target { + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Source { + private final String value; + private final String property1; + + public Source(String value, String property1) { + this.value = value; + this.property1 = property1; + } + + public String getValue() { + return value; + } + + public String getProperty1() { + return property1; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2949/Issue2949Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2949/Issue2949Test.java new file mode 100644 index 0000000000..2a277a4bfd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2949/Issue2949Test.java @@ -0,0 +1,35 @@ +/* + * 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._2949; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Issue2949Mapper.class +}) +class Issue2949Test { + + @ProcessorTest + void shouldCorrectlyInheritInverseBeanMappingWithIgnoreUnmappedSourceProeprties() { + Issue2949Mapper.Target target = Issue2949Mapper.INSTANCE.toTarget( new Issue2949Mapper.Source( + "test", + "first" + ) ); + + assertThat( target.getValue() ).isEqualTo( "test" ); + + Issue2949Mapper.Source source = Issue2949Mapper.INSTANCE.toSource( target ); + + assertThat( source.getValue() ).isEqualTo( "test" ); + assertThat( source.getProperty1() ).isNull(); + } +} From 874bf1fd2c44065376483c2a9a700455aa0c31ce Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 24 Aug 2022 18:38:44 +0200 Subject: [PATCH 292/597] #2950 Add support for Jakarta CDI --- .../java/org/mapstruct/MappingConstants.java | 13 ++++- .../test/resources/fullFeatureTest/pom.xml | 4 ++ parent/pom.xml | 5 ++ processor/pom.xml | 5 ++ .../ap/internal/gem/MappingConstantsGem.java | 2 + .../processor/CdiComponentProcessor.java | 26 ++++++++- .../JakartaCdiComponentProcessor.java | 52 +++++++++++++++++ ...p.internal.processor.ModelElementProcessor | 1 + .../mapstruct/ap/test/gem/ConstantTest.java | 4 +- ...diDefaultCompileOptionFieldMapperTest.java | 54 ++++++++++++++++++ ...merCdiDefaultCompileOptionFieldMapper.java | 21 +++++++ ...derCdiDefaultCompileOptionFieldMapper.java | 26 +++++++++ ...merCdiDefaultCompileOptionFieldMapper.java | 21 +++++++ ...derCdiDefaultCompileOptionFieldMapper.java | 26 +++++++++ ...diDefaultCompileOptionFieldMapperTest.java | 56 +++++++++++++++++++ ...diDefaultCompileOptionFieldMapperTest.java | 54 ++++++++++++++++++ ...rtaCdiDefaultCompileOptionFieldMapper.java | 21 +++++++ ...rtaCdiDefaultCompileOptionFieldMapper.java | 26 +++++++++ ...diDefaultCompileOptionFieldMapperTest.java | 55 ++++++++++++++++++ ...diDefaultCompileOptionFieldMapperTest.java | 54 ++++++++++++++++++ .../org/mapstruct/ap/testutil/WithCdi.java | 28 ++++++++++ .../mapstruct/ap/testutil/WithJakartaCdi.java | 28 ++++++++++ 22 files changed, 578 insertions(+), 4 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaCdiComponentProcessor.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/CdiDefaultCompileOptionFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/CustomerCdiDefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/GenderCdiDefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/CustomerCdiDefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/GenderCdiDefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/JakartaCdiCdiDefaultCompileOptionFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/CustomerJakartaCdiDefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/GenderJakartaCdiDefaultCompileOptionFieldMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/JakartaCdiDefaultCompileOptionFieldMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithCdi.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaCdi.java diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java index ce2438298c..3d3d8a4c77 100644 --- a/core/src/main/java/org/mapstruct/MappingConstants.java +++ b/core/src/main/java/org/mapstruct/MappingConstants.java @@ -109,7 +109,12 @@ private ComponentModel() { public static final String DEFAULT = "default"; /** - * The generated mapper is an application-scoped CDI bean and can be retrieved via @Inject + * The generated mapper is an application-scoped CDI bean and can be retrieved via @Inject. + * The annotations are either from {@code javax} or {@code jakarta}. + * Priority have the {@code javax} annotations. + * In case you want to only use Jakarta then use {@link #JAKARTA_CDI}. + * + * @see #JAKARTA_CDI */ public static final String CDI = "cdi"; @@ -138,6 +143,12 @@ private ComponentModel() { */ public static final String JAKARTA = "jakarta"; + /** + * The generated mapper is an application-scoped Jakarta CDI bean and can be retrieved via @Inject. + * @see #CDI + */ + public static final String JAKARTA_CDI = "jakarta-cdi"; + } } diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index ec26f45ce2..62f7986e9c 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -64,6 +64,10 @@ jakarta.inject jakarta.inject-api + + jakarta.enterprise + jakarta.enterprise.cdi-api + diff --git a/parent/pom.xml b/parent/pom.xml index 1c031a7b71..426405b4b7 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -160,6 +160,11 @@ cdi-api 2.0.SP1 + + jakarta.enterprise + jakarta.enterprise.cdi-api + 4.0.1 + javax.inject javax.inject diff --git a/processor/pom.xml b/processor/pom.xml index 6f2bb6495c..ff7864f32e 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -89,6 +89,11 @@ jakarta.inject-api test + + jakarta.enterprise + jakarta.enterprise.cdi-api + test + diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java index cb6d49c0cb..bc58024ca3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/MappingConstantsGem.java @@ -51,6 +51,8 @@ private ComponentModelGem() { public static final String JSR330 = "jsr330"; public static final String JAKARTA = "jakarta"; + + public static final String JAKARTA_CDI = "jakarta-cdi"; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/CdiComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/CdiComponentProcessor.java index 74ff2b118b..d8f136034b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/CdiComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/CdiComponentProcessor.java @@ -12,6 +12,8 @@ import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Mapper; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.AnnotationProcessingException; /** * A {@link ModelElementProcessor} which converts the given {@link Mapper} @@ -30,13 +32,13 @@ protected String getComponentModelIdentifier() { @Override protected List getTypeAnnotations(Mapper mapper) { return Collections.singletonList( - new Annotation( getTypeFactory().getType( "javax.enterprise.context.ApplicationScoped" ) ) + new Annotation( getType( "ApplicationScoped" ) ) ); } @Override protected List getMapperReferenceAnnotations() { - return Arrays.asList( new Annotation( getTypeFactory().getType( "javax.inject.Inject" ) ) ); + return Arrays.asList( new Annotation( getType( "Inject" ) ) ); } @Override @@ -48,4 +50,24 @@ protected boolean requiresGenerationOfDecoratorClass() { protected boolean additionalPublicEmptyConstructor() { return true; } + + private Type getType(String simpleName) { + String javaxPrefix = "javax.inject."; + String jakartaPrefix = "jakarta.inject."; + if ( "ApplicationScoped".equals( simpleName ) ) { + javaxPrefix = "javax.enterprise.context."; + jakartaPrefix = "jakarta.enterprise.context."; + } + if ( getTypeFactory().isTypeAvailable( javaxPrefix + simpleName ) ) { + return getTypeFactory().getType( javaxPrefix + simpleName ); + } + + if ( getTypeFactory().isTypeAvailable( jakartaPrefix + simpleName ) ) { + return getTypeFactory().getType( jakartaPrefix + simpleName ); + } + + throw new AnnotationProcessingException( + "Couldn't find any of the CDI or Jakarta CDI Dependency types." + + " Are you missing a dependency on your classpath?" ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaCdiComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaCdiComponentProcessor.java new file mode 100644 index 0000000000..11c765668e --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaCdiComponentProcessor.java @@ -0,0 +1,52 @@ +/* + * 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.internal.processor; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.mapstruct.ap.internal.gem.MappingConstantsGem; +import org.mapstruct.ap.internal.model.Annotation; +import org.mapstruct.ap.internal.model.Mapper; + +/** + * A {@link ModelElementProcessor} which converts the given {@link Mapper} + * object into an application-scoped Jakarta CDI bean in case Jakarta CDI + * is configured as the target component model for this mapper. + * + * @author Filip Hrisafov + */ +public class JakartaCdiComponentProcessor extends AnnotationBasedComponentModelProcessor { + + @Override + protected String getComponentModelIdentifier() { + return MappingConstantsGem.ComponentModelGem.JAKARTA_CDI; + } + + @Override + protected List getTypeAnnotations(Mapper mapper) { + return Collections.singletonList( + new Annotation( getTypeFactory().getType( "jakarta.enterprise.context.ApplicationScoped" ) ) + ); + } + + @Override + protected List getMapperReferenceAnnotations() { + return Arrays.asList( new Annotation( getTypeFactory().getType( "jakarta.inject.Inject" ) ) ); + } + + @Override + protected boolean requiresGenerationOfDecoratorClass() { + return false; + } + + @Override + protected boolean additionalPublicEmptyConstructor() { + return true; + } + +} diff --git a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor index cab1d83ed7..d5234fca5b 100644 --- a/processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor +++ b/processor/src/main/resources/META-INF/services/org.mapstruct.ap.internal.processor.ModelElementProcessor @@ -3,6 +3,7 @@ # Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 org.mapstruct.ap.internal.processor.CdiComponentProcessor +org.mapstruct.ap.internal.processor.JakartaCdiComponentProcessor org.mapstruct.ap.internal.processor.Jsr330ComponentProcessor org.mapstruct.ap.internal.processor.JakartaComponentProcessor org.mapstruct.ap.internal.processor.MapperCreationProcessor diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java index 548ee61f2e..25489a5b6b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/ConstantTest.java @@ -34,7 +34,7 @@ public void constantsShouldBeEqual() { } @Test - public void componentModelContantsShouldBeEqual() { + public void componentModelConstantsShouldBeEqual() { assertThat( MappingConstants.ComponentModel.DEFAULT ) .isEqualTo( MappingConstantsGem.ComponentModelGem.DEFAULT ); assertThat( MappingConstants.ComponentModel.CDI ).isEqualTo( MappingConstantsGem.ComponentModelGem.CDI ); @@ -42,5 +42,7 @@ public void componentModelContantsShouldBeEqual() { assertThat( MappingConstants.ComponentModel.JSR330 ).isEqualTo( MappingConstantsGem.ComponentModelGem.JSR330 ); assertThat( MappingConstants.ComponentModel.JAKARTA ) .isEqualTo( MappingConstantsGem.ComponentModelGem.JAKARTA ); + assertThat( MappingConstants.ComponentModel.JAKARTA_CDI ) + .isEqualTo( MappingConstantsGem.ComponentModelGem.JAKARTA_CDI ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/CdiDefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/CdiDefaultCompileOptionFieldMapperTest.java new file mode 100644 index 0000000000..bf8b292ec7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/CdiDefaultCompileOptionFieldMapperTest.java @@ -0,0 +1,54 @@ +/* + * 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.injectionstrategy.cdi._default; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithCdi; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test field injection for component model jakarta-cdi. + * Default value option mapstruct.defaultInjectionStrategy is "field" + * + * @author Filip Hrisafov + */ +@IssueKey("2950") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerCdiDefaultCompileOptionFieldMapper.class, + GenderCdiDefaultCompileOptionFieldMapper.class +}) +@WithCdi +public class CdiDefaultCompileOptionFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveFieldInjection() { + generatedSource.forMapper( CustomerCdiDefaultCompileOptionFieldMapper.class ) + .content() + .contains( "import javax.enterprise.context.ApplicationScoped;" ) + .contains( "import javax.inject.Inject;" ) + .contains( "@Inject" + lineSeparator() + " private GenderCdiDefaultCompileOptionFieldMapper" ) + .contains( "@ApplicationScoped" + lineSeparator() + "public class" ) + .doesNotContain( "public CustomerCdiDefaultCompileOptionFieldMapperImpl(" ) + .doesNotContain( "jakarta.inject" ) + .doesNotContain( "jakarta.enterprise" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/CustomerCdiDefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/CustomerCdiDefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..ed0ed0a76e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/CustomerCdiDefaultCompileOptionFieldMapper.java @@ -0,0 +1,21 @@ +/* + * 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.injectionstrategy.cdi._default; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.CDI, + uses = GenderCdiDefaultCompileOptionFieldMapper.class) +public interface CustomerCdiDefaultCompileOptionFieldMapper { + + CustomerDto asTarget(CustomerEntity customerEntity); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/GenderCdiDefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/GenderCdiDefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..14d6a2e0d7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/_default/GenderCdiDefaultCompileOptionFieldMapper.java @@ -0,0 +1,26 @@ +/* + * 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.injectionstrategy.cdi._default; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.CDI) +public interface GenderCdiDefaultCompileOptionFieldMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/CustomerCdiDefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/CustomerCdiDefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..4c893b7486 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/CustomerCdiDefaultCompileOptionFieldMapper.java @@ -0,0 +1,21 @@ +/* + * 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.injectionstrategy.cdi.jakarta; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.CDI, + uses = GenderCdiDefaultCompileOptionFieldMapper.class) +public interface CustomerCdiDefaultCompileOptionFieldMapper { + + CustomerDto asTarget(CustomerEntity customerEntity); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/GenderCdiDefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/GenderCdiDefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..8664b0b433 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/GenderCdiDefaultCompileOptionFieldMapper.java @@ -0,0 +1,26 @@ +/* + * 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.injectionstrategy.cdi.jakarta; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.CDI) +public interface GenderCdiDefaultCompileOptionFieldMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest.java new file mode 100644 index 0000000000..55480399a3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest.java @@ -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.injectionstrategy.cdi.jakarta; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithCdi; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaCdi; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test field injection for component model jsr330. + * Default value option mapstruct.defaultInjectionStrategy is "field" + * + * @author Filip Hrisafov + */ +@IssueKey("2950") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerCdiDefaultCompileOptionFieldMapper.class, + GenderCdiDefaultCompileOptionFieldMapper.class +}) +@WithJakartaCdi +@WithCdi +public class JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveCdiInjection() { + generatedSource.forMapper( CustomerCdiDefaultCompileOptionFieldMapper.class ) + .content() + .contains( "import javax.enterprise.context.ApplicationScoped;" ) + .contains( "import javax.inject.Inject;" ) + .contains( "@Inject" + lineSeparator() + " private GenderCdiDefaultCompileOptionFieldMapper" ) + .contains( "@ApplicationScoped" + lineSeparator() + "public class" ) + .doesNotContain( "public CustomerCdiDefaultCompileOptionFieldMapperImpl(" ) + .doesNotContain( "jakarta.inject" ) + .doesNotContain( "jakarta.enterprise" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/JakartaCdiCdiDefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/JakartaCdiCdiDefaultCompileOptionFieldMapperTest.java new file mode 100644 index 0000000000..953747e404 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/cdi/jakarta/JakartaCdiCdiDefaultCompileOptionFieldMapperTest.java @@ -0,0 +1,54 @@ +/* + * 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.injectionstrategy.cdi.jakarta; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaCdi; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test field injection for component model jsr330. + * Default value option mapstruct.defaultInjectionStrategy is "field" + * + * @author Filip Hrisafov + */ +@IssueKey("2950") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerCdiDefaultCompileOptionFieldMapper.class, + GenderCdiDefaultCompileOptionFieldMapper.class +}) +@WithJakartaCdi +public class JakartaCdiCdiDefaultCompileOptionFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaCdiInjection() { + generatedSource.forMapper( CustomerCdiDefaultCompileOptionFieldMapper.class ) + .content() + .contains( "import jakarta.enterprise.context.ApplicationScoped;" ) + .contains( "import jakarta.inject.Inject;" ) + .contains( "@Inject" + lineSeparator() + " private GenderCdiDefaultCompileOptionFieldMapper" ) + .contains( "@ApplicationScoped" + lineSeparator() + "public class" ) + .doesNotContain( "public CustomerCdiDefaultCompileOptionFieldMapperImpl(" ) + .doesNotContain( "javax.inject" ) + .doesNotContain( "javax.enterprise" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/CustomerJakartaCdiDefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/CustomerJakartaCdiDefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..9ec0709f52 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/CustomerJakartaCdiDefaultCompileOptionFieldMapper.java @@ -0,0 +1,21 @@ +/* + * 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.injectionstrategy.jakarta_cdi._default; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA_CDI, + uses = GenderJakartaCdiDefaultCompileOptionFieldMapper.class) +public interface CustomerJakartaCdiDefaultCompileOptionFieldMapper { + + CustomerDto asTarget(CustomerEntity customerEntity); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/GenderJakartaCdiDefaultCompileOptionFieldMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/GenderJakartaCdiDefaultCompileOptionFieldMapper.java new file mode 100644 index 0000000000..0c1aef6ec6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/GenderJakartaCdiDefaultCompileOptionFieldMapper.java @@ -0,0 +1,26 @@ +/* + * 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.injectionstrategy.jakarta_cdi._default; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Filip Hrisafov + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330) +public interface GenderJakartaCdiDefaultCompileOptionFieldMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest.java new file mode 100644 index 0000000000..b542c99c2a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest.java @@ -0,0 +1,55 @@ +/* + * 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.injectionstrategy.jakarta_cdi._default; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithCdi; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaCdi; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test field injection for component model jakarta. + * Default value option mapstruct.defaultInjectionStrategy is "field" + * + * @author Filip Hrisafov + */ +@IssueKey("2950") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJakartaCdiDefaultCompileOptionFieldMapper.class, + GenderJakartaCdiDefaultCompileOptionFieldMapper.class +}) +@WithJakartaCdi +@WithCdi +public class JakartaCdiAndCdiDefaultCompileOptionFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveJakartaInjection() { + generatedSource.forMapper( CustomerJakartaCdiDefaultCompileOptionFieldMapper.class ) + .content() + .contains( "import jakarta.enterprise.context.ApplicationScoped;" ) + .contains( "import jakarta.inject.Inject;" ) + .contains( "@Inject" + lineSeparator() + " private GenderJakartaCdiDefaultCompileOptionFieldMapper" ) + .contains( "@ApplicationScoped" + lineSeparator() + "public class" ) + .doesNotContain( "javax.inject" ) + .doesNotContain( "javax.enterprise" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/JakartaCdiDefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/JakartaCdiDefaultCompileOptionFieldMapperTest.java new file mode 100644 index 0000000000..57714cdb01 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/_default/JakartaCdiDefaultCompileOptionFieldMapperTest.java @@ -0,0 +1,54 @@ +/* + * 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.injectionstrategy.jakarta_cdi._default; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaCdi; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test field injection for component model jakarta-cdi. + * Default value option mapstruct.defaultInjectionStrategy is "field" + * + * @author Filip Hrisafov + */ +@IssueKey("2950") +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJakartaCdiDefaultCompileOptionFieldMapper.class, + GenderJakartaCdiDefaultCompileOptionFieldMapper.class +}) +@WithJakartaCdi +public class JakartaCdiDefaultCompileOptionFieldMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveFieldInjection() { + generatedSource.forMapper( CustomerJakartaCdiDefaultCompileOptionFieldMapper.class ) + .content() + .contains( "import jakarta.enterprise.context.ApplicationScoped;" ) + .contains( "import jakarta.inject.Inject;" ) + .contains( "@Inject" + lineSeparator() + " private GenderJakartaCdiDefaultCompileOptionFieldMapper" ) + .contains( "@ApplicationScoped" + lineSeparator() + "public class" ) + .doesNotContain( "public CustomerJakartaCdiDefaultCompileOptionFieldMapperImpl(" ) + .doesNotContain( "javax.inject" ) + .doesNotContain( "javax.enterprise" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithCdi.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithCdi.java new file mode 100644 index 0000000000..6da78fbfe0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithCdi.java @@ -0,0 +1,28 @@ +/* + * 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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Spring Dependencies + * + * @author Filip Hrisafov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithTestDependency({ + "javax.inject", + "cdi-api", +}) +public @interface WithCdi { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaCdi.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaCdi.java new file mode 100644 index 0000000000..f22a1d26e1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaCdi.java @@ -0,0 +1,28 @@ +/* + * 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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Spring Dependencies + * + * @author Filip Hrisafov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithTestDependency({ + "jakarta.inject-api", + "jakarta.enterprise.cdi-api", +}) +public @interface WithJakartaCdi { + +} From fd4a2548b3a1317cf4c419ef022889680fc6b056 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Aug 2022 11:16:48 +0200 Subject: [PATCH 293/597] #2928 Add IntelliJ and Eclipse plugin information --- .../main/asciidoc/chapter-2-set-up.asciidoc | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 48b352bd94..601bc42a99 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -273,3 +273,28 @@ disableBuilders` MapStruct can be used with Java 9 and higher versions. To allow usage of the `@Generated` annotation `java.annotation.processing.Generated` (part of the `java.compiler` module) can be enabled. + +=== IDE Integration + +There are optional MapStruct plugins for IntelliJ and Eclipse that allow you to have additional completion support (and more) in the annotations. + +==== IntelliJ + +The https://plugins.jetbrains.com/plugin/10036-mapstruct-support[MapStruct IntelliJ] plugin offers assistance in projects that use MapStruct. + +Some features include: + +* Code completion in `target`, `source`, `expression` +* Go To Declaration for properties in `target` and `source` +* Find Usages of properties in `target` and `source` +* Refactoring support +* Errors and Quick Fixes + +==== Eclipse + +The https://marketplace.eclipse.org/content/mapstruct-eclipse-plugin[MapStruct Eclipse] Plugin offers assistance in projects that use MapStruct. + +Some features include: + +* Code completion in `target` and `source` +* Quick Fixes From 237543c47cb1827189e8c8d81b5b5b9c2eabadf1 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 24 Aug 2022 18:59:31 +0200 Subject: [PATCH 294/597] #2897 Always import types defined in `Mapper#imports` --- .../ap/internal/model/common/TypeFactory.java | 19 ++++++++++- .../processor/MapperCreationProcessor.java | 2 +- .../ap/test/bugs/_2897/Issue2897Mapper.java | 23 +++++++++++++ .../ap/test/bugs/_2897/Issue2897Test.java | 33 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2897/Source.java | 22 +++++++++++++ .../mapstruct/ap/test/bugs/_2897/Target.java | 22 +++++++++++++ .../ap/test/bugs/_2897/util/Util.java | 21 ++++++++++++ 7 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Issue2897Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Issue2897Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/util/Util.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index b209626725..f28e0c687e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -193,7 +193,24 @@ public Type getType(TypeMirror mirror) { return getType( mirror, false ); } + /** + * Return a type that is always going to be imported. + * This is useful when using it in {@code Mapper#imports} + * for types that should be used in expressions. + * + * @param mirror the type mirror for which we need a type + * + * @return the type + */ + public Type getAlwaysImportedType(TypeMirror mirror) { + return getType( mirror, false, true ); + } + private Type getType(TypeMirror mirror, boolean isLiteral) { + return getType( mirror, isLiteral, null ); + } + + private Type getType(TypeMirror mirror, boolean isLiteral, Boolean alwaysImport) { if ( !canBeProcessed( mirror ) ) { throw new TypeHierarchyErroneousException( mirror ); } @@ -212,7 +229,7 @@ private Type getType(TypeMirror mirror, boolean isLiteral) { String qualifiedName; TypeElement typeElement; Type componentType; - Boolean toBeImported = null; + Boolean toBeImported = alwaysImport; if ( mirror.getKind() == TypeKind.DECLARED ) { DeclaredType declaredType = (DeclaredType) mirror; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index c05d2d18bd..b69ba388b0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -307,7 +307,7 @@ private SortedSet getExtraImports(TypeElement element, MapperOptions mapp for ( TypeMirror extraImport : mapperOptions.imports() ) { - Type type = typeFactory.getType( extraImport ); + Type type = typeFactory.getAlwaysImportedType( extraImport ); extraImports.add( type ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Issue2897Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Issue2897Mapper.java new file mode 100644 index 0000000000..e13f2083ee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Issue2897Mapper.java @@ -0,0 +1,23 @@ +/* + * 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._2897; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.bugs._2897.util.Util; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(imports = Util.Factory.class) +public interface Issue2897Mapper { + + Issue2897Mapper INSTANCE = Mappers.getMapper( Issue2897Mapper.class ); + + @Mapping( target = "value", expression = "java(Factory.parse( source ))") + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Issue2897Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Issue2897Test.java new file mode 100644 index 0000000000..718ece4a90 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Issue2897Test.java @@ -0,0 +1,33 @@ +/* + * 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._2897; + +import org.mapstruct.ap.test.bugs._2897.util.Util; +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 Filip Hrisafov + */ +@IssueKey("2897") +@WithClasses({ + Util.class, + Issue2897Mapper.class, + Source.class, + Target.class, +}) +class Issue2897Test { + + @ProcessorTest + void shouldImportNestedClassInMapperImports() { + Target target = Issue2897Mapper.INSTANCE.map( new Source( "test" ) ); + + assertThat( target.getValue() ).isEqualTo( "parsed(test)" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Source.java new file mode 100644 index 0000000000..5d46ad1ba3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Source.java @@ -0,0 +1,22 @@ +/* + * 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._2897; + +/** + * @author Filip Hrisafov + */ +public class Source { + + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Target.java new file mode 100644 index 0000000000..71fbabbe57 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/Target.java @@ -0,0 +1,22 @@ +/* + * 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._2897; + +/** + * @author Filip Hrisafov + */ +public class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/util/Util.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/util/Util.java new file mode 100644 index 0000000000..5ef0c7e254 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2897/util/Util.java @@ -0,0 +1,21 @@ +/* + * 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._2897.util; + +import org.mapstruct.ap.test.bugs._2897.Source; + +/** + * @author Filip Hrisafov + */ +public class Util { + + public static class Factory { + + public static String parse(Source source) { + return source == null ? null : "parsed(" + source.getValue() + ")"; + } + } +} From b24e831cf0960cc25a4967fe6e1f11c536c0b118 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 24 Aug 2022 19:11:52 +0200 Subject: [PATCH 295/597] #2937 Fix conditional check for collections with adders --- .../ap/internal/model/MethodReference.ftl | 1 + .../model/MethodReferencePresenceCheck.ftl | 1 + .../ap/internal/model/common/SourceRHS.ftl | 2 +- .../ap/test/bugs/_2937/Issue2937Mapper.java | 59 +++++++++++++++++++ .../ap/test/bugs/_2937/Issue2937Test.java | 42 +++++++++++++ 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2937/Issue2937Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2937/Issue2937Test.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl index 80f893f1a2..4b45643dc8 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl @@ -64,6 +64,7 @@ --> <#macro _assignment assignmentToUse> <@includeModel object=assignmentToUse + presenceCheck=ext.presenceCheck targetBeanName=ext.targetBeanName existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl index 24f871e214..9a1c7b24ae 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl @@ -7,5 +7,6 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MethodReferencePresenceCheck" --> <@includeModel object=methodReference + presenceCheck=true targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/common/SourceRHS.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/common/SourceRHS.ftl index 0b60bd39ae..aaf1eb8df4 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/common/SourceRHS.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/common/SourceRHS.ftl @@ -6,4 +6,4 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.common.SourceRHS" --> -<#if sourceLoopVarName??>${sourceLoopVarName}<#elseif sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference} \ No newline at end of file +<#if sourceLoopVarName?? && !ext.presenceCheck??>${sourceLoopVarName}<#elseif sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference} \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2937/Issue2937Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2937/Issue2937Mapper.java new file mode 100644 index 0000000000..95d767acb8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2937/Issue2937Mapper.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.bugs._2937; + +import java.util.ArrayList; +import java.util.Collection; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface Issue2937Mapper { + + Issue2937Mapper INSTANCE = Mappers.getMapper( Issue2937Mapper.class ); + + Target map(Source source); + + @Condition + default boolean isApplicable(Collection collection) { + return collection == null || collection.size() > 1; + } + + class Source { + private final Collection names; + + public Source(Collection names) { + this.names = names; + } + + public Collection getNames() { + return names; + } + + } + + class Target { + private final Collection names; + + public Target() { + this.names = new ArrayList<>(); + } + + public Collection getNames() { + return names; + } + + public void addName(String name) { + this.names.add( name ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2937/Issue2937Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2937/Issue2937Test.java new file mode 100644 index 0000000000..140575263c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2937/Issue2937Test.java @@ -0,0 +1,42 @@ +/* + * 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._2937; + +import java.util.ArrayList; +import java.util.List; + +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 Filip Hrisafov + */ +@IssueKey("2937") +@WithClasses({ + Issue2937Mapper.class, +}) +class Issue2937Test { + + @ProcessorTest + void shouldCorrectlyUseConditionalForAdder() { + List sourceNames = new ArrayList<>(); + sourceNames.add( "Tester 1" ); + Issue2937Mapper.Source source = new Issue2937Mapper.Source( sourceNames ); + Issue2937Mapper.Target target = Issue2937Mapper.INSTANCE.map( source ); + + assertThat( target.getNames() ).isEmpty(); + + sourceNames.add( "Tester 2" ); + + target = Issue2937Mapper.INSTANCE.map( source ); + + assertThat( target.getNames() ) + .containsExactly( "Tester 1", "Tester 2" ); + } +} From 71b1a7b8a2f33016d38f01590f641e0e20d424ae Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Aug 2022 09:50:06 +0200 Subject: [PATCH 296/597] #2945 Stabilise top level imports Make sure that GeneratedType always gets the imported types from a Type before adding them --- .../ap/internal/model/GeneratedType.java | 6 ++-- .../ap/test/bugs/_2945/Issue2945Mapper.java | 22 ++++++++++++ .../ap/test/bugs/_2945/Issue2945Test.java | 35 +++++++++++++++++++ .../test/bugs/_2945/_target/EnumHolder.java | 13 +++++++ .../ap/test/bugs/_2945/_target/Target.java | 18 ++++++++++ .../ap/test/bugs/_2945/source/Source.java | 22 ++++++++++++ 6 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/Issue2945Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/Issue2945Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/_target/EnumHolder.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/_target/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/source/Source.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java index f8f65c927f..df6ed9b2b1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java @@ -258,8 +258,10 @@ protected void addIfImportRequired(Collection collection, Type typeToAdd) return; } - if ( needsImportDeclaration( typeToAdd ) ) { - collection.add( typeToAdd ); + for ( Type type : typeToAdd.getImportTypes() ) { + if ( needsImportDeclaration( type ) ) { + collection.add( type ); + } } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/Issue2945Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/Issue2945Mapper.java new file mode 100644 index 0000000000..604d546273 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/Issue2945Mapper.java @@ -0,0 +1,22 @@ +/* + * 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._2945; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.bugs._2945._target.Target; +import org.mapstruct.ap.test.bugs._2945.source.Source; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2945Mapper { + + Issue2945Mapper INSTANCE = Mappers.getMapper( Issue2945Mapper.class ); + + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/Issue2945Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/Issue2945Test.java new file mode 100644 index 0000000000..11dcf752c0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/Issue2945Test.java @@ -0,0 +1,35 @@ +/* + * 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._2945; + +import org.mapstruct.ap.test.bugs._2945._target.EnumHolder; +import org.mapstruct.ap.test.bugs._2945._target.Target; +import org.mapstruct.ap.test.bugs._2945.source.Source; +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 Filip Hrisafov + */ +@IssueKey("2945") +@WithClasses({ + EnumHolder.class, + Issue2945Mapper.class, + Source.class, + Target.class, +}) +class Issue2945Test { + + @ProcessorTest + void shouldCompile() { + Target target = Issue2945Mapper.INSTANCE.map( new Source( "VALUE_1" ) ); + + assertThat( target.getProperty() ).isEqualTo( EnumHolder.Property.VALUE_1 ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/_target/EnumHolder.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/_target/EnumHolder.java new file mode 100644 index 0000000000..0b75e9ce36 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/_target/EnumHolder.java @@ -0,0 +1,13 @@ +/* + * 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._2945._target; + +public class EnumHolder { + public enum Property { + VALUE_1, + VALUE_2; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/_target/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/_target/Target.java new file mode 100644 index 0000000000..25b69eed29 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/_target/Target.java @@ -0,0 +1,18 @@ +/* + * 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._2945._target; + +public class Target { + private EnumHolder.Property property; + + public EnumHolder.Property getProperty() { + return property; + } + + public void setProperty(EnumHolder.Property property) { + this.property = property; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/source/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/source/Source.java new file mode 100644 index 0000000000..8364941ad1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2945/source/Source.java @@ -0,0 +1,22 @@ +/* + * 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._2945.source; + +/** + * @author Filip Hrisafov + */ +public class Source { + + private final String property; + + public Source(String property) { + this.property = property; + } + + public String getProperty() { + return property; + } +} From 42500ca755a68eef115261adea79ff80892ddd49 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 Aug 2022 18:54:33 +0200 Subject: [PATCH 297/597] #2907 Add test case for nested import of array --- .../ap/test/bugs/_2907/Issue2907Test.java | 35 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2907/Source.java | 21 +++++++++++ .../ap/test/bugs/_2907/SourceNested.java | 19 ++++++++++ .../mapstruct/ap/test/bugs/_2907/Target.java | 31 ++++++++++++++++ .../bugs/_2907/mapper/Issue2907Mapper.java | 17 +++++++++ 5 files changed, 123 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Issue2907Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/SourceNested.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/mapper/Issue2907Mapper.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Issue2907Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Issue2907Test.java new file mode 100644 index 0000000000..a005b6a1ad --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Issue2907Test.java @@ -0,0 +1,35 @@ +/* + * 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._2907; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.bugs._2907.mapper.Issue2907Mapper; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2907") +@WithClasses({ + Issue2907Mapper.class, + Source.class, + SourceNested.class, + Target.class, +}) +class Issue2907Test { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + void shouldNotGeneratedImportForNestedClass() { + generatedSource.forMapper( Issue2907Mapper.class ) + .containsNoImportFor( Target.TargetNested.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Source.java new file mode 100644 index 0000000000..42d015fde7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Source.java @@ -0,0 +1,21 @@ +/* + * 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._2907; + +import java.util.Set; + +public class Source { + + private Set nested; + + public Set getNested() { + return nested; + } + + public void setNested(Set nested) { + this.nested = nested; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/SourceNested.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/SourceNested.java new file mode 100644 index 0000000000..a58a94f0f3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/SourceNested.java @@ -0,0 +1,19 @@ +/* + * 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._2907; + +public class SourceNested { + + private String prop; + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Target.java new file mode 100644 index 0000000000..80a796d20d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/Target.java @@ -0,0 +1,31 @@ +/* + * 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._2907; + +public class Target { + + private TargetNested[] nested; + + public TargetNested[] getNested() { + return nested; + } + + public void setNested(TargetNested[] nested) { + this.nested = nested; + } + + public static class TargetNested { + private String prop; + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/mapper/Issue2907Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/mapper/Issue2907Mapper.java new file mode 100644 index 0000000000..5244f95bb4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2907/mapper/Issue2907Mapper.java @@ -0,0 +1,17 @@ +/* + * 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._2907.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.bugs._2907.Source; +import org.mapstruct.ap.test.bugs._2907.Target; + +@Mapper +public interface Issue2907Mapper { + + Target map(Source source); + +} From 853e7b27df3f7a053bec28846a646588557fcfca Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 24 Aug 2022 19:39:09 +0200 Subject: [PATCH 298/597] #2925 Fix IllegalArgumentException when resolving generic parameters When resolving the parameter for a method like: ``` Optional from(T value) ``` There was an exception in javac because getting a DeclaredType from an Optional with a primitive type argument throws an exception. Therefore, when assigning the type arguments we get the boxed equivalent. This problem does not happen in the Eclipse compiler --- .../internal/model/source/MethodMatcher.java | 4 ++- .../ap/test/bugs/_2925/Issue2925Mapper.java | 23 +++++++++++++ .../ap/test/bugs/_2925/Issue2925Test.java | 32 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_2925/Source.java | 19 +++++++++++ .../mapstruct/ap/test/bugs/_2925/Target.java | 22 +++++++++++++ 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Issue2925Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Issue2925Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Target.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index f0d9536799..baa3d7eac5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -374,7 +374,9 @@ else if ( typeFromCandidateMethodTypeParameter.isWildCardBoundByTypeVar() // something went wrong return null; } - typeArgs[i] = matchingType.getTypeMirror(); + // Use the boxed equivalent for the type arguments, + // because a primitive type cannot be a type argument + typeArgs[i] = matchingType.getBoxedEquivalent().getTypeMirror(); } else { // it is not a type var (e.g. Map ), String is not a type var diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Issue2925Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Issue2925Mapper.java new file mode 100644 index 0000000000..d231413820 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Issue2925Mapper.java @@ -0,0 +1,23 @@ +/* + * 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._2925; + +import java.util.Optional; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue2925Mapper { + + Issue2925Mapper INSTANCE = Mappers.getMapper( Issue2925Mapper.class ); + + Target map(Source source); + + static Optional toOptional(T value) { + return Optional.ofNullable( value ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Issue2925Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Issue2925Test.java new file mode 100644 index 0000000000..73d009d093 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Issue2925Test.java @@ -0,0 +1,32 @@ +/* + * 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._2925; + +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 Filip Hrisafov + */ +@IssueKey("2925") +@WithClasses({ + Issue2925Mapper.class, + Source.class, + Target.class, +}) +class Issue2925Test { + + @ProcessorTest + void shouldUseOptionalWrappingMethod() { + Target target = Issue2925Mapper.INSTANCE.map( new Source( 10L ) ); + + assertThat( target.getValue() ) + .hasValue( 10L ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Source.java new file mode 100644 index 0000000000..c21ce67775 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Source.java @@ -0,0 +1,19 @@ +/* + * 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._2925; + +public class Source { + + private final long value; + + public Source(long value) { + this.value = value; + } + + public long getValue() { + return value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Target.java new file mode 100644 index 0000000000..4693a83fdb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2925/Target.java @@ -0,0 +1,22 @@ +/* + * 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._2925; + +import java.util.Optional; + +public class Target { + + private Long value; + + public Optional getValue() { + return Optional.ofNullable( value ); + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + public void setValue(Optional value) { + this.value = value.orElse( null ); + } +} From 3f798744ac347fbddaa7e9f79383c0798e479f18 Mon Sep 17 00:00:00 2001 From: Taihao Zhang <46465678+zhtaihao@users.noreply.github.com> Date: Wed, 24 Aug 2022 19:40:08 +0200 Subject: [PATCH 299/597] Fix typo in docs (#2982) --- .../main/asciidoc/chapter-10-advanced-mapping-options.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 9c1b2243eb..a6e42b6d48 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -304,7 +304,7 @@ The difference is that it allows users to write custom condition methods that wi A custom condition method is a method that is annotated with `org.mapstruct.Condition` and returns `boolean`. -e.g. if you only want to map a String property when it is not `null, and it is not empty then you can do something like: +e.g. if you only want to map a String property when it is not `null`, and it is not empty then you can do something like: .Mapper using custom condition check method ==== From 4fa66229d9e3949f451fe8b79b62dcbbd138b669 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 26 Aug 2022 19:35:54 +0200 Subject: [PATCH 300/597] #2990 Stabilise top level imports --- .../mapstruct/ap/internal/model/common/Type.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index ce82fff621..fd7f3d6904 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -181,9 +181,16 @@ public Type(TypeUtils typeUtils, ElementUtils elementUtils, TypeFactory typeFact this.loggingVerbose = loggingVerbose; - // The top level type for an array type is the top level type of the component type - TypeElement typeElementForTopLevel = - this.componentType == null ? this.typeElement : this.componentType.getTypeElement(); + TypeElement typeElementForTopLevel; + if ( Boolean.TRUE.equals( isToBeImported ) ) { + // If the is to be imported is explicitly set to true then we shouldn't look for the top level type + typeElementForTopLevel = null; + } + else { + // The top level type for an array type is the top level type of the component type + typeElementForTopLevel = + this.componentType == null ? this.typeElement : this.componentType.getTypeElement(); + } this.topLevelType = topLevelType( typeElementForTopLevel, this.typeFactory ); this.nameWithTopLevelTypeName = nameWithTopLevelTypeName( typeElementForTopLevel, this.name ); } From 4708f4b2aa1d165b17f8d42586085c2c8d8aaf2a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 25 Aug 2022 08:40:50 +0200 Subject: [PATCH 301/597] #2950 Disable CDI in the full features tests on Java 8 --- .../tests/FullFeatureCompilationExclusionCliEnhancer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java index c8bac6208c..0f261d6568 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -28,6 +28,10 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1801/*.java" ); switch ( currentJreVersion ) { + case JAVA_8: + additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" ); + break; case JAVA_9: // TODO find out why this fails: additionalExcludes.add( "org/mapstruct/ap/test/collection/wildcard/BeanMapper.java" ); From ac356cab2529e1ab71205e2f9389686fe3b303c3 Mon Sep 17 00:00:00 2001 From: Orange Add <48479242+chenzijia12300@users.noreply.github.com> Date: Sun, 28 Aug 2022 18:33:46 +0800 Subject: [PATCH 302/597] #2983 Add `@AnnotateWith` support to non bean mapping methods --- .../model/AbstractMappingMethodBuilder.java | 14 ++++++ .../ap/internal/model/BeanMappingMethod.java | 16 +------ .../model/ContainerMappingMethod.java | 5 +- .../internal/model/IterableMappingMethod.java | 5 +- .../ap/internal/model/MapMappingMethod.java | 6 ++- .../model/NormalTypeMappingMethod.java | 13 +++++- .../internal/model/StreamMappingMethod.java | 21 +++++---- .../ap/internal/model/ValueMappingMethod.java | 20 +++++++- .../internal/model/IterableMappingMethod.ftl | 3 ++ .../ap/internal/model/MapMappingMethod.ftl | 3 ++ .../ap/internal/model/StreamMappingMethod.ftl | 3 ++ .../ap/internal/model/ValueMappingMethod.ftl | 3 ++ .../AnnotateBeanMappingMethodMapper.java | 46 +++++++++++++++++++ .../AnnotateIterableMappingMethodMapper.java | 21 +++++++++ .../AnnotateMapMappingMethodMapper.java | 24 ++++++++++ .../AnnotateStreamMappingMethodMapper.java | 21 +++++++++ .../AnnotateValueMappingMethodMapper.java | 26 +++++++++++ .../test/annotatewith/AnnotateWithTest.java | 46 ++++++++++++++++++- 18 files changed, 264 insertions(+), 32 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateBeanMappingMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateIterableMappingMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateMapMappingMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateStreamMappingMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateValueMappingMethodMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index efc7a71469..6efb67ba9c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -13,6 +13,9 @@ import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.util.Strings; +import java.util.ArrayList; +import java.util.List; + /** * An abstract builder that can be reused for building {@link MappingMethod}(s). * @@ -117,4 +120,15 @@ public ForgedMethodHistory getDescription() { return description; } + public List getMethodAnnotations() { + AdditionalAnnotationsBuilder additionalAnnotationsBuilder = + new AdditionalAnnotationsBuilder( + ctx.getElementUtils(), + ctx.getTypeFactory(), + ctx.getMessager() ); + List annotations = new ArrayList<>(); + annotations.addAll( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); + return annotations; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index e748c173af..4f2dc4e5f1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -85,7 +85,6 @@ */ public class BeanMappingMethod extends NormalTypeMappingMethod { - private final List annotations; private final List propertyMappings; private final Map> mappingsByParameter; private final Map> constructorMappingsByParameter; @@ -113,7 +112,6 @@ public static class Builder extends AbstractMappingMethodBuilder unprocessedSourceParameters = new HashSet<>(); private final Set existingVariableNames = new HashSet<>(); private final Map> unprocessedDefinedTargets = new LinkedHashMap<>(); - private final List annotations = new ArrayList<>(); private MappingReferences mappingReferences; private MethodReference factoryMethod; @@ -216,12 +214,6 @@ else if ( !method.isUpdateMethod() ) { // If the return type cannot be constructed then no need to try to create mappings return null; } - AdditionalAnnotationsBuilder additionalAnnotationsBuilder = - new AdditionalAnnotationsBuilder( - ctx.getElementUtils(), - ctx.getTypeFactory(), - ctx.getMessager() ); - annotations.addAll( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); /* the type that needs to be used in the mapping process as target */ Type resultTypeToMap = returnTypeToConstruct == null ? method.getResultType() : returnTypeToConstruct; @@ -370,7 +362,7 @@ else if ( !method.isUpdateMethod() ) { return new BeanMappingMethod( method, - annotations, + getMethodAnnotations(), existingVariableNames, propertyMappings, factoryMethod, @@ -1724,6 +1716,7 @@ private BeanMappingMethod(Method method, List subclassMappings) { super( method, + annotations, existingVariableNames, factoryMethod, mapNullToDefault, @@ -1732,7 +1725,6 @@ private BeanMappingMethod(Method method, ); //CHECKSTYLE:ON - this.annotations = annotations; this.propertyMappings = propertyMappings; this.returnTypeBuilder = returnTypeBuilder; this.finalizerMethod = finalizerMethod; @@ -1771,10 +1763,6 @@ else if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) { this.subclassMappings = subclassMappings; } - public List getAnnotations() { - return annotations; - } - public List getConstantMappings() { return constantMappings; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java index 382aff9737..21e547eea0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java @@ -32,12 +32,13 @@ public abstract class ContainerMappingMethod extends NormalTypeMappingMethod { private final String index2Name; private IterableCreation iterableCreation; - ContainerMappingMethod(Method method, Collection existingVariables, Assignment parameterAssignment, + ContainerMappingMethod(Method method, List annotations, + Collection existingVariables, Assignment parameterAssignment, MethodReference factoryMethod, boolean mapNullToDefault, String loopVariableName, List beforeMappingReferences, List afterMappingReferences, SelectionParameters selectionParameters) { - super( method, existingVariables, factoryMethod, mapNullToDefault, beforeMappingReferences, + super( method, annotations, existingVariables, factoryMethod, mapNullToDefault, beforeMappingReferences, afterMappingReferences ); this.elementAssignment = parameterAssignment; this.loopVariableName = loopVariableName; 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 38ec44f1e1..10e64b7008 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 @@ -57,6 +57,7 @@ protected IterableMappingMethod instantiateMappingMethod(Method method, Collecti List afterMappingMethods, SelectionParameters selectionParameters) { return new IterableMappingMethod( method, + getMethodAnnotations(), existingVariables, assignment, factoryMethod, @@ -69,13 +70,15 @@ protected IterableMappingMethod instantiateMappingMethod(Method method, Collecti } } - private IterableMappingMethod(Method method, Collection existingVariables, Assignment parameterAssignment, + private IterableMappingMethod(Method method, List annotations, + Collection existingVariables, Assignment parameterAssignment, MethodReference factoryMethod, boolean mapNullToDefault, String loopVariableName, List beforeMappingReferences, List afterMappingReferences, SelectionParameters selectionParameters) { super( method, + annotations, existingVariables, parameterAssignment, factoryMethod, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java index a8b68b435f..85766e2eca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java @@ -199,6 +199,7 @@ public MapMappingMethod build() { return new MapMappingMethod( method, + getMethodAnnotations(), existingVariables, keyAssignment, valueAssignment, @@ -224,11 +225,12 @@ protected boolean shouldUsePropertyNamesInHistory() { } - private MapMappingMethod(Method method, Collection existingVariableNames, Assignment keyAssignment, + private MapMappingMethod(Method method, List annotations, + Collection existingVariableNames, Assignment keyAssignment, Assignment valueAssignment, MethodReference factoryMethod, boolean mapNullToDefault, List beforeMappingReferences, List afterMappingReferences) { - super( method, existingVariableNames, factoryMethod, mapNullToDefault, beforeMappingReferences, + super( method, annotations, existingVariableNames, factoryMethod, mapNullToDefault, beforeMappingReferences, afterMappingReferences ); this.keyAssignment = keyAssignment; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java index fcd8d70d52..79cd0e0566 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java @@ -24,7 +24,10 @@ public abstract class NormalTypeMappingMethod extends MappingMethod { private final boolean overridden; private final boolean mapNullToDefault; - NormalTypeMappingMethod(Method method, Collection existingVariableNames, MethodReference factoryMethod, + private final List annotations; + + NormalTypeMappingMethod(Method method, List annotations, + Collection existingVariableNames, MethodReference factoryMethod, boolean mapNullToDefault, List beforeMappingReferences, List afterMappingReferences) { @@ -32,6 +35,7 @@ public abstract class NormalTypeMappingMethod extends MappingMethod { this.factoryMethod = factoryMethod; this.overridden = method.overridesMethod(); this.mapNullToDefault = mapNullToDefault; + this.annotations = annotations; } @Override @@ -45,6 +49,9 @@ public Set getImportTypes() { else if ( factoryMethod != null ) { types.addAll( factoryMethod.getImportTypes() ); } + for ( Annotation annotation : annotations ) { + types.addAll( annotation.getImportTypes() ); + } return types; } @@ -60,6 +67,10 @@ public MethodReference getFactoryMethod() { return this.factoryMethod; } + public List getAnnotations() { + return annotations; + } + @Override public int hashCode() { final int prime = 31; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/StreamMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/StreamMappingMethod.java index 552040d8ce..57ee9ccdf4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/StreamMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/StreamMappingMethod.java @@ -5,6 +5,12 @@ */ package org.mapstruct.ap.internal.model; +import org.mapstruct.ap.internal.model.assignment.Java8FunctionWrapper; +import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.source.Method; +import org.mapstruct.ap.internal.model.source.SelectionParameters; + import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -13,12 +19,6 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; -import org.mapstruct.ap.internal.model.assignment.Java8FunctionWrapper; -import org.mapstruct.ap.internal.model.common.Assignment; -import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.source.Method; -import org.mapstruct.ap.internal.model.source.SelectionParameters; - import static org.mapstruct.ap.internal.util.Collections.first; /** @@ -63,9 +63,9 @@ protected StreamMappingMethod instantiateMappingMethod(Method method, Collection sourceParameterType.isIterableType() ) { helperImports.add( ctx.getTypeFactory().getType( StreamSupport.class ) ); } - return new StreamMappingMethod( method, + getMethodAnnotations(), existingVariables, assignment, factoryMethod, @@ -78,14 +78,16 @@ protected StreamMappingMethod instantiateMappingMethod(Method method, Collection ); } } - - private StreamMappingMethod(Method method, Collection existingVariables, Assignment parameterAssignment, + //CHECKSTYLE:OFF + private StreamMappingMethod(Method method, List annotations, + Collection existingVariables, Assignment parameterAssignment, MethodReference factoryMethod, boolean mapNullToDefault, String loopVariableName, List beforeMappingReferences, List afterMappingReferences, SelectionParameters selectionParameters, Set helperImports) { super( method, + annotations, existingVariables, parameterAssignment, factoryMethod, @@ -95,6 +97,7 @@ private StreamMappingMethod(Method method, Collection existingVariables, afterMappingReferences, selectionParameters ); + //CHECKSTYLE:ON this.helperImports = helperImports; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 0d71ed5506..f61e80c2d4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -41,6 +41,7 @@ */ public class ValueMappingMethod extends MappingMethod { + private final List annotations; private final List valueMappings; private final MappingEntry defaultTarget; private final MappingEntry nullTarget; @@ -116,9 +117,16 @@ else if ( sourceType.isString() && targetType.isEnumType() ) { LifecycleMethodResolver.beforeMappingMethods( method, selectionParameters, ctx, existingVariables ); List afterMappingMethods = LifecycleMethodResolver.afterMappingMethods( method, selectionParameters, ctx, existingVariables ); - + AdditionalAnnotationsBuilder additionalAnnotationsBuilder = + new AdditionalAnnotationsBuilder( + ctx.getElementUtils(), + ctx.getTypeFactory(), + ctx.getMessager() ); + List annotations = new ArrayList<>(); + annotations.addAll( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); // finally return a mapping return new ValueMappingMethod( method, + annotations, mappingEntries, valueMappings.nullValueTarget, valueMappings.defaultTargetValue, @@ -532,6 +540,7 @@ String getValue(ValueMappingOptions valueMapping) { } private ValueMappingMethod(Method method, + List annotations, List enumMappings, String nullTarget, String defaultTarget, @@ -544,6 +553,7 @@ private ValueMappingMethod(Method method, this.defaultTarget = new MappingEntry( null, defaultTarget != null ? defaultTarget : THROW_EXCEPTION); this.unexpectedValueMappingException = unexpectedValueMappingException; this.overridden = method.overridesMethod(); + this.annotations = annotations; } @Override @@ -556,7 +566,9 @@ public Set getImportTypes() { importTypes.addAll( unexpectedValueMappingException.getImportTypes() ); } } - + for ( Annotation annotation : annotations ) { + importTypes.addAll( annotation.getImportTypes() ); + } return importTypes; } @@ -590,6 +602,10 @@ public boolean isOverridden() { return overridden; } + public List getAnnotations() { + return annotations; + } + public static class MappingEntry { private final String source; private final String target; 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 495111b7a1..9af6e762bf 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 @@ -6,6 +6,9 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.IterableMappingMethod" --> +<#list annotations as annotation> + <#nt><@includeModel object=annotation/> + <#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> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl index 443d87cba7..0e388da103 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl @@ -6,6 +6,9 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MapMappingMethod" --> +<#list annotations as annotation> + <#nt><@includeModel object=annotation/> + <#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> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl index 860859fc3a..7b5b44bd56 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl @@ -6,6 +6,9 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.StreamMappingMethod" --> +<#list annotations as annotation> + <#nt><@includeModel object=annotation/> + <#if overridden>@Override <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#--TODO does it even make sense to do a callback if the result is a Stream, as they are immutable--> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl index 4d961c40af..2c3e2b6410 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -6,6 +6,9 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.ValueMappingMethod" --> +<#list annotations as annotation> + <#nt><@includeModel object=annotation/> + <#if overridden>@Override <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, ) { <#list beforeMappingReferencesWithoutMappingTarget as callback> diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateBeanMappingMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateBeanMappingMethodMapper.java new file mode 100644 index 0000000000..cc19c2d282 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateBeanMappingMethodMapper.java @@ -0,0 +1,46 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +/** + * @author orange add + */ +@Mapper +public interface AnnotateBeanMappingMethodMapper { + + @AnnotateWith(CustomMethodOnlyAnnotation.class) + Target map(Source source); + + class Source { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class Target { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateIterableMappingMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateIterableMappingMethodMapper.java new file mode 100644 index 0000000000..40c6e60fe6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateIterableMappingMethodMapper.java @@ -0,0 +1,21 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +import java.util.List; + +/** + * @author orange add + */ +@Mapper +public interface AnnotateIterableMappingMethodMapper { + + @AnnotateWith(CustomMethodOnlyAnnotation.class) + List toStringList(List integers); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateMapMappingMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateMapMappingMethodMapper.java new file mode 100644 index 0000000000..458cb68d4d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateMapMappingMethodMapper.java @@ -0,0 +1,24 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.MapMapping; +import org.mapstruct.Mapper; + +import java.util.Date; +import java.util.Map; + +/** + * @author orange add + */ +@Mapper +public interface AnnotateMapMappingMethodMapper { + + @MapMapping(valueDateFormat = "dd.MM.yyyy") + @AnnotateWith(CustomMethodOnlyAnnotation.class) + Map longDateMapToStringStringMap(Map source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateStreamMappingMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateStreamMappingMethodMapper.java new file mode 100644 index 0000000000..c574b59697 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateStreamMappingMethodMapper.java @@ -0,0 +1,21 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +import java.util.stream.Stream; + +/** + * @author orange add + */ +@Mapper +public interface AnnotateStreamMappingMethodMapper { + + @AnnotateWith(CustomMethodOnlyAnnotation.class) + Stream toStringStream(Stream integerStream); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateValueMappingMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateValueMappingMethodMapper.java new file mode 100644 index 0000000000..a9e7c7b519 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateValueMappingMethodMapper.java @@ -0,0 +1,26 @@ +/* + * 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.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; + +/** + * @author orange add + */ +@Mapper +public interface AnnotateValueMappingMethodMapper { + + @ValueMappings({ + @ValueMapping(target = "EXISTING", source = "EXISTING"), + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "OTHER_EXISTING" ) + }) + @AnnotateWith(CustomMethodOnlyAnnotation.class) + AnnotateWithEnum map(String str); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java index c2055b4c5c..31b8d8fad9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java @@ -6,7 +6,6 @@ package org.mapstruct.ap.test.annotatewith; import org.junit.jupiter.api.extension.RegisterExtension; - import org.mapstruct.Mapper; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; @@ -17,6 +16,11 @@ import org.mapstruct.ap.testutil.runner.GeneratedSource; import org.mapstruct.factory.Mappers; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -545,4 +549,44 @@ public void erroneousMapperWithParameterRepeat() { public void mapperWithIdenticalAnnotationRepeated() { } + @ProcessorTest + @WithClasses( {AnnotateBeanMappingMethodMapper.class, CustomMethodOnlyAnnotation.class} ) + public void beanMappingMethodWithCorrectCustomAnnotation() throws NoSuchMethodException { + AnnotateBeanMappingMethodMapper mapper = Mappers.getMapper( AnnotateBeanMappingMethodMapper.class ); + Method method = mapper.getClass().getMethod( "map", AnnotateBeanMappingMethodMapper.Source.class ); + assertThat( method.getAnnotation( CustomMethodOnlyAnnotation.class ) ).isNotNull(); + } + + @ProcessorTest + @WithClasses( {AnnotateIterableMappingMethodMapper.class, CustomMethodOnlyAnnotation.class} ) + public void iterableMappingMethodWithCorrectCustomAnnotation() throws NoSuchMethodException { + AnnotateIterableMappingMethodMapper mapper = Mappers.getMapper( AnnotateIterableMappingMethodMapper.class ); + Method method = mapper.getClass().getMethod( "toStringList", List.class ); + assertThat( method.getAnnotation( CustomMethodOnlyAnnotation.class ) ).isNotNull(); + } + + @ProcessorTest + @WithClasses( {AnnotateMapMappingMethodMapper.class, CustomMethodOnlyAnnotation.class} ) + public void mapMappingMethodWithCorrectCustomAnnotation() throws NoSuchMethodException { + AnnotateMapMappingMethodMapper mapper = Mappers.getMapper( AnnotateMapMappingMethodMapper.class ); + Method method = mapper.getClass().getMethod( "longDateMapToStringStringMap", Map.class ); + assertThat( method.getAnnotation( CustomMethodOnlyAnnotation.class ) ).isNotNull(); + } + + @ProcessorTest + @WithClasses( {AnnotateStreamMappingMethodMapper.class, CustomMethodOnlyAnnotation.class} ) + public void streamMappingMethodWithCorrectCustomAnnotation() throws NoSuchMethodException { + AnnotateStreamMappingMethodMapper mapper = Mappers.getMapper( AnnotateStreamMappingMethodMapper.class ); + Method method = mapper.getClass().getMethod( "toStringStream", Stream.class ); + assertThat( method.getAnnotation( CustomMethodOnlyAnnotation.class ) ).isNotNull(); + } + + @ProcessorTest + @WithClasses( {AnnotateValueMappingMethodMapper.class, AnnotateWithEnum.class, CustomMethodOnlyAnnotation.class} ) + public void valueMappingMethodWithCorrectCustomAnnotation() throws NoSuchMethodException { + AnnotateValueMappingMethodMapper mapper = Mappers.getMapper( AnnotateValueMappingMethodMapper.class ); + Method method = mapper.getClass().getMethod( "map", String.class ); + assertThat( method.getAnnotation( CustomMethodOnlyAnnotation.class ) ).isNotNull(); + } + } From 3cc2aa76751b8a2f11f0577303df90737e783e1b Mon Sep 17 00:00:00 2001 From: Orange Add <48479242+chenzijia12300@users.noreply.github.com> Date: Sun, 28 Aug 2022 18:41:25 +0800 Subject: [PATCH 303/597] #2825 Fix SubclassMapping stackoverflow exception --- .../creation/MappingResolverImpl.java | 6 +-- .../mapstruct/ap/test/bugs/_2825/Animal.java | 21 +++++++++++ .../org/mapstruct/ap/test/bugs/_2825/Cat.java | 21 +++++++++++ .../org/mapstruct/ap/test/bugs/_2825/Dog.java | 21 +++++++++++ .../ap/test/bugs/_2825/Issue2825Mapper.java | 24 ++++++++++++ .../ap/test/bugs/_2825/Issue2825Test.java | 37 +++++++++++++++++++ .../ap/test/bugs/_2825/TargetAnimal.java | 31 ++++++++++++++++ 7 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Animal.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Cat.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Dog.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Issue2825Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Issue2825Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/TargetAnimal.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 2e2a760bf4..5a2af176f6 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -196,7 +196,7 @@ private ResolvingAttempt(List sourceModel, Method mappingMethod, ForgedM this.mappingMethod = mappingMethod; this.description = description; - this.methods = filterPossibleCandidateMethods( sourceModel ); + this.methods = filterPossibleCandidateMethods( sourceModel, mappingMethod ); this.formattingParameters = formattingParameters == null ? FormattingParameters.EMPTY : formattingParameters; this.sourceRHS = sourceRHS; @@ -210,10 +210,10 @@ private ResolvingAttempt(List sourceModel, Method mappingMethod, ForgedM } // CHECKSTYLE:ON - private List filterPossibleCandidateMethods(List candidateMethods) { + private List filterPossibleCandidateMethods(List candidateMethods, T mappingMethod) { List result = new ArrayList<>( candidateMethods.size() ); for ( T candidate : candidateMethods ) { - if ( isCandidateForMapping( candidate ) ) { + if ( isCandidateForMapping( candidate ) && !candidate.equals( mappingMethod )) { result.add( candidate ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Animal.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Animal.java new file mode 100644 index 0000000000..3d60eac9fa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Animal.java @@ -0,0 +1,21 @@ +/* + * 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._2825; + +/** + * @author orange add + */ +public class Animal { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Cat.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Cat.java new file mode 100644 index 0000000000..ec826c0ffb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Cat.java @@ -0,0 +1,21 @@ +/* + * 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._2825; + +/** + * @author orange add + */ +public class Cat extends Animal { + private String race; + + public String getRace() { + return race; + } + + public void setRace(String race) { + this.race = race; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Dog.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Dog.java new file mode 100644 index 0000000000..53b41a98c9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Dog.java @@ -0,0 +1,21 @@ +/* + * 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._2825; + +/** + * @author orange add + */ +public class Dog extends Animal { + private String race; + + public String getRace() { + return race; + } + + public void setRace(String race) { + this.race = race; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Issue2825Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Issue2825Mapper.java new file mode 100644 index 0000000000..c515011b0b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Issue2825Mapper.java @@ -0,0 +1,24 @@ +/* + * 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._2825; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author orange add + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface Issue2825Mapper { + + Issue2825Mapper INSTANCE = Mappers.getMapper( Issue2825Mapper.class ); + + @SubclassMapping(target = TargetAnimal.class, source = Dog.class) + @SubclassMapping(target = TargetAnimal.class, source = Cat.class) + TargetAnimal map(Animal source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Issue2825Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Issue2825Test.java new file mode 100644 index 0000000000..9c0609b754 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/Issue2825Test.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.bugs._2825; + +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 orange add + */ +@IssueKey("2825") +@WithClasses({ + Animal.class, + Cat.class, + Dog.class, + Issue2825Mapper.class, + TargetAnimal.class, +}) +public class Issue2825Test { + + @ProcessorTest + public void mappingMethodShouldNotBeReusedForSubclassMappings() { + Dog dog = new Dog(); + dog.setName( "Lucky" ); + dog.setRace( "Shepherd" ); + TargetAnimal target = Issue2825Mapper.INSTANCE.map( dog ); + assertThat( target.getName() ).isEqualTo( "Lucky" ); + assertThat( target.getRace() ).isEqualTo( "Shepherd" ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/TargetAnimal.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/TargetAnimal.java new file mode 100644 index 0000000000..479741099a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2825/TargetAnimal.java @@ -0,0 +1,31 @@ +/* + * 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._2825; + +/** + * @author orange add + */ +public class TargetAnimal { + private String name; + + private String race; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getRace() { + return race; + } + + public void setRace(String race) { + this.race = race; + } +} From 68571de01bd9b06cef647a3d9ad073f0f0cd2456 Mon Sep 17 00:00:00 2001 From: Prasanth Omanakuttan Date: Fri, 26 Aug 2022 19:25:58 +0530 Subject: [PATCH 304/597] Update Typos in java-doc Closes #2989 --- .../conversion/AbstractJavaTimeToStringConversion.java | 8 ++++---- ...xistingInstanceSetterWrapperForCollectionsAndMaps.java | 2 +- .../assignment/GetterWrapperForCollectionsAndMaps.java | 2 +- .../ap/internal/model/assignment/UpdateWrapper.java | 2 +- .../ap/internal/model/beanmapping/PropertyEntry.java | 2 +- .../ap/internal/model/beanmapping/SourceReference.java | 4 ++-- .../java/org/mapstruct/ap/internal/util/RoundContext.java | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractJavaTimeToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractJavaTimeToStringConversion.java index 2f530618c9..3503a0e274 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractJavaTimeToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/AbstractJavaTimeToStringConversion.java @@ -21,15 +21,15 @@ *

    *

    * In general each type comes with a "parse" method to convert a string to this particular type. - * For formatting a dedicated instance of {@link java.time.format.DateTimeFormatter} is used. + * For formatting a dedicated instance of {@link DateTimeFormatter} is used. *

    *

    * If no date format for mapping is specified predefined ISO* formatters from - * {@link java.time.format.DateTimeFormatter} are used. + * {@link DateTimeFormatter} are used. *

    *

    - * An overview of date and time types shipped with Java 8 can be found at - * http://docs.oracle.com/javase/tutorial/datetime/iso/index.html. + * An overview of date and time types shipped with Java 8 can be found at the + * Standard Calendar Tutorial *

    */ public abstract class AbstractJavaTimeToStringConversion extends SimpleConversion { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java index 06a2712f00..2dd7f81a0b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java @@ -22,7 +22,7 @@ /** * This wrapper handles the situation where an assignment is done for an update method. * - * In case of a pre-existing target the wrapper checks if there is an collection or map initialized on the target bean + * In case of a pre-existing target the wrapper checks if there is a collection or map initialized on the target bean * (not null). If so it uses the addAll (for collections) or putAll (for maps). The collection / map is cleared in case * of a pre-existing target {@link org.mapstruct.MappingTarget }before adding the source entries. * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java index 40e195dcd0..d29a80b420 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java @@ -16,7 +16,7 @@ * This wrapper handles the situation were an assignment must be done via a target getter method because there * is no setter available. * - * The wrapper checks if there is an collection or map initialized on the target bean (not null). If so it uses the + * The wrapper checks if there is a collection or map initialized on the target bean (not null). If so it uses the * addAll (for collections) or putAll (for maps). The collection / map is cleared in case of a pre-existing target * {@link org.mapstruct.MappingTarget }before adding the source entries. The goal is that the same collection / map * is used as target. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java index cf0140e8ce..ff5089d6c2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java @@ -54,7 +54,7 @@ private static Type determineImplType(Assignment factoryMethod, Type targetType) return targetType.getImplementationType(); } - // no factory method means we create a new instance ourself and thus need to import the type + // no factory method means we create a new instance ourselves and thus need to import the type return targetType; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java index cb6cd8af79..a3c8a74e3f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/PropertyEntry.java @@ -14,7 +14,7 @@ /** * A PropertyEntry contains information on the name, readAccessor and presenceCheck (for source) - * and return type of a property. + * and return type of property. */ public class PropertyEntry { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index 8824f95901..4e7dad220c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -143,7 +143,7 @@ public SourceReference build() { * the parameter name to avoid ambiguity * * consider: {@code Target map( Source1 source1 )} - * entries in an @Mapping#source can be "source1.propx" or just "propx" to be valid + * entries in a @Mapping#source can be "source1.propx" or just "propx" to be valid * * @param segments the segments of @Mapping#source * @param parameter the one and only parameter @@ -213,7 +213,7 @@ private SourceReference buildFromMultipleSourceParameters(String[] segments, Par * needs to match the parameter name to avoid ambiguity * * consider: {@code Target map( Source1 source1, Source2 source2 )} - * entries in an @Mapping#source need to be "source1.propx" or "source2.propy.propz" to be valid + * entries in a @Mapping#source need to be "source1.propx" or "source2.propy.propz" to be valid * * @param segments the segments of @Mapping#source * @return parameter that matches with first segment of @Mapping#source diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/RoundContext.java b/processor/src/main/java/org/mapstruct/ap/internal/util/RoundContext.java index 78e61fbf29..e34d013bc5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/RoundContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/RoundContext.java @@ -41,7 +41,7 @@ public void addTypeReadyForProcessing(TypeMirror type) { /** * Whether the given type has been found to be ready for further processing or not. This is the case if the type's - * hierarchy is complete (no super-types need to be generated by other processors) an no processors have signaled + * hierarchy is complete (no super-types need to be generated by other processors) and no processors have signaled * the intention to amend the given type. * * @param type the typed to be checked for its readiness From 21069e5a2ece79d3f4a7f7d247b2f96060ccedbb Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 29 Aug 2022 21:15:01 +0200 Subject: [PATCH 305/597] #2895 Generate more readable annotations --- .../ap/internal/model/Annotation.ftl | 15 +++++- .../test/annotatewith/AnnotateWithTest.java | 9 +++- .../test/annotatewith/CustomNamedMapper.java | 38 +++++++++++++++ .../annotatewith/CustomNamedMapperImpl.java | 46 ++++++++++++++++++- .../MultipleArrayValuesMapperImpl.java | 14 +++++- 5 files changed, 117 insertions(+), 5 deletions(-) diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl index eb67d732fc..81f3d2ba8b 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl @@ -6,4 +6,17 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.Annotation" --> -@<@includeModel object=type/><#if (properties?size > 0) >(<#list properties as property><@includeModel object=property/><#if property_has_next>, ) \ No newline at end of file +<#switch properties?size> + <#case 0> + @<@includeModel object=type/><#rt> + <#break> + <#case 1> + @<@includeModel object=type/>(<@includeModel object=properties[0]/>)<#rt> + <#break> + <#default> + @<@includeModel object=type/>( + <#list properties as property> + <#nt><@includeModel object=property/><#if property_has_next>, + + )<#rt> + \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java index 31b8d8fad9..e3b20f547e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java @@ -42,8 +42,13 @@ public void mapperBecomesDeprecatedAndGetsCustomAnnotation() { } @ProcessorTest - @WithClasses( { CustomNamedMapper.class, CustomAnnotationWithParamsContainer.class, - CustomAnnotationWithParams.class } ) + @WithClasses( { + CustomNamedMapper.class, + CustomAnnotationWithParamsContainer.class, + CustomAnnotationWithParams.class, + CustomClassOnlyAnnotation.class, + CustomMethodOnlyAnnotation.class, + } ) public void annotationWithValue() { generatedSource.addComparisonToFixtureFor( CustomNamedMapper.class ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedMapper.java index 32241ab4ab..9f48d8c764 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/CustomNamedMapper.java @@ -13,6 +13,7 @@ * @author Ben Zegveld */ @Mapper +@AnnotateWith( CustomClassOnlyAnnotation.class ) @AnnotateWith( value = CustomAnnotationWithParams.class, elements = { @Element( name = "stringArray", strings = "test" ), @Element( name = "stringParam", strings = "test" ), @@ -35,6 +36,43 @@ @Element( name = "shortArray", shorts = 3 ), @Element( name = "shortParam", shorts = 1 ) } ) +@AnnotateWith( value = CustomAnnotationWithParams.class, elements = { + @Element(name = "stringParam", strings = "single value") +}) public interface CustomNamedMapper { + @AnnotateWith(value = CustomAnnotationWithParams.class, elements = { + @Element(name = "stringParam", strings = "double method value"), + @Element(name = "stringArray", strings = { "first", "second" }), + }) + @AnnotateWith(value = CustomAnnotationWithParams.class, elements = { + @Element(name = "stringParam", strings = "single method value") + }) + @AnnotateWith( CustomMethodOnlyAnnotation.class ) + Target map(Source source); + + class Target { + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/CustomNamedMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/CustomNamedMapperImpl.java index cb01cfec85..a485d6676f 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/CustomNamedMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/CustomNamedMapperImpl.java @@ -12,6 +12,50 @@ date = "2022-06-06T16:48:18+0200", comments = "version: , compiler: javac, environment: Java 11.0.12 (Azul Systems, Inc.)" ) -@CustomAnnotationWithParams(stringArray = "test", stringParam = "test", booleanArray = true, booleanParam = true, byteArray = 16, byteParam = 19, charArray = 'd', charParam = 'a', enumArray = AnnotateWithEnum.EXISTING, enumParam = AnnotateWithEnum.EXISTING, doubleArray = 0.3, doubleParam = 1.2, floatArray = 0.300000011920929f, floatParam = 1.2000000476837158f, intArray = 3, intParam = 1, longArray = 3L, longParam = 1L, shortArray = 3, shortParam = 1) +@CustomClassOnlyAnnotation +@CustomAnnotationWithParams( + stringArray = "test", + stringParam = "test", + booleanArray = true, + booleanParam = true, + byteArray = 16, + byteParam = 19, + charArray = 'd', + charParam = 'a', + enumArray = AnnotateWithEnum.EXISTING, + enumParam = AnnotateWithEnum.EXISTING, + doubleArray = 0.3, + doubleParam = 1.2, + floatArray = 0.300000011920929f, + floatParam = 1.2000000476837158f, + intArray = 3, + intParam = 1, + longArray = 3L, + longParam = 1L, + shortArray = 3, + shortParam = 1 +) +@CustomAnnotationWithParams(stringParam = "single value") public class CustomNamedMapperImpl implements CustomNamedMapper { + + @CustomAnnotationWithParams( + stringParam = "double method value", + stringArray = { "first", "second" } + ) + @CustomAnnotationWithParams(stringParam = "single method value") + @CustomMethodOnlyAnnotation + @Override + public Target map(Source source) { + if ( source == null ) { + return null; + } + + String value = null; + + value = source.getValue(); + + Target target = new Target( value ); + + return target; + } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapperImpl.java index fe9bb93f13..ea15207c66 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/annotatewith/MultipleArrayValuesMapperImpl.java @@ -13,6 +13,18 @@ date = "2022-06-06T16:48:23+0200", comments = "version: , compiler: javac, environment: Java 11.0.12 (Azul Systems, Inc.)" ) -@CustomAnnotationWithParams(stringArray = { "test1", "test2" }, booleanArray = { false, true }, byteArray = { 8, 31 }, charArray = { 'b', 'c' }, doubleArray = { 1.2, 3.4 }, floatArray = { 1.2000000476837158f, 3.4000000953674316f }, intArray = { 12, 34 }, longArray = { 12L, 34L }, shortArray = { 12, 34 }, classArray = { Mapper.class, CustomAnnotationWithParams.class }, stringParam = "required parameter") +@CustomAnnotationWithParams( + stringArray = { "test1", "test2" }, + booleanArray = { false, true }, + byteArray = { 8, 31 }, + charArray = { 'b', 'c' }, + doubleArray = { 1.2, 3.4 }, + floatArray = { 1.2000000476837158f, 3.4000000953674316f }, + intArray = { 12, 34 }, + longArray = { 12L, 34L }, + shortArray = { 12, 34 }, + classArray = { Mapper.class, CustomAnnotationWithParams.class }, + stringParam = "required parameter" +) public class MultipleArrayValuesMapperImpl implements MultipleArrayValuesMapper { } From bbf63ae1777981d1d89693d6f9d980064997cf28 Mon Sep 17 00:00:00 2001 From: Iaroslav Bogdanchikov <6546969+ibogdanchikov@users.noreply.github.com> Date: Fri, 2 Sep 2022 22:04:01 +0200 Subject: [PATCH 306/597] #2730 Add support for Jakarta XML Binding --- distribution/pom.xml | 8 +- integrationtest/pom.xml | 2 +- .../itest/tests/MavenIntegrationTest.java | 4 + .../test/resources/fullFeatureTest/pom.xml | 17 ++- .../test/resources/jakartaJaxbTest/pom.xml | 64 ++++++++++ .../itest/jakarta/jaxb/OrderDetailsDto.java | 42 +++++++ .../itest/jakarta/jaxb/OrderDto.java | 52 ++++++++ .../itest/jakarta/jaxb/OrderStatusDto.java | 35 ++++++ .../jakarta/jaxb/ShippingAddressDto.java | 50 ++++++++ .../jakarta/jaxb/SourceTargetMapper.java | 50 ++++++++ .../itest/jakarta/jaxb/SubTypeDto.java | 27 ++++ .../itest/jakarta/jaxb/SuperTypeDto.java | 27 ++++ .../src/main/resources/binding/binding.xjb | 28 +++++ .../src/main/resources/schema/test1.xsd | 36 ++++++ .../src/main/resources/schema/test2.xsd | 33 +++++ .../src/main/resources/schema/underscores.xsd | 34 +++++ .../jaxb/JakartaJaxbBasedMapperTest.java | 118 ++++++++++++++++++ .../src/test/resources/jaxbTest/pom.xml | 12 +- parent/pom.xml | 29 ++++- processor/pom.xml | 8 +- .../gem/jakarta/JakartaGemGenerator.java | 26 ++++ .../source/builtin/BuiltInMappingMethods.java | 21 +++- .../model/source/builtin/JaxbElemToValue.java | 11 +- .../JakartaXmlElementDeclSelector.java | 48 +++++++ .../selector/JavaxXmlElementDeclSelector.java | 48 +++++++ .../source/selector/MethodSelectors.java | 3 +- .../selector/XmlElementDeclSelector.java | 65 +++++++--- .../ap/internal/util/JaxbConstants.java | 3 +- .../ap/test/builtin/BuiltInTest.java | 77 ++++++++++++ .../bean/JakartaJaxbElementListProperty.java | 28 +++++ .../bean/JakartaJaxbElementProperty.java | 25 ++++ .../builtin/mapper/JakartaJaxbListMapper.java | 19 +++ .../builtin/mapper/JakartaJaxbMapper.java | 31 +++++ .../ap/testutil/WithJakartaJaxb.java | 27 ++++ 34 files changed, 1062 insertions(+), 46 deletions(-) create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/pom.xml create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDetailsDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderStatusDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/ShippingAddressDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SubTypeDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SuperTypeDto.java create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/binding/binding.xjb create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test1.xsd create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test2.xsd create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/underscores.xsd create mode 100644 integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/gem/jakarta/JakartaGemGenerator.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementListProperty.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementProperty.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbListMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaJaxb.java diff --git a/distribution/pom.xml b/distribution/pom.xml index 9ceeb505ea..11fb7c9f37 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -39,6 +39,13 @@ org.mapstruct.tools.gem gem-api + + + jakarta.xml.bind + jakarta.xml.bind-api + provided + true + @@ -187,7 +194,6 @@ javax.xml.bind jaxb-api - 2.3.1 provided true diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 364faae643..f888e226ff 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -133,8 +133,8 @@ javax.xml.bind jaxb-api - 2.3.1 provided + true diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 7d63ccf774..75513dd6c6 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -75,6 +75,10 @@ void java8Test() { void jaxbTest() { } + @ProcessorTest(baseDir = "jakartaJaxbTest") + void jakartaJaxbTest() { + } + @ProcessorTest(baseDir = "jsr330Test") void jsr330Test() { } diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index 62f7986e9c..ac69114bd6 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -83,6 +83,13 @@ joda-time joda-time + + + jakarta.xml.bind + jakarta.xml.bind-api + provided + true + @@ -93,14 +100,16 @@ - jakarta.xml.bind - jakarta.xml.bind-api - 2.3.2 + javax.xml.bind + jaxb-api + provided + true org.glassfish.jaxb jaxb-runtime - 2.3.2 + provided + true diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml b/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml new file mode 100644 index 0000000000..3aabc1c7ae --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/pom.xml @@ -0,0 +1,64 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + jakartaJaxbTest + jar + + + + jakarta.xml.bind + jakarta.xml.bind-api + provided + true + + + com.sun.xml.bind + jaxb-impl + provided + true + + + + + + + org.codehaus.mojo + jaxb2-maven-plugin + 3.1.0 + + + xjc + initialize + + xjc + + + + + + ${project.build.resources[0].directory}/binding + + + ${project.build.resources[0].directory}/schema + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDetailsDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDetailsDto.java new file mode 100644 index 0000000000..e8500633e4 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDetailsDto.java @@ -0,0 +1,42 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.jakarta.jaxb; + +import java.util.List; + +/** + * @author Sjaak Derksen + */ +public class OrderDetailsDto { + + private String name; + private List description; + private OrderStatusDto status; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getDescription() { + return description; + } + + public void setDescription(List description) { + this.description = description; + } + + public OrderStatusDto getStatus() { + return status; + } + + public void setStatus(OrderStatusDto status) { + this.status = status; + } +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDto.java new file mode 100644 index 0000000000..f94d5362ef --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderDto.java @@ -0,0 +1,52 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.jakarta.jaxb; + +import java.util.Date; + +/** + * @author Sjaak Derksen + */ +public class OrderDto { + + private Long orderNumber; + private Date orderDate; + private OrderDetailsDto orderDetails; + private ShippingAddressDto shippingAddress; + + public Long getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber(Long orderNumber) { + this.orderNumber = orderNumber; + } + + public Date getOrderDate() { + return orderDate; + } + + public void setOrderDate(Date orderDate) { + this.orderDate = orderDate; + } + + public OrderDetailsDto getOrderDetails() { + return orderDetails; + } + + public void setOrderDetails(OrderDetailsDto orderDetails) { + this.orderDetails = orderDetails; + } + + public ShippingAddressDto getShippingAddress() { + return shippingAddress; + } + + public void setShippingAddress(ShippingAddressDto shippingAddress) { + this.shippingAddress = shippingAddress; + } + +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderStatusDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderStatusDto.java new file mode 100644 index 0000000000..5da5d45c99 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/OrderStatusDto.java @@ -0,0 +1,35 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.jakarta.jaxb; + +/** + * @author Sjaak Derksen + */ +public enum OrderStatusDto { + + ORDERED( "small" ), + PROCESSED( "medium" ), + DELIVERED( "large" ); + private final String value; + + OrderStatusDto(String v) { + value = v; + } + + public String value() { + return value; + } + + public static OrderStatusDto fromValue(String v) { + for ( OrderStatusDto c : OrderStatusDto.values() ) { + if ( c.value.equals( v ) ) { + return c; + } + } + throw new IllegalArgumentException( v ); + } + +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/ShippingAddressDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/ShippingAddressDto.java new file mode 100644 index 0000000000..6bc40a19b2 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/ShippingAddressDto.java @@ -0,0 +1,50 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.jakarta.jaxb; + +/** + * @author Sjaak Derksen + */ +public class ShippingAddressDto { + + private String street; + private String houseNumber; + private String city; + private String country; + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getHouseNumber() { + return houseNumber; + } + + public void setHouseNumber(String houseNumber) { + this.houseNumber = houseNumber; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java new file mode 100644 index 0000000000..3b76aad437 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SourceTargetMapper.java @@ -0,0 +1,50 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.jakarta.jaxb; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import org.mapstruct.itest.jakarta.jaxb.xsd.test1.OrderDetailsType; +import org.mapstruct.itest.jakarta.jaxb.xsd.test1.OrderType; +import org.mapstruct.itest.jakarta.jaxb.xsd.test2.OrderStatusType; +import org.mapstruct.itest.jakarta.jaxb.xsd.test2.ShippingAddressType; +import org.mapstruct.itest.jakarta.jaxb.xsd.underscores.SubType; + + +/** + * @author Sjaak Derksen + */ +@Mapper(uses = { + org.mapstruct.itest.jakarta.jaxb.xsd.test1.ObjectFactory.class, + org.mapstruct.itest.jakarta.jaxb.xsd.test2.ObjectFactory.class, + org.mapstruct.itest.jakarta.jaxb.xsd.underscores.ObjectFactory.class +}) +public interface SourceTargetMapper { + + SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + + // source 2 target methods + OrderDto sourceToTarget(OrderType source); + + OrderDetailsDto detailsToDto(OrderDetailsType source); + + OrderStatusDto statusToDto(OrderStatusType source); + + ShippingAddressDto shippingAddressToDto(ShippingAddressType source); + + SubTypeDto subTypeToDto(SubType source); + + // target 2 source methods + OrderType targetToSource(OrderDto target); + + OrderDetailsType dtoToDetails(OrderDetailsDto target); + + OrderStatusType dtoToStatus(OrderStatusDto target); + + ShippingAddressType dtoToShippingAddress(ShippingAddressDto source); + + SubType dtoToSubType(SubTypeDto source); +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SubTypeDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SubTypeDto.java new file mode 100644 index 0000000000..88218c2771 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SubTypeDto.java @@ -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.itest.jakarta.jaxb; + +public class SubTypeDto extends SuperTypeDto { + private String declaredCamelCase; + private String declaredUnderscore; + + public String getDeclaredCamelCase() { + return declaredCamelCase; + } + + public void setDeclaredCamelCase(String declaredCamelCase) { + this.declaredCamelCase = declaredCamelCase; + } + + public String getDeclaredUnderscore() { + return declaredUnderscore; + } + + public void setDeclaredUnderscore(String declaredUnderscore) { + this.declaredUnderscore = declaredUnderscore; + } +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SuperTypeDto.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SuperTypeDto.java new file mode 100644 index 0000000000..cd0c6e22e7 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/java/org/mapstruct/itest/jakarta/jaxb/SuperTypeDto.java @@ -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.itest.jakarta.jaxb; + +public class SuperTypeDto { + private String inheritedCamelCase; + private String inheritedUnderscore; + + public String getInheritedCamelCase() { + return inheritedCamelCase; + } + + public void setInheritedCamelCase(String inheritedCamelCase) { + this.inheritedCamelCase = inheritedCamelCase; + } + + public String getInheritedUnderscore() { + return inheritedUnderscore; + } + + public void setInheritedUnderscore(String inheritedUnderscore) { + this.inheritedUnderscore = inheritedUnderscore; + } +} diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/binding/binding.xjb b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/binding/binding.xjb new file mode 100644 index 0000000000..8f26b1a1ea --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/binding/binding.xjb @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test1.xsd b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test1.xsd new file mode 100644 index 0000000000..3433b01465 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test1.xsd @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test2.xsd b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test2.xsd new file mode 100644 index 0000000000..f3b564a48e --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/test2.xsd @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/underscores.xsd b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/underscores.xsd new file mode 100644 index 0000000000..b7f5904656 --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/main/resources/schema/underscores.xsd @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java new file mode 100644 index 0000000000..b81c946d9c --- /dev/null +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java @@ -0,0 +1,118 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.jakarta.jaxb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBElement; +import jakarta.xml.bind.JAXBException; +import jakarta.xml.bind.Marshaller; + +import org.junit.Test; +import org.mapstruct.itest.jakarta.jaxb.xsd.test1.ObjectFactory; +import org.mapstruct.itest.jakarta.jaxb.xsd.test1.OrderType; +import org.mapstruct.itest.jakarta.jaxb.xsd.underscores.SubType; + +/** + * Test for generation of Jakarta JAXB based mapper implementations. + * + * @author Iaroslav Bogdanchikov + */ +public class JakartaJaxbBasedMapperTest { + + @Test + public void shouldMapJakartaJaxb() throws ParseException, JAXBException { + + SourceTargetMapper mapper = SourceTargetMapper.INSTANCE; + + OrderDto source1 = new OrderDto(); + source1.setOrderDetails( new OrderDetailsDto() ); + source1.setOrderNumber( 11L ); + source1.setOrderDate( createDate( "31-08-1982 10:20:56" ) ); + source1.setShippingAddress( new ShippingAddressDto() ); + source1.getShippingAddress().setCity( "SmallTown" ); + source1.getShippingAddress().setHouseNumber( "11a" ); + source1.getShippingAddress().setStreet( "Awesome rd" ); + source1.getShippingAddress().setCountry( "USA" ); + source1.getOrderDetails().setDescription( new ArrayList() ); + source1.getOrderDetails().setName( "Shopping list for a Mapper" ); + source1.getOrderDetails().getDescription().add( "1 MapStruct" ); + source1.getOrderDetails().getDescription().add( "3 Lines of Code" ); + source1.getOrderDetails().getDescription().add( "1 Dose of Luck" ); + source1.getOrderDetails().setStatus( OrderStatusDto.ORDERED ); + + // map to JAXB + OrderType target = mapper.targetToSource( source1 ); + + // do a pretty print + ObjectFactory of = new ObjectFactory(); + System.out.println( toXml( of.createOrder( target ) ) ); + + // map back from JAXB + OrderDto source2 = mapper.sourceToTarget( target ); + + // verify that source1 and source 2 are equal + assertThat( source2.getOrderNumber() ).isEqualTo( source1.getOrderNumber() ); + assertThat( source2.getOrderDate() ).isEqualTo( source1.getOrderDate() ); + assertThat( source2.getOrderDetails().getDescription().size() ).isEqualTo( + source1.getOrderDetails().getDescription().size() + ); + assertThat( source2.getOrderDetails().getDescription().get( 0 ) ).isEqualTo( + source1.getOrderDetails().getDescription().get( 0 ) + ); + assertThat( source2.getOrderDetails().getDescription().get( 1 ) ).isEqualTo( + source1.getOrderDetails().getDescription().get( 1 ) + ); + assertThat( source2.getOrderDetails().getDescription().get( 2 ) ).isEqualTo( + source1.getOrderDetails().getDescription().get( 2 ) + ); + assertThat( source2.getOrderDetails().getName() ).isEqualTo( source1.getOrderDetails().getName() ); + assertThat( source2.getOrderDetails().getStatus() ).isEqualTo( source1.getOrderDetails().getStatus() ); + } + + @Test + public void underscores() throws ParseException, JAXBException { + + SourceTargetMapper mapper = SourceTargetMapper.INSTANCE; + + SubTypeDto source1 = new SubTypeDto(); + source1.setInheritedCamelCase("InheritedCamelCase"); + source1.setInheritedUnderscore("InheritedUnderscore"); + source1.setDeclaredCamelCase("DeclaredCamelCase"); + source1.setDeclaredUnderscore("DeclaredUnderscore"); + + SubType target = mapper.dtoToSubType( source1 ); + + SubTypeDto source2 = mapper.subTypeToDto( target ); + + assertThat( source2.getInheritedCamelCase() ).isEqualTo( source1.getInheritedCamelCase() ); + assertThat( source2.getInheritedUnderscore() ).isEqualTo( source1.getInheritedUnderscore() ); + assertThat( source2.getDeclaredCamelCase() ).isEqualTo( source1.getDeclaredCamelCase() ); + assertThat( source2.getDeclaredUnderscore() ).isEqualTo( source1.getDeclaredUnderscore() ); + } + + private Date createDate(String date) throws ParseException { + SimpleDateFormat sdf = new SimpleDateFormat( "dd-M-yyyy hh:mm:ss" ); + return sdf.parse( date ); + } + + private String toXml(JAXBElement element) throws JAXBException { + JAXBContext jc = JAXBContext.newInstance( element.getValue().getClass() ); + Marshaller marshaller = jc.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + marshaller.marshal( element, baos ); + return baos.toString(); + } +} diff --git a/integrationtest/src/test/resources/jaxbTest/pom.xml b/integrationtest/src/test/resources/jaxbTest/pom.xml index 000e7cf598..0e69e23e01 100644 --- a/integrationtest/src/test/resources/jaxbTest/pom.xml +++ b/integrationtest/src/test/resources/jaxbTest/pom.xml @@ -51,7 +51,7 @@ org.glassfish.jaxb jaxb-runtime - 2.3.2 + ${jaxb-runtime.version} @@ -66,14 +66,16 @@ - jakarta.xml.bind - jakarta.xml.bind-api - 2.3.2 + javax.xml.bind + jaxb-api + provided + true org.glassfish.jaxb jaxb-runtime - 2.3.2 + provided + true diff --git a/parent/pom.xml b/parent/pom.xml index 426405b4b7..3f536034c7 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -42,6 +42,7 @@ --> 1.8 3.21.2 + 2.3.2 @@ -255,6 +256,30 @@ 2.9 + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + org.glassfish.jaxb + jaxb-runtime + ${jaxb-runtime.version} + + + + jakarta.xml.bind + jakarta.xml.bind-api + 3.0.1 + + + com.sun.xml.bind + jaxb-impl + 3.0.2 + + org.eclipse.tycho @@ -488,12 +513,12 @@ org.codehaus.mojo animal-sniffer-maven-plugin - 1.17 + 1.20 org.ow2.asm asm - 6.2.1 + 7.0 diff --git a/processor/pom.xml b/processor/pom.xml index ff7864f32e..12fc615f5f 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -137,6 +137,13 @@ joda-time test + + + jakarta.xml.bind + jakarta.xml.bind-api + provided + true + @@ -375,7 +382,6 @@ javax.xml.bind jaxb-api - 2.3.1 provided true diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/jakarta/JakartaGemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/jakarta/JakartaGemGenerator.java new file mode 100644 index 0000000000..93bdebeaef --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/jakarta/JakartaGemGenerator.java @@ -0,0 +1,26 @@ +/* + * 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.internal.gem.jakarta; + +import jakarta.xml.bind.annotation.XmlElementDecl; +import jakarta.xml.bind.annotation.XmlElementRef; +import org.mapstruct.tools.gem.GemDefinition; + +/** + * This class is a temporary solution to an issue in the Gem Tools library. + * + *

    + * This class can be merged with {@link org.mapstruct.ap.internal.gem.GemGenerator} + * after the mentioned issue is resolved. + *

    + * + * @see Gem Tools issue #10 + * @author Iaroslav Bogdanchikov + */ +@GemDefinition(XmlElementDecl.class) +@GemDefinition(XmlElementRef.class) +class JakartaGemGenerator { +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java index 0edae7f10f..6cd1605b23 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.List; +import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.JaxbConstants; import org.mapstruct.ap.internal.util.JodaTimeConstants; @@ -24,7 +25,7 @@ public class BuiltInMappingMethods { public BuiltInMappingMethods(TypeFactory typeFactory) { boolean isXmlGregorianCalendarPresent = isXmlGregorianCalendarAvailable( typeFactory ); - builtInMethods = new ArrayList<>( 20 ); + builtInMethods = new ArrayList<>( 21 ); if ( isXmlGregorianCalendarPresent ) { builtInMethods.add( new DateToXmlGregorianCalendar( typeFactory ) ); builtInMethods.add( new XmlGregorianCalendarToDate( typeFactory ) ); @@ -39,8 +40,14 @@ public BuiltInMappingMethods(TypeFactory typeFactory) { builtInMethods.add( new XmlGregorianCalendarToLocalDateTime( typeFactory ) ); } - if ( isJaxbAvailable( typeFactory ) ) { - builtInMethods.add( new JaxbElemToValue( typeFactory ) ); + if ( isJavaxJaxbAvailable( typeFactory ) ) { + Type type = typeFactory.getType( JaxbConstants.JAVAX_JAXB_ELEMENT_FQN ); + builtInMethods.add( new JaxbElemToValue( type ) ); + } + + if ( isJakartaJaxbAvailable( typeFactory ) ) { + Type type = typeFactory.getType( JaxbConstants.JAKARTA_JAXB_ELEMENT_FQN ); + builtInMethods.add( new JaxbElemToValue( type ) ); } builtInMethods.add( new ZonedDateTimeToCalendar( typeFactory ) ); @@ -58,8 +65,12 @@ public BuiltInMappingMethods(TypeFactory typeFactory) { } } - private static boolean isJaxbAvailable(TypeFactory typeFactory) { - return typeFactory.isTypeAvailable( JaxbConstants.JAXB_ELEMENT_FQN ); + private static boolean isJavaxJaxbAvailable(TypeFactory typeFactory) { + return typeFactory.isTypeAvailable( JaxbConstants.JAVAX_JAXB_ELEMENT_FQN ); + } + + private static boolean isJakartaJaxbAvailable(TypeFactory typeFactory) { + return typeFactory.isTypeAvailable( JaxbConstants.JAKARTA_JAXB_ELEMENT_FQN ); } private static boolean isXmlGregorianCalendarAvailable(TypeFactory typeFactory) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java index 0d3d4c30af..2a5d1639e6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JaxbElemToValue.java @@ -5,26 +5,23 @@ */ package org.mapstruct.ap.internal.model.source.builtin; -import static org.mapstruct.ap.internal.util.Collections.asSet; - import java.util.Set; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.util.JaxbConstants; + +import static org.mapstruct.ap.internal.util.Collections.asSet; /** * @author Sjaak Derksen */ -public class JaxbElemToValue extends BuiltInMethod { +class JaxbElemToValue extends BuiltInMethod { private final Parameter parameter; private final Type returnType; private final Set importTypes; - public JaxbElemToValue(TypeFactory typeFactory) { - Type type = typeFactory.getType( JaxbConstants.JAXB_ELEMENT_FQN ); + JaxbElemToValue(Type type) { this.parameter = new Parameter( "element", type ); this.returnType = type.getTypeParameters().get( 0 ); this.importTypes = asSet( parameter.getType() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java new file mode 100644 index 0000000000..df5cd848a5 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java @@ -0,0 +1,48 @@ +/* + * 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.internal.model.source.selector; + +import javax.lang.model.element.Element; + +import org.mapstruct.ap.internal.gem.jakarta.XmlElementDeclGem; +import org.mapstruct.ap.internal.gem.jakarta.XmlElementRefGem; +import org.mapstruct.ap.internal.util.TypeUtils; + +/** + * The concrete implementation of the {@link XmlElementDeclSelector} that + * works with {@link jakarta.xml.bind.annotation.XmlElementRef} and + * {@link jakarta.xml.bind.annotation.XmlElementDecl}. + * + * @author Iaroslav Bogdanchikov + */ +class JakartaXmlElementDeclSelector extends XmlElementDeclSelector { + + JakartaXmlElementDeclSelector(TypeUtils typeUtils) { + super( typeUtils ); + } + + @Override + XmlElementDeclInfo getXmlElementDeclInfo(Element element) { + XmlElementDeclGem gem = XmlElementDeclGem.instanceOn( element ); + + if (gem == null) { + return null; + } + + return new XmlElementDeclInfo( gem.name().get(), gem.scope().get() ); + } + + @Override + XmlElementRefInfo getXmlElementRefInfo(Element element) { + XmlElementRefGem gem = XmlElementRefGem.instanceOn( element ); + + if (gem == null) { + return null; + } + + return new XmlElementRefInfo( gem.name().get(), gem.type().get() ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java new file mode 100644 index 0000000000..1d02e97e90 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java @@ -0,0 +1,48 @@ +/* + * 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.internal.model.source.selector; + +import javax.lang.model.element.Element; + +import org.mapstruct.ap.internal.gem.XmlElementDeclGem; +import org.mapstruct.ap.internal.gem.XmlElementRefGem; +import org.mapstruct.ap.internal.util.TypeUtils; + +/** + * The concrete implementation of the {@link XmlElementDeclSelector} that + * works with {@link javax.xml.bind.annotation.XmlElementRef} and + * {@link javax.xml.bind.annotation.XmlElementDecl}. + * + * @author Iaroslav Bogdanchikov + */ +class JavaxXmlElementDeclSelector extends XmlElementDeclSelector { + + JavaxXmlElementDeclSelector(TypeUtils typeUtils) { + super( typeUtils ); + } + + @Override + XmlElementDeclInfo getXmlElementDeclInfo(Element element) { + XmlElementDeclGem gem = XmlElementDeclGem.instanceOn( element ); + + if (gem == null) { + return null; + } + + return new XmlElementDeclInfo( gem.name().get(), gem.scope().get() ); + } + + @Override + XmlElementRefInfo getXmlElementRefInfo(Element element) { + XmlElementRefGem gem = XmlElementRefGem.instanceOn( element ); + + if (gem == null) { + return null; + } + + return new XmlElementRefInfo( gem.name().get(), gem.type().get() ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java index de429174da..519e1c3d6d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java @@ -32,7 +32,8 @@ public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils, TypeFacto new TypeSelector( typeFactory, messager ), new QualifierSelector( typeUtils, elementUtils ), new TargetTypeSelector( typeUtils ), - new XmlElementDeclSelector( typeUtils ), + new JavaxXmlElementDeclSelector( typeUtils ), + new JakartaXmlElementDeclSelector( typeUtils ), new InheritanceSelector(), new CreateOrUpdateSelector(), new SourceRhsSelector(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java index 7bdae0b771..91b4b5ca10 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java @@ -11,19 +11,17 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; -import org.mapstruct.ap.internal.util.TypeUtils; -import org.mapstruct.ap.internal.gem.XmlElementRefGem; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SourceMethod; -import org.mapstruct.ap.internal.gem.XmlElementDeclGem; +import org.mapstruct.ap.internal.util.TypeUtils; /** - * Finds the {@link javax.xml.bind.annotation.XmlElementRef} annotation on a field (of the mapping result type or its + * Finds the {@code XmlElementRef} annotation on a field (of the mapping result type or its * super types) matching the * target property name. Then selects those methods with matching {@code name} and {@code scope} attributes of the - * {@link javax.xml.bind.annotation.XmlElementDecl} annotation, if that is present. Matching happens in the following + * {@code XmlElementDecl} annotation, if that is present. Matching happens in the following * order: *
      *
    1. Name and Scope matches
    2. @@ -34,12 +32,15 @@ * the given method is not annotated with {@code XmlElementDecl} it will be considered as matching. * * @author Sjaak Derksen + * + * @see JavaxXmlElementDeclSelector + * @see JakartaXmlElementDeclSelector */ -public class XmlElementDeclSelector implements MethodSelector { +abstract class XmlElementDeclSelector implements MethodSelector { private final TypeUtils typeUtils; - public XmlElementDeclSelector(TypeUtils typeUtils) { + XmlElementDeclSelector(TypeUtils typeUtils) { this.typeUtils = typeUtils; } @@ -63,15 +64,14 @@ public List> getMatchingMethods(Method mapp } SourceMethod candidateMethod = (SourceMethod) candidate.getMethod(); - XmlElementDeclGem xmlElementDecl = - XmlElementDeclGem.instanceOn( candidateMethod.getExecutable() ); + XmlElementDeclInfo xmlElementDeclInfo = getXmlElementDeclInfo( candidateMethod.getExecutable() ); - if ( xmlElementDecl == null ) { + if ( xmlElementDeclInfo == null ) { continue; } - String name = xmlElementDecl.name().get(); - TypeMirror scope = xmlElementDecl.scope().getValue(); + String name = xmlElementDeclInfo.nameValue(); + TypeMirror scope = xmlElementDeclInfo.scopeType(); boolean nameIsSetAndMatches = name != null && name.equals( xmlElementRefInfo.nameValue() ); boolean scopeIsSetAndMatches = @@ -142,9 +142,9 @@ private XmlElementRefInfo findXmlElementRef(Type resultType, String targetProper for ( Element enclosed : currentElement.getEnclosedElements() ) { if ( enclosed.getKind().equals( ElementKind.FIELD ) && enclosed.getSimpleName().contentEquals( targetPropertyName ) ) { - XmlElementRefGem xmlElementRef = XmlElementRefGem.instanceOn( enclosed ); - if ( xmlElementRef != null ) { - return new XmlElementRefInfo( xmlElementRef.name().get(), currentMirror ); + XmlElementRefInfo xmlElementRefInfo = getXmlElementRefInfo( enclosed ); + if ( xmlElementRefInfo != null ) { + return new XmlElementRefInfo( xmlElementRefInfo.nameValue(), currentMirror ); } } } @@ -154,7 +154,11 @@ private XmlElementRefInfo findXmlElementRef(Type resultType, String targetProper return defaultInfo; } - private static class XmlElementRefInfo { + abstract XmlElementDeclInfo getXmlElementDeclInfo(Element element); + + abstract XmlElementRefInfo getXmlElementRefInfo(Element element); + + static class XmlElementRefInfo { private final String nameValue; private final TypeMirror sourceType; @@ -163,12 +167,37 @@ private static class XmlElementRefInfo { this.sourceType = sourceType; } - public String nameValue() { + String nameValue() { return nameValue; } - public TypeMirror sourceType() { + TypeMirror sourceType() { return sourceType; } } + + /** + * A class, whose purpose is to combine the use of + * {@link org.mapstruct.ap.internal.gem.XmlElementDeclGem} + * and + * {@link org.mapstruct.ap.internal.gem.jakarta.XmlElementDeclGem}. + */ + static class XmlElementDeclInfo { + + private final String nameValue; + private final TypeMirror scopeType; + + XmlElementDeclInfo(String nameValue, TypeMirror scopeType) { + this.nameValue = nameValue; + this.scopeType = scopeType; + } + + String nameValue() { + return nameValue; + } + + TypeMirror scopeType() { + return scopeType; + } + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java index db18673ba2..c89877062e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java @@ -10,7 +10,8 @@ */ public final class JaxbConstants { - public static final String JAXB_ELEMENT_FQN = "javax.xml.bind.JAXBElement"; + public static final String JAVAX_JAXB_ELEMENT_FQN = "javax.xml.bind.JAXBElement"; + public static final String JAKARTA_JAXB_ELEMENT_FQN = "jakarta.xml.bind.JAXBElement"; private JaxbConstants() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java index 2dc41b822d..c8ee922af9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/BuiltInTest.java @@ -29,6 +29,8 @@ import org.mapstruct.ap.test.builtin.bean.BigDecimalProperty; import org.mapstruct.ap.test.builtin.bean.CalendarProperty; import org.mapstruct.ap.test.builtin.bean.DateProperty; +import org.mapstruct.ap.test.builtin.bean.JakartaJaxbElementListProperty; +import org.mapstruct.ap.test.builtin.bean.JakartaJaxbElementProperty; import org.mapstruct.ap.test.builtin.bean.JaxbElementListProperty; import org.mapstruct.ap.test.builtin.bean.JaxbElementProperty; import org.mapstruct.ap.test.builtin.bean.SomeType; @@ -45,6 +47,8 @@ import org.mapstruct.ap.test.builtin.mapper.DateToCalendarMapper; import org.mapstruct.ap.test.builtin.mapper.DateToXmlGregCalMapper; import org.mapstruct.ap.test.builtin.mapper.IterableSourceTargetMapper; +import org.mapstruct.ap.test.builtin.mapper.JakartaJaxbListMapper; +import org.mapstruct.ap.test.builtin.mapper.JakartaJaxbMapper; import org.mapstruct.ap.test.builtin.mapper.JaxbListMapper; import org.mapstruct.ap.test.builtin.mapper.JaxbMapper; import org.mapstruct.ap.test.builtin.mapper.MapSourceTargetMapper; @@ -58,6 +62,7 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaJaxb; import org.mapstruct.ap.testutil.WithJavaxJaxb; import static org.assertj.core.api.Assertions.assertThat; @@ -101,6 +106,23 @@ public void shouldApplyBuiltInOnJAXBElement() { assertThat( target.publicProp ).isEqualTo( "PUBLIC TEST" ); } + @ProcessorTest + @WithClasses( { + JakartaJaxbMapper.class, + JakartaJaxbElementProperty.class, + } ) + @WithJakartaJaxb + public void shouldApplyBuiltInOnJakartaJaxbElement() { + JakartaJaxbElementProperty source = new JakartaJaxbElementProperty(); + source.setProp( createJakartaJaxb( "TEST" ) ); + source.publicProp = createJakartaJaxb( "PUBLIC TEST" ); + + StringProperty target = JakartaJaxbMapper.INSTANCE.map( source ); + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isEqualTo( "TEST" ); + assertThat( target.publicProp ).isEqualTo( "PUBLIC TEST" ); + } + @ProcessorTest @WithClasses( { JaxbMapper.class, @@ -128,6 +150,33 @@ public void shouldApplyBuiltInOnJAXBElementExtra() { assertThat( target2.getProp() ).isNotNull(); } + @ProcessorTest + @WithClasses( { + JakartaJaxbMapper.class, + JakartaJaxbElementProperty.class, + } ) + @WithJakartaJaxb + @IssueKey( "1698" ) + public void shouldApplyBuiltInOnJakartaJAXBElementExtra() { + JakartaJaxbElementProperty source = new JakartaJaxbElementProperty(); + source.setProp( createJakartaJaxb( "5" ) ); + source.publicProp = createJakartaJaxb( "5" ); + + BigDecimalProperty target = JakartaJaxbMapper.INSTANCE.mapBD( source ); + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isEqualTo( new BigDecimal( "5" ) ); + assertThat( target.publicProp ).isEqualTo( new BigDecimal( "5" ) ); + + JakartaJaxbElementProperty source2 = new JakartaJaxbElementProperty(); + source2.setProp( createJakartaJaxb( "5" ) ); + source2.publicProp = createJakartaJaxb( "5" ); + + SomeTypeProperty target2 = JakartaJaxbMapper.INSTANCE.mapSomeType( source2 ); + assertThat( target2 ).isNotNull(); + assertThat( target2.publicProp ).isNotNull(); + assertThat( target2.getProp() ).isNotNull(); + } + @ProcessorTest @WithClasses( { JaxbListMapper.class, @@ -147,6 +196,24 @@ public void shouldApplyBuiltInOnJAXBElementList() { assertThat( target.publicProp.get( 0 ) ).isEqualTo( "PUBLIC TEST2" ); } + @ProcessorTest + @WithClasses( { + JakartaJaxbListMapper.class, + JakartaJaxbElementListProperty.class, + } ) + @WithJakartaJaxb + @IssueKey( "141" ) + public void shouldApplyBuiltInOnJakartaJAXBElementList() { + JakartaJaxbElementListProperty source = new JakartaJaxbElementListProperty(); + source.setProp( createJakartaJaxbList( "TEST2" ) ); + source.publicProp = createJakartaJaxbList( "PUBLIC TEST2" ); + + StringListProperty target = JakartaJaxbListMapper.INSTANCE.map( source ); + assertThat( target ).isNotNull(); + assertThat( target.getProp().get( 0 ) ).isEqualTo( "TEST2" ); + assertThat( target.publicProp.get( 0 ) ).isEqualTo( "PUBLIC TEST2" ); + } + @ProcessorTest @WithClasses( DateToXmlGregCalMapper.class ) public void shouldApplyBuiltInOnDateToXmlGregCal() throws ParseException { @@ -414,12 +481,22 @@ private JAXBElement createJaxb(String test) { return new JAXBElement<>( new QName( "www.mapstruct.org", "test" ), String.class, test ); } + private jakarta.xml.bind.JAXBElement createJakartaJaxb(String test) { + return new jakarta.xml.bind.JAXBElement<>( new QName( "www.mapstruct.org", "test" ), String.class, test ); + } + private List> createJaxbList(String test) { List> result = new ArrayList<>(); result.add( createJaxb( test ) ); return result; } + private List> createJakartaJaxbList(String test) { + List> result = new ArrayList<>(); + result.add( createJakartaJaxb( test ) ); + return result; + } + private Date createDate(String date) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat( "dd-M-yyyy hh:mm:ss" ); return sdf.parse( date ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementListProperty.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementListProperty.java new file mode 100644 index 0000000000..f6ab537253 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementListProperty.java @@ -0,0 +1,28 @@ +/* + * 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.builtin.bean; + +import java.util.List; + +import jakarta.xml.bind.JAXBElement; + +public class JakartaJaxbElementListProperty { + + // CHECKSTYLE:OFF + public List> publicProp; + // CHECKSTYLE:ON + + private List> prop; + + public List> getProp() { + return prop; + } + + public void setProp( List> prop ) { + this.prop = prop; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementProperty.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementProperty.java new file mode 100644 index 0000000000..0336afe814 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/bean/JakartaJaxbElementProperty.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.ap.test.builtin.bean; + +import jakarta.xml.bind.JAXBElement; + +public class JakartaJaxbElementProperty { + + // CHECKSTYLE:OFF + public JAXBElement publicProp; + // CHECKSTYLE:ON + + private JAXBElement prop; + + public JAXBElement getProp() { + return prop; + } + + public void setProp( JAXBElement prop ) { + this.prop = prop; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbListMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbListMapper.java new file mode 100644 index 0000000000..602e2180f1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbListMapper.java @@ -0,0 +1,19 @@ +/* + * 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.builtin.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.builtin.bean.JakartaJaxbElementListProperty; +import org.mapstruct.ap.test.builtin.bean.StringListProperty; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface JakartaJaxbListMapper { + + JakartaJaxbListMapper INSTANCE = Mappers.getMapper( JakartaJaxbListMapper.class ); + + StringListProperty map(JakartaJaxbElementListProperty source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbMapper.java new file mode 100644 index 0000000000..9334792572 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builtin/mapper/JakartaJaxbMapper.java @@ -0,0 +1,31 @@ +/* + * 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.builtin.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.builtin.bean.BigDecimalProperty; +import org.mapstruct.ap.test.builtin.bean.JakartaJaxbElementProperty; +import org.mapstruct.ap.test.builtin.bean.SomeType; +import org.mapstruct.ap.test.builtin.bean.SomeTypeProperty; +import org.mapstruct.ap.test.builtin.bean.StringProperty; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface JakartaJaxbMapper { + + JakartaJaxbMapper INSTANCE = Mappers.getMapper( JakartaJaxbMapper.class ); + + StringProperty map(JakartaJaxbElementProperty source); + + BigDecimalProperty mapBD(JakartaJaxbElementProperty source); + + SomeTypeProperty mapSomeType(JakartaJaxbElementProperty source); + + @SuppressWarnings( "unused" ) + default SomeType map( String in ) { + return new SomeType(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaJaxb.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaJaxb.java new file mode 100644 index 0000000000..86c9d83a54 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithJakartaJaxb.java @@ -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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Jakarta XML Binding dependencies. + * + * @author Iaroslav Bogdanchikov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithTestDependency({ + "jakarta.xml.bind", +}) +public @interface WithJakartaJaxb { + +} From 97c67552882d6fb1c6ab51bf14a6c9d289e652c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Campanero=20Ortiz?= Date: Sun, 4 Sep 2022 13:58:10 +0200 Subject: [PATCH 307/597] #2963 Add support for enum to integer conversion --- .../chapter-5-data-type-conversions.asciidoc | 3 + .../ap/internal/conversion/Conversions.java | 14 +++- .../conversion/EnumToIntegerConversion.java | 38 +++++++++ .../_enum/EnumToIntegerConversionTest.java | 77 +++++++++++++++++++ .../conversion/_enum/EnumToIntegerEnum.java | 14 ++++ .../conversion/_enum/EnumToIntegerMapper.java | 19 +++++ .../conversion/_enum/EnumToIntegerSource.java | 28 +++++++ .../conversion/_enum/EnumToIntegerTarget.java | 28 +++++++ 8 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/EnumToIntegerConversion.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerEnum.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerTarget.java diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index e456af01ba..51ccb21a46 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -43,6 +43,9 @@ public interface CarMapper { ==== * Between `enum` types and `String`. +* Between `enum` types and `Integer`, according to `enum.ordinal()`. +** When converting from an `Integer`, the value needs to be less than the number of values of the enum, otherwise an `ArrayOutOfBoundsException` is thrown. + * Between big number types (`java.math.BigInteger`, `java.math.BigDecimal`) and Java primitive types (including their wrappers) as well as String. A format string as understood by `java.text.DecimalFormat` can be specified. .Conversion from BigDecimal to String diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index e012481054..e947c78571 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -41,6 +41,7 @@ public class Conversions { private final Map conversions = new HashMap<>(); private final Type enumType; private final Type stringType; + private final Type integerType; private final TypeFactory typeFactory; public Conversions(TypeFactory typeFactory) { @@ -48,6 +49,7 @@ public Conversions(TypeFactory typeFactory) { this.enumType = typeFactory.getType( Enum.class ); this.stringType = typeFactory.getType( String.class ); + this.integerType = typeFactory.getType( Integer.class ); //native types <> native types, including wrappers registerNativeTypeConversion( byte.class, Byte.class ); @@ -185,6 +187,8 @@ public Conversions(TypeFactory typeFactory) { //misc. register( Enum.class, String.class, new EnumStringConversion() ); + register( Enum.class, Integer.class, new EnumToIntegerConversion() ); + register( Enum.class, int.class, new EnumToIntegerConversion() ); register( Date.class, String.class, new DateToStringConversion() ); register( BigDecimal.class, BigInteger.class, new BigDecimalToBigIntegerConversion() ); @@ -324,10 +328,16 @@ private void register(String sourceTypeName, Class targetClass, ConversionPro } public ConversionProvider getConversion(Type sourceType, Type targetType) { - if ( sourceType.isEnumType() && targetType.equals( stringType ) ) { + if ( sourceType.isEnumType() && + ( targetType.equals( stringType ) || + targetType.getBoxedEquivalent().equals( integerType ) ) + ) { sourceType = enumType; } - else if ( targetType.isEnumType() && sourceType.equals( stringType ) ) { + else if ( targetType.isEnumType() && + ( sourceType.equals( stringType ) || + sourceType.getBoxedEquivalent().equals( integerType ) ) + ) { targetType = enumType; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/EnumToIntegerConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/EnumToIntegerConversion.java new file mode 100644 index 0000000000..b43dc4a48f --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/EnumToIntegerConversion.java @@ -0,0 +1,38 @@ +/* + * 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.internal.conversion; + +import static org.mapstruct.ap.internal.util.Collections.asSet; + +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * Conversion between {@link Enum} and {@link Integer} types. + * + * @author Jose Carlos Campanero Ortiz + */ +public class EnumToIntegerConversion extends SimpleConversion { + + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".ordinal()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return conversionContext.getTargetType().createReferenceName() + ".values()[ ]"; + } + + @Override + protected Set getFromConversionImportTypes(ConversionContext conversionContext) { + return asSet( + conversionContext.getTargetType() + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerConversionTest.java new file mode 100644 index 0000000000..1302c22bc9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerConversionTest.java @@ -0,0 +1,77 @@ +/* + * 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.conversion._enum; + +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; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Tests conversions between {@link Enum} and Integer. + * + * @author Jose Carlos Campanero Ortiz + */ +@IssueKey("2963") +@WithClasses({ + EnumToIntegerSource.class, + EnumToIntegerTarget.class, + EnumToIntegerMapper.class, + EnumToIntegerEnum.class +}) +public class EnumToIntegerConversionTest { + + @ProcessorTest + public void shouldApplyEnumToIntegerConversion() { + EnumToIntegerSource source = new EnumToIntegerSource(); + + for ( EnumToIntegerEnum value: EnumToIntegerEnum.values() ) { + source.setEnumValue( value ); + + EnumToIntegerTarget target = EnumToIntegerMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getEnumValue() ).isEqualTo( source.getEnumValue().ordinal() ); + } + } + + @ProcessorTest + public void shouldApplyReverseEnumToIntegerConversion() { + EnumToIntegerTarget target = new EnumToIntegerTarget(); + + int numberOfValues = EnumToIntegerEnum.values().length; + for ( int value = 0; value < numberOfValues; value++ ) { + target.setEnumValue( value ); + + EnumToIntegerSource source = EnumToIntegerMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getEnumValue() ).isEqualTo( EnumToIntegerEnum.values()[ target.getEnumValue() ] ); + } + } + + @ProcessorTest + public void shouldHandleOutOfBoundsEnumOrdinal() { + EnumToIntegerTarget target = new EnumToIntegerTarget(); + target.setInvalidEnumValue( EnumToIntegerEnum.values().length + 1 ); + + assertThatThrownBy( () -> EnumToIntegerMapper.INSTANCE.targetToSource( target ) ) + .isInstanceOf( ArrayIndexOutOfBoundsException.class ); + } + + @ProcessorTest + public void shouldHandleNullIntegerValue() { + EnumToIntegerTarget target = new EnumToIntegerTarget(); + + EnumToIntegerSource source = EnumToIntegerMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getEnumValue() ).isNull(); + assertThat( source.getInvalidEnumValue() ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerEnum.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerEnum.java new file mode 100644 index 0000000000..4aad7bef57 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerEnum.java @@ -0,0 +1,14 @@ +/* + * 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.conversion._enum; + +public enum EnumToIntegerEnum { + ARBITRARY_VALUE_ZERO, + ARBITRARY_VALUE_ONE, + ARBITRARY_VALUE_TWO, + ARBITRARY_VALUE_THREE +} + diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerMapper.java new file mode 100644 index 0000000000..946ee7c463 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerMapper.java @@ -0,0 +1,19 @@ +/* + * 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.conversion._enum; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface EnumToIntegerMapper { + EnumToIntegerMapper INSTANCE = Mappers.getMapper( EnumToIntegerMapper.class ); + + EnumToIntegerTarget sourceToTarget(EnumToIntegerSource source); + + EnumToIntegerSource targetToSource(EnumToIntegerTarget target); +} + diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerSource.java new file mode 100644 index 0000000000..4e60311d9b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerSource.java @@ -0,0 +1,28 @@ +/* + * 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.conversion._enum; + +public class EnumToIntegerSource { + private EnumToIntegerEnum enumValue; + + private EnumToIntegerEnum invalidEnumValue; + + public EnumToIntegerEnum getEnumValue() { + return enumValue; + } + + public void setEnumValue(final EnumToIntegerEnum enumValue) { + this.enumValue = enumValue; + } + + public EnumToIntegerEnum getInvalidEnumValue() { + return invalidEnumValue; + } + + public void setInvalidEnumValue(EnumToIntegerEnum invalidEnumValue) { + this.invalidEnumValue = invalidEnumValue; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerTarget.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerTarget.java new file mode 100644 index 0000000000..a8819c27ed --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerTarget.java @@ -0,0 +1,28 @@ +/* + * 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.conversion._enum; + +public class EnumToIntegerTarget { + private Integer enumValue; + + private Integer invalidEnumValue; + + public Integer getEnumValue() { + return enumValue; + } + + public void setEnumValue(final Integer enumValue) { + this.enumValue = enumValue; + } + + public Integer getInvalidEnumValue() { + return invalidEnumValue; + } + + public void setInvalidEnumValue(Integer invalidEnumValue) { + this.invalidEnumValue = invalidEnumValue; + } +} From d593afed694e407c108596df584bd7fc739c2f5c Mon Sep 17 00:00:00 2001 From: Prasanth Omanakuttan Date: Mon, 12 Sep 2022 22:15:22 +0530 Subject: [PATCH 308/597] Avoid unnecessary unboxing of Boolean (#3003) --- .../java/org/mapstruct/ap/MappingProcessor.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 4f9e0fa384..2a4af558f2 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -8,7 +8,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; @@ -119,7 +118,7 @@ public class MappingProcessor extends AbstractProcessor { *

      * If the hierarchy of a mapper's source/target types is never completed (i.e. the missing super-types are not * generated by other processors), this mapper will not be generated; That's fine, the compiler will raise an error - * due to the inconsistent Java types used as source or target anyways. + * due to the inconsistent Java types used as source or target anyway. */ private Set deferredMappers = new HashSet<>(); @@ -142,15 +141,15 @@ private Options createOptions() { String unmappedSourcePolicy = processingEnv.getOptions().get( UNMAPPED_SOURCE_POLICY ); return new Options( - Boolean.valueOf( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ), - Boolean.valueOf( processingEnv.getOptions().get( SUPPRESS_GENERATOR_VERSION_INFO_COMMENT ) ), + Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ), + Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_VERSION_INFO_COMMENT ) ), unmappedTargetPolicy != null ? ReportingPolicyGem.valueOf( unmappedTargetPolicy.toUpperCase() ) : null, unmappedSourcePolicy != null ? ReportingPolicyGem.valueOf( unmappedSourcePolicy.toUpperCase() ) : null, processingEnv.getOptions().get( DEFAULT_COMPONENT_MODEL ), processingEnv.getOptions().get( DEFAULT_INJECTION_STRATEGY ), - Boolean.valueOf( processingEnv.getOptions().get( ALWAYS_GENERATE_SERVICE_FILE ) ), - Boolean.valueOf( processingEnv.getOptions().get( DISABLE_BUILDERS ) ), - Boolean.valueOf( processingEnv.getOptions().get( VERBOSE ) ) + Boolean.parseBoolean( processingEnv.getOptions().get( ALWAYS_GENERATE_SERVICE_FILE ) ), + Boolean.parseBoolean( processingEnv.getOptions().get( DISABLE_BUILDERS ) ), + Boolean.parseBoolean( processingEnv.getOptions().get( VERBOSE ) ) ); } @@ -352,7 +351,7 @@ private R process(ProcessorContext context, ModelElementProcessor p /** * Retrieves all model element processors, ordered by their priority value - * (with the method retrieval processor having the lowest priority value (1) + * (with the method retrieval processor having the lowest priority value (1)) * and the code generation processor the highest priority value. * * @return A list with all model element processors. @@ -372,7 +371,7 @@ private R process(ProcessorContext context, ModelElementProcessor p processors.add( processorIterator.next() ); } - Collections.sort( processors, new ProcessorComparator() ); + processors.sort( new ProcessorComparator() ); return processors; } From 8d670e7db72961a6a3470a8fdc85ec164007ca51 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 4 Sep 2022 09:39:27 +0200 Subject: [PATCH 309/597] #3008 Replace old issue template with new GitHub issue form templates --- .github/ISSUE_TEMPLATE/bug_report.yml | 45 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 16 ++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 43 +++++++++++++++++++++ ISSUE_TEMPLATE.md | 9 ----- 4 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..b6703e1800 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,45 @@ +name: Bug report +description: Create a report and help us improve +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Please fill in all required fields with as many details as possible. + - type: textarea + id: expected + attributes: + label: Expected behavior + description: | + Describe what you were expecting MapStruct to do + placeholder: | + Here you can also add the generated code that you would like MapStruct to generate + - type: textarea + id: actual + attributes: + label: Actual behavior + description: | + Describe what you observed MapStruct did instead + placeholder: | + Here you can also add the generated code that MapStruct generated + - type: textarea + id: steps + attributes: + label: Steps to reproduce the problem + description: | + - Share your mapping configuration + - An [MCVE (Minimal Complete Verifiable Example)](https://stackoverflow.com/help/minimal-reproducible-example) can be helpful to provide a complete reproduction case + placeholder: | + Share your MapStruct configuration + validations: + required: true + - type: input + id: mapstruct-version + attributes: + label: MapStruct Version + description: | + Which MapStruct version did you use? + Note: While you can obviously continue using older versions of MapStruct, it may well be that your bug is already fixed. If you're using an older version, please also try to reproduce the bug in the latest version of MapStruct before reporting it. + placeholder: ex. MapStruct 1.5.2 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..5b7ced86a4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,16 @@ +contact_links: + - name: MapStruct Discussions + url: https://github.com/mapstruct/mapstruct/discussions + about: Please use the MapStruct GitHub Discussions for open ended discussions and to reach out to the community. + - name: Stack Overflow + url: https://stackoverflow.com/questions/tagged/mapstruct + about: For questions about how to use MapStruct, consider asking your question on Stack Overflow, tagged [mapstruct]. + - name: Documentation + url: https://mapstruct.org/documentation/stable/reference/html/ + about: The MapStruct reference documentation. + - name: Gitter Chat + url: https://gitter.im/mapstruct/mapstruct-users + about: For realtime communication with the MapStruct community, consider writing in our Gitter chat room. + - name: MapStruct Examples + url: https://github.com/mapstruct/mapstruct-examples + about: Some examples of what can be achieved with MapStruct. (contributions are always welcome) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..191bba8345 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,43 @@ +name: Feature Request +description: Suggest an idea +body: + - type: markdown + attributes: + value: | + Please describe the use-case you have. This will better help us understand the context in which you're looking for a new feature. + - type: textarea + id: use-case + attributes: + label: Use case + description: | + Please describe the use-case you have. This will better help us understand the context in which you're looking for a new feature. + placeholder: Describe the use-case here + validations: + required: true + - type: textarea + id: solution + attributes: + label: Generated Code + description: | + Please describe the possible generated code you'd like to see in MapStruct generate. + Please note, it's not always easy to describe a good solution. Describing the use-case above is much more important to us. + placeholder: Describe the possible solution here. + validations: + required: false + - type: textarea + id: workarounds + attributes: + label: Possible workarounds + description: | + Please describe the possible workarounds you've implemented to work around the lacking functionality. + placeholder: Describe the possible workarounds here. + validations: + required: false + - type: input + id: mapstruct-version + attributes: + label: MapStruct Version + description: What MapStruct version and edition did you try? + placeholder: ex. MapStruct 1.5.2 + validations: + required: false diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index ecaf441514..0000000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,9 +0,0 @@ -- [ ] Is this an issue (and hence not a question)? - -If this is a question how to use MapStruct there are several resources available. -- Our reference- and API [documentation](http://mapstruct.org/documentation/reference-guide/). -- Our [examples](https://github.com/mapstruct/mapstruct-examples) repository (contributions always welcome) -- Our [FAQ](http://mapstruct.org/faq/) -- [StackOverflow](https://stackoverflow.com), tag MapStruct -- [Gitter](https://gitter.im/mapstruct/mapstruct-users) (you usually get fast feedback) -- Our [google group](https://groups.google.com/forum/#!forum/mapstruct-users) \ No newline at end of file From 811cd569bb80ed45f0da74cb603d9bfea774ad0c Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 27 Sep 2022 02:02:01 +0900 Subject: [PATCH 310/597] Javadoc and documentation polishing (#3026) --- core/src/main/java/org/mapstruct/EnumMapping.java | 2 +- .../java/org/mapstruct/NullValueCheckStrategy.java | 2 +- .../mapstruct/ap/internal/model/HelperMethod.java | 2 +- .../internal/model/source/builtin/BuiltInMethod.java | 2 +- .../java/org/mapstruct/ap/internal/util/Message.java | 2 +- .../ap/internal/model/StreamMappingMethod.ftl | 2 +- .../mapstruct/ap/test/bugs/_1719/Issue1719Test.java | 4 ++-- .../test/context/ContextParameterErroneousTest.java | 2 +- readme.md | 12 ++++++------ 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/mapstruct/EnumMapping.java b/core/src/main/java/org/mapstruct/EnumMapping.java index ba3a5e0cb0..375f969b01 100644 --- a/core/src/main/java/org/mapstruct/EnumMapping.java +++ b/core/src/main/java/org/mapstruct/EnumMapping.java @@ -98,7 +98,7 @@ *

        *
      • {@link MappingConstants#SUFFIX_TRANSFORMATION} - applies the given {@link #configuration()} as a * suffix to the source enum
      • - *
      • {@link MappingConstants#STRIP_SUFFIX_TRANSFORMATION} - strips the the given {@link #configuration()} + *
      • {@link MappingConstants#STRIP_SUFFIX_TRANSFORMATION} - strips the given {@link #configuration()} * from the end of the source enum
      • *
      • {@link MappingConstants#PREFIX_TRANSFORMATION} - applies the given {@link #configuration()} as a * prefix to the source enum
      • diff --git a/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java b/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java index 446b879d92..22dba7c58b 100644 --- a/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java +++ b/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java @@ -8,7 +8,7 @@ /** * Strategy for dealing with null source values. * - * Note: This strategy is not in effect when the a specific source presence check method is defined + * Note: This strategy is not in effect when a specific source presence check method is defined * in the service provider interface (SPI). *

        * Note: some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java index 35b35aee73..6ff85c585f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/HelperMethod.java @@ -176,7 +176,7 @@ public boolean equals(Object obj) { * * @param parameter source * @param returnType target - * @return {@code true}, iff the the type variables match + * @return {@code true}, iff the type variables match */ public boolean doTypeVarsMatch(Type parameter, Type returnType) { return true; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java index f46b201576..6d41872159 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMethod.java @@ -190,7 +190,7 @@ public boolean equals(Object obj) { * * @param parameter source * @param returnType target - * @return {@code true}, iff the the type variables match + * @return {@code true}, iff the type variables match */ public boolean doTypeVarsMatch(Type parameter, Type returnType) { return true; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 92951ad521..29600489fb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -159,7 +159,7 @@ public enum Message { RETRIEVAL_NO_INPUT_ARGS( "Can't generate mapping method with no input arguments." ), RETRIEVAL_DUPLICATE_MAPPING_TARGETS( "Can't generate mapping method with more than one @MappingTarget parameter." ), RETRIEVAL_VOID_MAPPING_METHOD( "Can't generate mapping method with return type void." ), - RETRIEVAL_NON_ASSIGNABLE_RESULTTYPE( "The result type is not assignable to the the return type." ), + RETRIEVAL_NON_ASSIGNABLE_RESULTTYPE( "The result type is not assignable to the return type." ), RETRIEVAL_ITERABLE_TO_NON_ITERABLE( "Can't generate mapping method from iterable type from java stdlib to non-iterable type." ), RETRIEVAL_MAPPING_HAS_TARGET_TYPE_PARAMETER( "Can't generate mapping method that has a parameter annotated with @TargetType." ), RETRIEVAL_NON_ITERABLE_TO_ITERABLE( "Can't generate mapping method from non-iterable type to iterable type from java stdlib." ), diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl index 7b5b44bd56..29977bdd5d 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl @@ -114,7 +114,7 @@ <#else> <#-- Streams are immutable so we can't update them --> <#if !existingInstanceMapping> - <#--TODO fhr: after the the result is no longer the same instance, how does it affect the + <#--TODO fhr: after the result is no longer the same instance, how does it affect the Before mapping methods. Does it even make sense to have before mapping on a stream? --> <#if sourceParameter.type.arrayType> <@returnLocalVarDefOrUpdate>Stream.of( ${sourceParameter.name} )<@streamMapSupplier />; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1719/Issue1719Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1719/Issue1719Test.java index 182e1849be..7a8665e49f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1719/Issue1719Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1719/Issue1719Test.java @@ -23,8 +23,8 @@ public class Issue1719Test { /** * For adder methods MapStuct cannot generate an update method. MapStruct would cannot know how to remove objects - * from the child-parent relation. It cannot even assume that the the collection can be cleared at forehand. - * Therefore the only sensible choice is for MapStruct to create a create method for the target elements. + * from the child-parent relation. It cannot even assume that the collection can be cleared at forehand. + * Therefore, the only sensible choice is for MapStruct to create a create method for the target elements. */ @ProcessorTest @WithClasses(Issue1719Mapper.class) diff --git a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java index 77fefe756e..46bb08cb70 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/context/ContextParameterErroneousTest.java @@ -19,7 +19,7 @@ /** * Tests the erroneous usage of the {@link Context} annotation in the following situations: *

          - *
        • using the the same context parameter type twice in the same method + *
        • using the same context parameter type twice in the same method *
        * * @author Andreas Gudian diff --git a/readme.md b/readme.md index e5811060a6..e1ee8f9deb 100644 --- a/readme.md +++ b/readme.md @@ -114,7 +114,7 @@ plugins { dependencies { ... - compile 'org.mapstruct:mapstruct:1.5.2.Final' + implementation 'org.mapstruct:mapstruct:1.5.2.Final' annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.2.Final' testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.2.Final' // if you are using mapstruct in test code @@ -122,7 +122,7 @@ dependencies { ... ``` -If you don't work with a dependency management tool, you can obtain a distribution bundle from [SourceForge](https://sourceforge.net/projects/mapstruct/files/). +If you don't work with a dependency management tool, you can obtain a distribution bundle from [Releases page](https://github.com/mapstruct/mapstruct/releases). ## Documentation and getting help @@ -132,16 +132,16 @@ To learn more about MapStruct, refer to the [project homepage](http://mapstruct. MapStruct uses Maven for its build. Java 11 is required for building MapStruct from source. To build the complete project, run - mvn clean install + ./mvnw clean install from the root of the project directory. To skip the distribution module, run - mvn clean install -DskipDistribution=true + ./mvnw clean install -DskipDistribution=true ## Importing into IDE -MapStruct uses the gem annotation processor to generate mapping gems for it's own annotations. -Therefore for seamless integration within an IDE annotation processing needs to be enabled. +MapStruct uses the gem annotation processor to generate mapping gems for its own annotations. +Therefore, for seamless integration within an IDE annotation processing needs to be enabled. ### IntelliJ From 73e8fd6152153ba8c2f76fc5e9a84b16d33f6a0b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Tue, 27 Sep 2022 09:35:54 +0200 Subject: [PATCH 311/597] #2840, #2913, #2921: MethodMatcher should not match widening methods In the MethodMatcher we need to do a special check when the target type is primitive. The reason for that is that a Long is assignable to a primitive double. However, doing that means that information can be lost and thus we should not pick such methods. When the target type is primitive, then a method will be matched if and only if boxed equivalent of the target type is assignable to the boxed equivalent of the candidate return type --- .../internal/model/source/MethodMatcher.java | 11 +++ .../ap/test/bugs/_2840/Issue2840Mapper.java | 51 +++++++++++ .../ap/test/bugs/_2840/Issue2840Test.java | 31 +++++++ .../ap/test/bugs/_2913/Issue2913Mapper.java | 85 +++++++++++++++++++ .../ap/test/bugs/_2913/Issue2913Test.java | 35 ++++++++ .../ap/test/bugs/_2921/Issue2921Mapper.java | 49 +++++++++++ .../ap/test/bugs/_2921/Issue2921Test.java | 28 ++++++ 7 files changed, 290 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2840/Issue2840Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2840/Issue2840Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2913/Issue2913Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2913/Issue2913Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2921/Issue2921Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2921/Issue2921Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index baa3d7eac5..42c26f09a0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -82,6 +82,17 @@ boolean matches(List sourceTypes, Type targetType) { // (the relation target / target type, target type being a class) if ( !analyser.candidateReturnType.isVoid() ) { + if ( targetType.isPrimitive() ) { + // If the target type is primitive + // then we are going to check if its boxed equivalent + // is assignable to the candidate return type + // This is done because primitives can be assigned from their own narrower counterparts + // directly without any casting. + // e.g. a Long is assignable to a primitive double + // However, in order not to lose information we are not going to allow this + return targetType.getBoxedEquivalent() + .isAssignableTo( analyser.candidateReturnType.getBoxedEquivalent() ); + } if ( !( analyser.candidateReturnType.isAssignableTo( targetType ) ) ) { return false; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2840/Issue2840Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2840/Issue2840Mapper.java new file mode 100644 index 0000000000..d2baac8cac --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2840/Issue2840Mapper.java @@ -0,0 +1,51 @@ +/* + * 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._2840; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2840Mapper { + + Issue2840Mapper INSTANCE = + Mappers.getMapper( Issue2840Mapper.class ); + + Issue2840Mapper.Target map(Short shortValue, Integer intValue); + + default int toInt(Number number) { + return number.intValue() + 5; + } + + default short toShort(Number number) { + return (short) (number.shortValue() + 10); + } + + class Target { + + private int intValue; + private short shortValue; + + public int getIntValue() { + return intValue; + } + + public void setIntValue(int intValue) { + this.intValue = intValue; + } + + public short getShortValue() { + return shortValue; + } + + public void setShortValue(short shortValue) { + this.shortValue = shortValue; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2840/Issue2840Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2840/Issue2840Test.java new file mode 100644 index 0000000000..1c6b19306f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2840/Issue2840Test.java @@ -0,0 +1,31 @@ + +/* + * 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._2840; + +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 Filip Hrisafov + */ +@IssueKey("2840") +@WithClasses({ + Issue2840Mapper.class, +}) +class Issue2840Test { + + @ProcessorTest + void shouldUseMethodWithMostSpecificReturnType() { + Issue2840Mapper.Target target = Issue2840Mapper.INSTANCE.map( (short) 10, 50 ); + + assertThat( target.getShortValue() ).isEqualTo( (short) 20 ); + assertThat( target.getIntValue() ).isEqualTo( 55 ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2913/Issue2913Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2913/Issue2913Mapper.java new file mode 100644 index 0000000000..095f5457fd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2913/Issue2913Mapper.java @@ -0,0 +1,85 @@ +/* + * 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._2913; + +import java.math.BigDecimal; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2913Mapper { + + Issue2913Mapper INSTANCE = Mappers.getMapper( Issue2913Mapper.class ); + + @Mapping(target = "doublePrimitiveValue", source = "rounding") + @Mapping(target = "doubleValue", source = "rounding") + @Mapping(target = "longPrimitiveValue", source = "rounding") + @Mapping(target = "longValue", source = "rounding") + Target map(Source source); + + default Long mapAmount(BigDecimal amount) { + return amount != null ? amount.movePointRight( 2 ).longValue() : null; + } + + class Target { + + private double doublePrimitiveValue; + private Double doubleValue; + private long longPrimitiveValue; + private Long longValue; + + public double getDoublePrimitiveValue() { + return doublePrimitiveValue; + } + + public void setDoublePrimitiveValue(double doublePrimitiveValue) { + this.doublePrimitiveValue = doublePrimitiveValue; + } + + public Double getDoubleValue() { + return doubleValue; + } + + public void setDoubleValue(Double doubleValue) { + this.doubleValue = doubleValue; + } + + public long getLongPrimitiveValue() { + return longPrimitiveValue; + } + + public void setLongPrimitiveValue(long longPrimitiveValue) { + this.longPrimitiveValue = longPrimitiveValue; + } + + public Long getLongValue() { + return longValue; + } + + public void setLongValue(Long longValue) { + this.longValue = longValue; + } + } + + class Source { + + private final BigDecimal rounding; + + public Source(BigDecimal rounding) { + this.rounding = rounding; + } + + public BigDecimal getRounding() { + return rounding; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2913/Issue2913Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2913/Issue2913Test.java new file mode 100644 index 0000000000..7257c9ab7e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2913/Issue2913Test.java @@ -0,0 +1,35 @@ +/* + * 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._2913; + +import java.math.BigDecimal; + +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 Filip Hrisafov + */ +@IssueKey("2913") +@WithClasses({ + Issue2913Mapper.class, +}) +class Issue2913Test { + + @ProcessorTest + void shouldNotWidenWithUserDefinedMethods() { + Issue2913Mapper.Source source = new Issue2913Mapper.Source( BigDecimal.valueOf( 10.543 ) ); + Issue2913Mapper.Target target = Issue2913Mapper.INSTANCE.map( source ); + + assertThat( target.getDoubleValue() ).isEqualTo( 10.543 ); + assertThat( target.getDoublePrimitiveValue() ).isEqualTo( 10.543 ); + assertThat( target.getLongValue() ).isEqualTo( 1054 ); + assertThat( target.getLongPrimitiveValue() ).isEqualTo( 1054 ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2921/Issue2921Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2921/Issue2921Mapper.java new file mode 100644 index 0000000000..d686fcbc16 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2921/Issue2921Mapper.java @@ -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._2921; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2921Mapper { + + Issue2921Mapper INSTANCE = Mappers.getMapper( Issue2921Mapper.class ); + + Target map(Source source); + + default Short toShort(Integer value) { + throw new UnsupportedOperationException( "toShort method should not be used" ); + } + + class Source { + private final Integer value; + + public Source(Integer value) { + this.value = value; + } + + public Integer getValue() { + return value; + } + } + + class Target { + private final int value; + + public Target(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2921/Issue2921Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2921/Issue2921Test.java new file mode 100644 index 0000000000..5b8dd0386c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2921/Issue2921Test.java @@ -0,0 +1,28 @@ +/* + * 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._2921; + +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 Filip Hrisafov + */ +@IssueKey("2921") +@WithClasses({ + Issue2921Mapper.class, +}) +class Issue2921Test { + + @ProcessorTest + void shouldNotUseIntegerToShortForMappingIntegerToInt() { + Issue2921Mapper.Target target = Issue2921Mapper.INSTANCE.map( new Issue2921Mapper.Source( 10 ) ); + assertThat( target.getValue() ).isEqualTo( 10 ); + } +} From e979f506fac6aeabfa3a1993b3b986449fcb7b3a Mon Sep 17 00:00:00 2001 From: Orange Add <48479242+chenzijia12300@users.noreply.github.com> Date: Thu, 29 Sep 2022 04:17:59 +0800 Subject: [PATCH 312/597] #2773 Copy `@Deprecated` annotation from method or mapper to implementation --- ...eatureCompilationExclusionCliEnhancer.java | 1 + .../test/resources/fullFeatureTest/pom.xml | 2 + .../ap/internal/gem/GemGenerator.java | 1 + .../model/AdditionalAnnotationsBuilder.java | 42 ++++++++++++- .../test/annotatewith/AnnotateWithTest.java | 1 - .../deprecated/DeprecatedMapperWithClass.java | 13 ++++ .../DeprecatedMapperWithMethod.java | 44 +++++++++++++ .../deprecated/DeprecatedTest.java | 56 +++++++++++++++++ .../deprecated/RepeatDeprecatedMapper.java | 15 +++++ .../jdk11/DeprecatedMapperWithClass.java | 13 ++++ .../jdk11/DeprecatedMapperWithMethod.java | 44 +++++++++++++ .../deprecated/jdk11/DeprecatedTest.java | 63 +++++++++++++++++++ .../RepeatDeprecatedMapperWithParams.java | 21 +++++++ 13 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedMapperWithClass.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedMapperWithMethod.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/RepeatDeprecatedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedMapperWithClass.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedMapperWithMethod.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/RepeatDeprecatedMapperWithParams.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java index 0f261d6568..e017d1b003 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -31,6 +31,7 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces case JAVA_8: additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/annotatewith/deprecated/jdk11/*.java" ); break; case JAVA_9: // TODO find out why this fails: diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index ac69114bd6..8a62f48588 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -25,6 +25,7 @@ x x x + x @@ -45,6 +46,7 @@ ${additionalExclude2} ${additionalExclude3} ${additionalExclude4} + ${additionalExclude5} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index cbb59f4e57..5caea8a008 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -45,6 +45,7 @@ * * @author Gunnar Morling */ +@GemDefinition(Deprecated.class) @GemDefinition(AnnotateWith.class) @GemDefinition(AnnotateWith.Element.class) @GemDefinition(AnnotateWiths.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java index f28cdfef9c..3f0b2123ae 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java @@ -10,6 +10,7 @@ import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -31,6 +32,7 @@ import org.mapstruct.ap.internal.gem.AnnotateWithGem; import org.mapstruct.ap.internal.gem.AnnotateWithsGem; +import org.mapstruct.ap.internal.gem.DeprecatedGem; import org.mapstruct.ap.internal.gem.ElementGem; import org.mapstruct.ap.internal.model.annotation.AnnotationElement; import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType; @@ -55,7 +57,6 @@ public class AdditionalAnnotationsBuilder extends RepeatableAnnotations { private static final String ANNOTATE_WITH_FQN = "org.mapstruct.AnnotateWith"; private static final String ANNOTATE_WITHS_FQN = "org.mapstruct.AnnotateWiths"; - private TypeFactory typeFactory; private FormattingMessager messager; @@ -90,6 +91,45 @@ protected void addInstances(AnnotateWithsGem gem, Element source, Set getProcessedAnnotations(Element source) { + Set processedAnnotations = super.getProcessedAnnotations( source ); + return addDeprecatedAnnotation( source, processedAnnotations ); + } + + private Set addDeprecatedAnnotation(Element source, Set annotations) { + DeprecatedGem deprecatedGem = DeprecatedGem.instanceOn( source ); + if ( deprecatedGem == null ) { + return annotations; + } + Type deprecatedType = typeFactory.getType( Deprecated.class ); + if ( annotations.stream().anyMatch( annotation -> annotation.getType().equals( deprecatedType ) ) ) { + messager.printMessage( + source, + deprecatedGem.mirror(), + Message.ANNOTATE_WITH_DUPLICATE, + deprecatedType.describe() ); + return annotations; + } + List annotationElements = new ArrayList<>(); + if ( deprecatedGem.since() != null && deprecatedGem.since().hasValue() ) { + annotationElements.add( new AnnotationElement( + AnnotationElementType.STRING, + "since", + Collections.singletonList( deprecatedGem.since().getValue() ) + ) ); + } + if ( deprecatedGem.forRemoval() != null && deprecatedGem.forRemoval().hasValue() ) { + annotationElements.add( new AnnotationElement( + AnnotationElementType.BOOLEAN, + "forRemoval", + Collections.singletonList( deprecatedGem.forRemoval().getValue() ) + ) ); + } + annotations.add( new Annotation(deprecatedType, annotationElements ) ); + return annotations; + } + private void addAndValidateMapping(Set mappings, Element source, AnnotateWithGem gem, Annotation anno) { if ( anno.getType().getTypeElement().getAnnotation( Repeatable.class ) == null ) { if ( mappings.stream().anyMatch( existing -> existing.getType().equals( anno.getType() ) ) ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java index e3b20f547e..3687fe2bc0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java @@ -593,5 +593,4 @@ public void valueMappingMethodWithCorrectCustomAnnotation() throws NoSuchMethodE Method method = mapper.getClass().getMethod( "map", String.class ); assertThat( method.getAnnotation( CustomMethodOnlyAnnotation.class ) ).isNotNull(); } - } diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedMapperWithClass.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedMapperWithClass.java new file mode 100644 index 0000000000..d0164e8be0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedMapperWithClass.java @@ -0,0 +1,13 @@ +/* + * 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.annotatewith.deprecated; + +import org.mapstruct.Mapper; + +@Mapper +@Deprecated +public class DeprecatedMapperWithClass { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedMapperWithMethod.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedMapperWithMethod.java new file mode 100644 index 0000000000..b6c2c8eb62 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedMapperWithMethod.java @@ -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.annotatewith.deprecated; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.annotatewith.CustomMethodOnlyAnnotation; + +@Mapper +public interface DeprecatedMapperWithMethod { + + @AnnotateWith(CustomMethodOnlyAnnotation.class) + @Deprecated + Target map(Source source); + + class Source { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class Target { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedTest.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedTest.java new file mode 100644 index 0000000000..5abca7171e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/DeprecatedTest.java @@ -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.annotatewith.deprecated; + +import java.lang.reflect.Method; +import org.mapstruct.ap.test.annotatewith.CustomMethodOnlyAnnotation; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.factory.Mappers; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author orange add + */ +public class DeprecatedTest { + + @ProcessorTest + @WithClasses( { DeprecatedMapperWithMethod.class, CustomMethodOnlyAnnotation.class} ) + public void deprecatedWithMethodCorrectCopy() throws NoSuchMethodException { + DeprecatedMapperWithMethod mapper = Mappers.getMapper( DeprecatedMapperWithMethod.class ); + Method method = mapper.getClass().getMethod( "map", DeprecatedMapperWithMethod.Source.class ); + Deprecated annotation = method.getAnnotation( Deprecated.class ); + assertThat( annotation ).isNotNull(); + } + + @ProcessorTest + @WithClasses(DeprecatedMapperWithClass.class) + public void deprecatedWithClassCorrectCopy() { + DeprecatedMapperWithClass mapper = Mappers.getMapper( DeprecatedMapperWithClass.class ); + Deprecated annotation = mapper.getClass().getAnnotation( Deprecated.class ); + assertThat( annotation ).isNotNull(); + } + + @ProcessorTest + @WithClasses(RepeatDeprecatedMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.WARNING, + type = RepeatDeprecatedMapper.class, + message = "Annotation \"Deprecated\" is already present with the " + + "same elements configuration." + ) + } + ) + public void deprecatedWithRepeat() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/RepeatDeprecatedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/RepeatDeprecatedMapper.java new file mode 100644 index 0000000000..dd085f101c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/RepeatDeprecatedMapper.java @@ -0,0 +1,15 @@ +/* + * 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.annotatewith.deprecated; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +@Mapper +@Deprecated +@AnnotateWith(Deprecated.class) +public class RepeatDeprecatedMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedMapperWithClass.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedMapperWithClass.java new file mode 100644 index 0000000000..2e0507f5ab --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedMapperWithClass.java @@ -0,0 +1,13 @@ +/* + * 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.annotatewith.deprecated.jdk11; + +import org.mapstruct.Mapper; + +@Mapper +@Deprecated(since = "11") +public class DeprecatedMapperWithClass { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedMapperWithMethod.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedMapperWithMethod.java new file mode 100644 index 0000000000..bf898e2043 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedMapperWithMethod.java @@ -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.annotatewith.deprecated.jdk11; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.annotatewith.CustomMethodOnlyAnnotation; + +@Mapper +public interface DeprecatedMapperWithMethod { + + @AnnotateWith(CustomMethodOnlyAnnotation.class) + @Deprecated(since = "18", forRemoval = false) + Target map(Source source); + + class Source { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class Target { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedTest.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedTest.java new file mode 100644 index 0000000000..4e10b6ded6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/DeprecatedTest.java @@ -0,0 +1,63 @@ +/* + * 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.annotatewith.deprecated.jdk11; + +import java.lang.reflect.Method; +import org.mapstruct.ap.test.annotatewith.CustomMethodOnlyAnnotation; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.factory.Mappers; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author orange add + */ +public class DeprecatedTest { + @ProcessorTest + @WithClasses({ DeprecatedMapperWithMethod.class, CustomMethodOnlyAnnotation.class}) + public void deprecatedWithMethodCorrectCopyForJdk11() throws NoSuchMethodException { + DeprecatedMapperWithMethod mapper = Mappers.getMapper( DeprecatedMapperWithMethod.class ); + Method method = mapper.getClass().getMethod( "map", DeprecatedMapperWithMethod.Source.class ); + Deprecated annotation = method.getAnnotation( Deprecated.class ); + assertThat( annotation ).isNotNull(); + assertThat( annotation.since() ).isEqualTo( "18" ); + assertThat( annotation.forRemoval() ).isEqualTo( false ); + } + + @ProcessorTest + @WithClasses(DeprecatedMapperWithClass.class) + public void deprecatedWithClassCorrectCopyForJdk11() { + DeprecatedMapperWithClass mapper = Mappers.getMapper( DeprecatedMapperWithClass.class ); + Deprecated annotation = mapper.getClass().getAnnotation( Deprecated.class ); + assertThat( annotation ).isNotNull(); + assertThat( annotation.since() ).isEqualTo( "11" ); + } + + @ProcessorTest + @WithClasses( { RepeatDeprecatedMapperWithParams.class}) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.WARNING, + type = RepeatDeprecatedMapperWithParams.class, + message = "Annotation \"Deprecated\" is already present with the " + + "same elements configuration." + ) + } + ) + public void bothExistPriorityAnnotateWithForJdk11() { + RepeatDeprecatedMapperWithParams mapper = Mappers.getMapper( RepeatDeprecatedMapperWithParams.class ); + Deprecated deprecated = mapper.getClass().getAnnotation( Deprecated.class ); + assertThat( deprecated ).isNotNull(); + assertThat( deprecated.since() ).isEqualTo( "1.5" ); + assertThat( deprecated.forRemoval() ).isEqualTo( false ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/RepeatDeprecatedMapperWithParams.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/RepeatDeprecatedMapperWithParams.java new file mode 100644 index 0000000000..971791bfd2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/deprecated/jdk11/RepeatDeprecatedMapperWithParams.java @@ -0,0 +1,21 @@ +/* + * 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.annotatewith.deprecated.jdk11; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; + +@Mapper +@Deprecated( since = "1.8" ) +@AnnotateWith( + value = Deprecated.class, + elements = { + @AnnotateWith.Element( name = "forRemoval", booleans = false), + @AnnotateWith.Element( name = "since", strings = "1.5") + } +) +public class RepeatDeprecatedMapperWithParams { +} From 608d476ed26ce4ac3096e2d9a2ee2a8226e42a00 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Thu, 29 Sep 2022 21:35:51 +0200 Subject: [PATCH 313/597] #3018: Use MappingControl with SubclassMapping --- .../ap/internal/model/BeanMappingMethod.java | 2 +- .../test/subclassmapping/DeepCloneMapper.java | 23 ++++++++++++++ .../DeepCloneMethodMapper.java | 26 ++++++++++++++++ .../subclassmapping/SubclassMappingTest.java | 30 +++++++++++++++++++ .../test/subclassmapping/mappables/Car.java | 1 + 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMethodMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 4f2dc4e5f1..24e160b9de 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -405,7 +405,7 @@ private SubclassMapping createSubclassMapping(SubclassMappingOptions subclassMap Collections.emptyList(), subclassMappingOptions.getTarget(), ctx.getTypeUtils() ).withSourceRHS( rightHandSide ), - null, + subclassMappingOptions.getMappingControl( ctx.getElementUtils() ), null, false ); Assignment assignment = ctx diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMapper.java new file mode 100644 index 0000000000..24d0244729 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMapper.java @@ -0,0 +1,23 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.control.DeepClone; +import org.mapstruct.factory.Mappers; + +@Mapper(mappingControl = DeepClone.class) +public interface DeepCloneMapper { + DeepCloneMapper INSTANCE = Mappers.getMapper( DeepCloneMapper.class ); + + @SubclassMapping( source = Car.class, target = Car.class ) + @SubclassMapping( source = Bike.class, target = Bike.class ) + Vehicle map(Vehicle vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMethodMapper.java new file mode 100644 index 0000000000..3f276b24cd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMethodMapper.java @@ -0,0 +1,26 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.NullValueMappingStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.control.DeepClone; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface DeepCloneMethodMapper { + DeepCloneMethodMapper INSTANCE = Mappers.getMapper( DeepCloneMethodMapper.class ); + + @SubclassMapping( source = Car.class, target = Car.class ) + @SubclassMapping( source = Bike.class, target = Bike.class ) + @BeanMapping( mappingControl = DeepClone.class, nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL ) + Vehicle map(Vehicle vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java index 7df555ecc2..6f9b6e160c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java @@ -52,6 +52,36 @@ void mappingIsDoneUsingSubclassMapping() { .containsExactly( CarDto.class, BikeDto.class ); } + @ProcessorTest + @WithClasses( DeepCloneMapper.class ) + void deepCloneMappingClonesObjects() { + Car car = new Car(); + car.setManual( true ); + car.setName( "namedCar" ); + car.setVehicleManufacturingCompany( "veMac" ); + + Vehicle result = DeepCloneMapper.INSTANCE.map( car ); + + assertThat( result ).isInstanceOf( Car.class ); + assertThat( result ).isNotSameAs( car ); + assertThat( result ).usingRecursiveComparison().isEqualTo( car ); + } + + @ProcessorTest + @WithClasses( DeepCloneMethodMapper.class ) + void deepCloneMappingOnMethodClonesObjects() { + Car car = new Car(); + car.setManual( true ); + car.setName( "namedCar" ); + car.setVehicleManufacturingCompany( "veMac" ); + + Vehicle result = DeepCloneMethodMapper.INSTANCE.map( car ); + + assertThat( result ).isInstanceOf( Car.class ); + assertThat( result ).isNotSameAs( car ); + assertThat( result ).usingRecursiveComparison().isEqualTo( car ); + } + @ProcessorTest @WithClasses( SimpleSubclassMapper.class ) void inverseMappingIsDoneUsingSubclassMapping() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Car.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Car.java index 71b2446d34..6f33a6cb61 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Car.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/mappables/Car.java @@ -15,4 +15,5 @@ public boolean isManual() { public void setManual(boolean manual) { this.manual = manual; } + } From af1eab0ece084aa04798e2d86fc20bd65bfa784e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 29 Sep 2022 22:10:27 +0200 Subject: [PATCH 314/597] #2743 BeanMappingOptions should not be inherited for forged methods --- .../model/source/BeanMappingOptions.java | 7 +- .../model/source/MappingMethodOptions.java | 2 +- .../ap/test/bugs/_2743/Issue2743Mapper.java | 75 +++++++++++++++++++ .../ap/test/bugs/_2743/Issue2743Test.java | 24 ++++++ 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2743/Issue2743Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2743/Issue2743Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index 60aac32e54..b73b4084fd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -54,13 +54,16 @@ public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) return options; } + public static BeanMappingOptions empty(DelegatingOptions delegatingOptions) { + return new BeanMappingOptions( null, Collections.emptyList(), null, delegatingOptions ); + } + public static BeanMappingOptions getInstanceOn(BeanMappingGem beanMapping, MapperOptions mapperOptions, ExecutableElement method, FormattingMessager messager, TypeUtils typeUtils, TypeFactory typeFactory ) { if ( beanMapping == null || !isConsistent( beanMapping, method, messager ) ) { - BeanMappingOptions options = new BeanMappingOptions( null, Collections.emptyList(), null, mapperOptions ); - return options; + return empty( mapperOptions ); } Objects.requireNonNull( method ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 4a140c4fb9..a1a64872a9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -365,7 +365,7 @@ public static MappingMethodOptions getForgedMethodInheritedOptions(MappingMethod options.mappings, options.iterableMapping, options.mapMapping, - options.beanMapping, + BeanMappingOptions.empty( options.beanMapping.next() ), options.enumMappingOptions, options.valueMappings, Collections.emptySet(), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2743/Issue2743Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2743/Issue2743Mapper.java new file mode 100644 index 0000000000..db3c0e6d49 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2743/Issue2743Mapper.java @@ -0,0 +1,75 @@ +/* + * 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._2743; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface Issue2743Mapper { + + @BeanMapping(ignoreUnmappedSourceProperties = { "number" }) + Target map(Source source); + + class Source { + + private final int number = 10; + private final NestedSource nested; + + public Source(NestedSource nested) { + this.nested = nested; + } + + public int getNumber() { + return number; + } + + public NestedSource getNested() { + return nested; + } + } + + class NestedSource { + private final String value; + + public NestedSource(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Target { + + private final NestedTarget nested; + + public Target(NestedTarget nested) { + this.nested = nested; + } + + public NestedTarget getNested() { + return nested; + } + } + + class NestedTarget { + private final String value; + + public NestedTarget(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2743/Issue2743Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2743/Issue2743Test.java new file mode 100644 index 0000000000..5c2bb61279 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2743/Issue2743Test.java @@ -0,0 +1,24 @@ +/* + * 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._2743; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Filip Hrisafov + */ +@IssueKey("2743") +@WithClasses({ + Issue2743Mapper.class +}) +class Issue2743Test { + + @ProcessorTest + void shouldCompile() { + } +} From 90a487ac0641c39ebec6680567cc97eaede49365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Campanero=20Ortiz?= Date: Sat, 1 Oct 2022 13:47:49 +0200 Subject: [PATCH 315/597] #1427 Add support for custom name in Spring stereotype annotations --- .../processor/SpringComponentProcessor.java | 67 +++++++++++++- .../spring/annotateWith/CustomStereotype.java | 21 +++++ ...ustomerSpringComponentQualifiedMapper.java | 20 ++++ ...stomerSpringControllerQualifiedMapper.java | 19 ++++ ...SpringCustomStereotypeQualifiedMapper.java | 21 +++++ ...stomerSpringRepositoryQualifiedMapper.java | 19 ++++ .../CustomerSpringServiceQualifiedMapper.java | 19 ++++ .../SpringAnnotateWithMapperTest.java | 91 +++++++++++++++++++ 8 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomStereotype.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringComponentQualifiedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringControllerQualifiedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringCustomStereotypeQualifiedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringRepositoryQualifiedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringServiceQualifiedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/SpringAnnotateWithMapperTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java index 4efc537c3d..84a672bcc9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java @@ -8,7 +8,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; @@ -16,6 +18,13 @@ import org.mapstruct.ap.internal.model.annotation.AnnotationElement; import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +import static javax.lang.model.element.ElementKind.PACKAGE; + /** * A {@link ModelElementProcessor} which converts the given {@link Mapper} * object into a Spring bean in case Spring is configured as the @@ -25,6 +34,7 @@ * @author Andreas Gudian */ public class SpringComponentProcessor extends AnnotationBasedComponentModelProcessor { + @Override protected String getComponentModelIdentifier() { return MappingConstantsGem.ComponentModelGem.SPRING; @@ -33,7 +43,9 @@ protected String getComponentModelIdentifier() { @Override protected List getTypeAnnotations(Mapper mapper) { List typeAnnotations = new ArrayList<>(); - typeAnnotations.add( component() ); + if ( !isAlreadyAnnotatedAsSpringStereotype( mapper ) ) { + typeAnnotations.add( component() ); + } if ( mapper.getDecorator() != null ) { typeAnnotations.add( qualifierDelegate() ); @@ -91,4 +103,57 @@ private Annotation primary() { private Annotation component() { return new Annotation( getTypeFactory().getType( "org.springframework.stereotype.Component" ) ); } + + private boolean isAlreadyAnnotatedAsSpringStereotype(Mapper mapper) { + Set handledElements = new HashSet<>(); + return mapper.getAnnotations() + .stream() + .anyMatch( + annotation -> isOrIncludesComponentAnnotation( annotation, handledElements ) + ); + } + + private boolean isOrIncludesComponentAnnotation(Annotation annotation, Set handledElements) { + return isOrIncludesComponentAnnotation( + annotation.getType().getTypeElement(), handledElements + ); + } + + private boolean isOrIncludesComponentAnnotation(Element element, Set handledElements) { + if ( "org.springframework.stereotype.Component".equals( + ( (TypeElement) element ).getQualifiedName().toString() + )) { + return true; + } + + for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { + Element annotationMirrorElement = annotationMirror.getAnnotationType().asElement(); + // Bypass java lang annotations to improve performance avoiding unnecessary checks + if ( !isAnnotationInPackage( annotationMirrorElement, "java.lang.annotation" ) && + !handledElements.contains( annotationMirrorElement ) ) { + handledElements.add( annotationMirrorElement ); + boolean isOrIncludesComponentAnnotation = isOrIncludesComponentAnnotation( + annotationMirrorElement, handledElements + ); + + if ( isOrIncludesComponentAnnotation ) { + return true; + } + } + } + + return false; + } + + private PackageElement getPackageOf( Element element ) { + while ( element.getKind() != PACKAGE ) { + element = element.getEnclosingElement(); + } + + return (PackageElement) element; + } + + private boolean isAnnotationInPackage(Element element, String packageFQN) { + return packageFQN.equals( getPackageOf( element ).getQualifiedName().toString() ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomStereotype.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomStereotype.java new file mode 100644 index 0000000000..ccc196c5d3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomStereotype.java @@ -0,0 +1,21 @@ +/* + * 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.injectionstrategy.spring.annotateWith; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.springframework.stereotype.Component; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface CustomStereotype { + String value() default ""; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringComponentQualifiedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringComponentQualifiedMapper.java new file mode 100644 index 0000000000..be08ff0b1c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringComponentQualifiedMapper.java @@ -0,0 +1,20 @@ +/* + * 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.injectionstrategy.spring.annotateWith; + +import org.springframework.stereotype.Component; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + +/** + * @author Ben Zegveld + */ +@AnnotateWith( value = Component.class, elements = @AnnotateWith.Element( strings = "AnnotateWithComponent" ) ) +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING ) +public interface CustomerSpringComponentQualifiedMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringControllerQualifiedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringControllerQualifiedMapper.java new file mode 100644 index 0000000000..7203fad7a9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringControllerQualifiedMapper.java @@ -0,0 +1,19 @@ +/* + * 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.injectionstrategy.spring.annotateWith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.springframework.stereotype.Controller; + +/** + * @author Jose Carlos Campanero Ortiz + */ +@AnnotateWith( value = Controller.class, elements = @AnnotateWith.Element( strings = "AnnotateWithController" ) ) +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING ) +public interface CustomerSpringControllerQualifiedMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringCustomStereotypeQualifiedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringCustomStereotypeQualifiedMapper.java new file mode 100644 index 0000000000..18a062497d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringCustomStereotypeQualifiedMapper.java @@ -0,0 +1,21 @@ +/* + * 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.injectionstrategy.spring.annotateWith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + +/** + * @author Jose Carlos Campanero Ortiz + */ +@AnnotateWith( + value = CustomStereotype.class, + elements = @AnnotateWith.Element( strings = "AnnotateWithCustomStereotype" ) +) +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING ) +public interface CustomerSpringCustomStereotypeQualifiedMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringRepositoryQualifiedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringRepositoryQualifiedMapper.java new file mode 100644 index 0000000000..7bbefbee2b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringRepositoryQualifiedMapper.java @@ -0,0 +1,19 @@ +/* + * 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.injectionstrategy.spring.annotateWith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.springframework.stereotype.Repository; + +/** + * @author Jose Carlos Campanero Ortiz + */ +@AnnotateWith( value = Repository.class, elements = @AnnotateWith.Element( strings = "AnnotateWithRepository" ) ) +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING ) +public interface CustomerSpringRepositoryQualifiedMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringServiceQualifiedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringServiceQualifiedMapper.java new file mode 100644 index 0000000000..52dff8ef8f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/CustomerSpringServiceQualifiedMapper.java @@ -0,0 +1,19 @@ +/* + * 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.injectionstrategy.spring.annotateWith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.springframework.stereotype.Service; + +/** + * @author Jose Carlos Campanero Ortiz + */ +@AnnotateWith( value = Service.class, elements = @AnnotateWith.Element( strings = "AnnotateWithService" ) ) +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING ) +public interface CustomerSpringServiceQualifiedMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/SpringAnnotateWithMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/SpringAnnotateWithMapperTest.java new file mode 100644 index 0000000000..cdc741814c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/annotateWith/SpringAnnotateWithMapperTest.java @@ -0,0 +1,91 @@ +/* + * 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.injectionstrategy.spring.annotateWith; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +/** + * Test field injection for component model spring. + * + * @author Filip Hrisafov + * @author Jose Carlos Campanero Ortiz + */ +@WithClasses({ + CustomerSpringComponentQualifiedMapper.class, + CustomerSpringControllerQualifiedMapper.class, + CustomerSpringServiceQualifiedMapper.class, + CustomerSpringRepositoryQualifiedMapper.class, + CustomStereotype.class, + CustomerSpringCustomStereotypeQualifiedMapper.class +}) +@IssueKey( "1427" ) +@WithSpring +public class SpringAnnotateWithMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveComponentAnnotatedQualifiedMapper() { + + // then + generatedSource.forMapper( CustomerSpringComponentQualifiedMapper.class ) + .content() + .contains( "@Component(value = \"AnnotateWithComponent\")" ) + .doesNotContain( "@Component" + System.lineSeparator() ); + + } + + @ProcessorTest + public void shouldHaveControllerAnnotatedQualifiedMapper() { + + // then + generatedSource.forMapper( CustomerSpringControllerQualifiedMapper.class ) + .content() + .contains( "@Controller(value = \"AnnotateWithController\")" ) + .doesNotContain( "@Component" ); + + } + + @ProcessorTest + public void shouldHaveServiceAnnotatedQualifiedMapper() { + + // then + generatedSource.forMapper( CustomerSpringServiceQualifiedMapper.class ) + .content() + .contains( "@Service(value = \"AnnotateWithService\")" ) + .doesNotContain( "@Component" ); + + } + + @ProcessorTest + public void shouldHaveRepositoryAnnotatedQualifiedMapper() { + + // then + generatedSource.forMapper( CustomerSpringRepositoryQualifiedMapper.class ) + .content() + .contains( "@Repository(value = \"AnnotateWithRepository\")" ) + .doesNotContain( "@Component" ); + + } + + @ProcessorTest + public void shouldHaveCustomStereotypeAnnotatedQualifiedMapper() { + + // then + generatedSource.forMapper( CustomerSpringCustomStereotypeQualifiedMapper.class ) + .content() + .contains( "@CustomStereotype(value = \"AnnotateWithCustomStereotype\")" ) + .doesNotContain( "@Component" ); + + } + +} From 411cc24dac06892dc7e5582aff9ce409302916b8 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sun, 2 Oct 2022 09:34:03 +0200 Subject: [PATCH 316/597] #2955 Fix `@AfterMapping` with return type not called for update mappings --- .../model/LifecycleMethodResolver.java | 2 +- .../CallbacksWithReturnValuesTest.java | 46 +++++++++++++++---- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java index 87527e87f8..8b44dee254 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java @@ -141,7 +141,7 @@ private static List collectLifecycleCallbackMe callbackMethods, Collections.emptyList(), targetType, - method.getReturnType(), + method.getResultType(), SelectionCriteria.forLifecycleMethods( selectionParameters ) ); return toLifecycleCallbackMethodRefs( diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java index 8c391bbfeb..7d6eaacb79 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java @@ -8,6 +8,7 @@ import java.util.Arrays; import java.util.concurrent.atomic.AtomicReference; +import org.junit.jupiter.api.AfterEach; import org.mapstruct.ap.test.callbacks.returning.NodeMapperContext.ContextListener; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; @@ -25,23 +26,29 @@ @WithClasses( { Attribute.class, AttributeDto.class, Node.class, NodeDto.class, NodeMapperDefault.class, NodeMapperWithContext.class, NodeMapperContext.class, Number.class, NumberMapperDefault.class, NumberMapperContext.class, NumberMapperWithContext.class } ) -public class CallbacksWithReturnValuesTest { +class CallbacksWithReturnValuesTest { + @AfterEach + void cleanup() { + NumberMapperContext.clearCache(); + NumberMapperContext.clearVisited(); + } + @ProcessorTest - public void mappingWithDefaultHandlingRaisesStackOverflowError() { + void mappingWithDefaultHandlingRaisesStackOverflowError() { Node root = buildNodes(); assertThatThrownBy( () -> NodeMapperDefault.INSTANCE.nodeToNodeDto( root ) ) .isInstanceOf( StackOverflowError.class ); } @ProcessorTest - public void updatingWithDefaultHandlingRaisesStackOverflowError() { + void updatingWithDefaultHandlingRaisesStackOverflowError() { Node root = buildNodes(); assertThatThrownBy( () -> NodeMapperDefault.INSTANCE.nodeToNodeDto( root, new NodeDto() ) ) .isInstanceOf( StackOverflowError.class ); } @ProcessorTest - public void mappingWithContextCorrectlyResolvesCycles() { + void mappingWithContextCorrectlyResolvesCycles() { final AtomicReference contextLevel = new AtomicReference<>( null ); ContextListener contextListener = new ContextListener() { @Override @@ -75,28 +82,49 @@ private static Node buildNodes() { } @ProcessorTest - public void numberMappingWithoutContextDoesNotUseCache() { + void numberMappingWithoutContextDoesNotUseCache() { Number n1 = NumberMapperDefault.INSTANCE.integerToNumber( 2342 ); Number n2 = NumberMapperDefault.INSTANCE.integerToNumber( 2342 ); + assertThat( n1 ).isEqualTo( n2 ); assertThat( n1 ).isNotSameAs( n2 ); } @ProcessorTest - public void numberMappingWithContextUsesCache() { + void numberMappingWithContextUsesCache() { NumberMapperContext.putCache( new Number( 2342 ) ); Number n1 = NumberMapperWithContext.INSTANCE.integerToNumber( 2342 ); Number n2 = NumberMapperWithContext.INSTANCE.integerToNumber( 2342 ); + assertThat( n1 ).isEqualTo( n2 ); assertThat( n1 ).isSameAs( n2 ); - NumberMapperContext.clearCache(); } @ProcessorTest - public void numberMappingWithContextCallsVisitNumber() { + void numberMappingWithContextCallsVisitNumber() { Number n1 = NumberMapperWithContext.INSTANCE.integerToNumber( 1234 ); Number n2 = NumberMapperWithContext.INSTANCE.integerToNumber( 5678 ); + assertThat( NumberMapperContext.getVisited() ).isEqualTo( Arrays.asList( n1, n2 ) ); - NumberMapperContext.clearVisited(); + } + + @ProcessorTest + @IssueKey( "2955" ) + void numberUpdateMappingWithContextUsesCacheAndThereforeDoesNotVisitNumber() { + Number target = new Number(); + Number expectedReturn = new Number( 2342 ); + NumberMapperContext.putCache( expectedReturn ); + NumberMapperWithContext.INSTANCE.integerToNumber( 2342, target ); + + assertThat( NumberMapperContext.getVisited() ).isEmpty(); + } + + @ProcessorTest + @IssueKey( "2955" ) + void numberUpdateMappingWithContextCallsVisitNumber() { + Number target = new Number(); + NumberMapperWithContext.INSTANCE.integerToNumber( 2342, target ); + + assertThat( NumberMapperContext.getVisited() ).contains( target ); } } From 266c5fa41ce6a07c4fb11b745b461c4ca19a8301 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 2 Oct 2022 19:20:13 +0200 Subject: [PATCH 317/597] #1216 Pick candidate method with most specific return type When there are multiple candidate methods returning different types. We should be able to use the method with the most specific return type (if such a method exists) --- .../source/selector/MethodSelectors.java | 4 +- .../MostSpecificResultTypeSelector.java | 45 +++++++++++++ .../source/selector/SelectionCriteria.java | 8 +++ ...MostSpecificResultTypeSelectingMapper.java | 31 +++++++++ .../selection/resulttype/FruitFamily.java | 22 ++++++ .../resulttype/InheritanceSelectionTest.java | 67 +++++++++++++++++++ ...MostSpecificResultTypeSelectingMapper.java | 30 +++++++++ ...ecificResultTypeSelectingUpdateMapper.java | 65 ++++++++++++++++++ 8 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousAmbiguousMostSpecificResultTypeSelectingMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/FruitFamily.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/MostSpecificResultTypeSelectingMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/MostSpecificResultTypeSelectingUpdateMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java index 519e1c3d6d..74fa1a33a3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java @@ -37,7 +37,9 @@ public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils, TypeFacto new InheritanceSelector(), new CreateOrUpdateSelector(), new SourceRhsSelector(), - new FactoryParameterSelector() ); + new FactoryParameterSelector(), + new MostSpecificResultTypeSelector() + ); } /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java new file mode 100644 index 0000000000..6e86a30bc7 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java @@ -0,0 +1,45 @@ +/* + * 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.internal.model.source.selector; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.source.Method; + +/** + * @author Filip Hrisafov + */ +public class MostSpecificResultTypeSelector implements MethodSelector { + + @Override + public List> getMatchingMethods(Method mappingMethod, + List> candidates, + List sourceTypes, Type mappingTargetType, + Type returnType, SelectionCriteria criteria) { + if ( candidates.size() < 2 || !criteria.isForMapping() || criteria.getQualifyingResultType() != null) { + return candidates; + } + + List> result = new ArrayList<>(); + + for ( SelectedMethod candidate : candidates ) { + if ( candidate.getMethod() + .getResultType() + .getBoxedEquivalent() + .equals( mappingTargetType.getBoxedEquivalent() ) ) { + // If the result type is the same as the target type + // then this candidate has the most specific match and should be used + result.add( candidate ); + } + } + + + // If not most specific types were found then return the current candidates + return result.isEmpty() ? candidates : result; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java index da8bae365a..7e12bbd188 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java @@ -68,6 +68,14 @@ public SelectionCriteria(SelectionParameters selectionParameters, MappingControl this.type = type; } + /** + * + * @return {@code true} if only mapping methods should be selected + */ + public boolean isForMapping() { + return type == null || type == Type.PREFER_UPDATE_MAPPING; + } + /** * @return true if factory methods should be selected, false otherwise. */ diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousAmbiguousMostSpecificResultTypeSelectingMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousAmbiguousMostSpecificResultTypeSelectingMapper.java new file mode 100644 index 0000000000..28a925c32b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/ErroneousAmbiguousMostSpecificResultTypeSelectingMapper.java @@ -0,0 +1,31 @@ +/* + * 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.selection.resulttype; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousAmbiguousMostSpecificResultTypeSelectingMapper { + + @Mapping( target = "apple", source = "fruit") + AppleFamily map(FruitFamily fruitFamily); + + default GoldenDelicious toGolden(IsFruit fruit) { + return fruit != null ? new GoldenDelicious( fruit.getType() ) : null; + } + + default Apple toApple1(IsFruit fruit) { + return fruit != null ? new Apple( fruit.getType() ) : null; + } + + default Apple toApple2(IsFruit fruit) { + return fruit != null ? new Apple( fruit.getType() ) : null; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/FruitFamily.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/FruitFamily.java new file mode 100644 index 0000000000..347e5a0be0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/FruitFamily.java @@ -0,0 +1,22 @@ +/* + * 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.selection.resulttype; + +/** + * @author Filip Hrisafov + */ +public class FruitFamily { + + private IsFruit fruit; + + public IsFruit getFruit() { + return fruit; + } + + public void setFruit(IsFruit fruit) { + this.fruit = fruit; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java index a6a4f1ad05..05245bafe0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java @@ -240,4 +240,71 @@ public void testShouldUseConstructorFromResultType() { .hasMessage( "Not allowed to change citrus type" ); assertThat( citrus.getType() ).isEqualTo( "lemon" ); } + + @ProcessorTest + @IssueKey("1216") + @WithClasses({ + Citrus.class, + GoldenDelicious.class, + FruitFamily.class, + AppleFamily.class, + MostSpecificResultTypeSelectingMapper.class, + Citrus.class + }) + public void testShouldUseMethodWithMostSpecificReturnType() { + FruitFamily fruitFamily = new FruitFamily(); + fruitFamily.setFruit( new Citrus( "citrus" ) ); + AppleFamily appleFamily = MostSpecificResultTypeSelectingMapper.INSTANCE.map( fruitFamily ); + + assertThat( appleFamily.getApple() ).isExactlyInstanceOf( Apple.class ); + assertThat( appleFamily.getApple().getType() ).isEqualTo( "citrus" ); + } + + @ProcessorTest + @IssueKey("1216") + @WithClasses({ + Citrus.class, + FruitFamily.class, + GoldenDelicious.class, + MostSpecificResultTypeSelectingUpdateMapper.class, + Citrus.class + }) + public void testShouldUseMethodWithMostSpecificReturnTypeForUpdateMappings() { + FruitFamily fruitFamily = new FruitFamily(); + fruitFamily.setFruit( new Citrus( "citrus" ) ); + MostSpecificResultTypeSelectingUpdateMapper.Target target = + new MostSpecificResultTypeSelectingUpdateMapper.Target( + new Apple( "from_test" ), + new GoldenDelicious( "from_test" ) + ); + MostSpecificResultTypeSelectingUpdateMapper.INSTANCE.update( target, fruitFamily ); + + assertThat( target.getApple() ).isExactlyInstanceOf( Apple.class ); + assertThat( target.getApple().getType() ).isEqualTo( "apple updated citrus" ); + assertThat( target.getGoldenApple() ).isExactlyInstanceOf( GoldenDelicious.class ); + assertThat( target.getGoldenApple().getType() ).isEqualTo( "golden updated citrus" ); + } + + @ProcessorTest + @IssueKey("1216") + @WithClasses({ + GoldenDelicious.class, + FruitFamily.class, + AppleFamily.class, + ErroneousAmbiguousMostSpecificResultTypeSelectingMapper.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousAmbiguousMostSpecificResultTypeSelectingMapper.class, + kind = Kind.ERROR, + line = 17, + message = "Ambiguous mapping methods found for mapping property \"IsFruit fruit\" to Apple: " + + "Apple toApple1(IsFruit fruit), Apple toApple2(IsFruit fruit). " + + "See https://mapstruct.org/faq/#ambiguous for more info." + ) + } + ) + public void testAmbiguousMostSpecificResultTypeErroneous() { + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/MostSpecificResultTypeSelectingMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/MostSpecificResultTypeSelectingMapper.java new file mode 100644 index 0000000000..16d9750ec2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/MostSpecificResultTypeSelectingMapper.java @@ -0,0 +1,30 @@ +/* + * 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.selection.resulttype; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface MostSpecificResultTypeSelectingMapper { + + MostSpecificResultTypeSelectingMapper INSTANCE = Mappers.getMapper( MostSpecificResultTypeSelectingMapper.class ); + + @Mapping( target = "apple", source = "fruit") + AppleFamily map(FruitFamily fruitFamily); + + default GoldenDelicious toGolden(IsFruit fruit) { + return fruit != null ? new GoldenDelicious( fruit.getType() ) : null; + } + + default Apple toApple(IsFruit fruit) { + return fruit != null ? new Apple( fruit.getType() ) : null; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/MostSpecificResultTypeSelectingUpdateMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/MostSpecificResultTypeSelectingUpdateMapper.java new file mode 100644 index 0000000000..5f270116d4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/MostSpecificResultTypeSelectingUpdateMapper.java @@ -0,0 +1,65 @@ +/* + * 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.selection.resulttype; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface MostSpecificResultTypeSelectingUpdateMapper { + + MostSpecificResultTypeSelectingUpdateMapper INSTANCE = Mappers.getMapper( + MostSpecificResultTypeSelectingUpdateMapper.class ); + + @Mapping(target = "apple", source = "fruit") + @Mapping(target = "goldenApple", source = "fruit") + void update(@MappingTarget Target target, FruitFamily fruitFamily); + + default void updateGolden(@MappingTarget GoldenDelicious target, IsFruit fruit) { + target.setType( "golden updated " + fruit.getType() ); + } + + default void updateApple(@MappingTarget Apple target, IsFruit fruit) { + target.setType( "apple updated " + fruit.getType() ); + } + + @ObjectFactory + default GoldenDelicious createGolden() { + return new GoldenDelicious( "from_object_factory" ); + } + + class Target { + protected Apple apple; + protected GoldenDelicious goldenApple; + + public Target(Apple apple, GoldenDelicious goldenApple) { + this.apple = apple; + this.goldenApple = goldenApple; + } + + public Apple getApple() { + return apple; + } + + public void setApple(Apple apple) { + this.apple = apple; + } + + public GoldenDelicious getGoldenApple() { + return goldenApple; + } + + public void setGoldenApple(GoldenDelicious goldenApple) { + this.goldenApple = goldenApple; + } + } +} From 3a325ea66bdd07741d1bda43d3c77c2709464ea7 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 3 Oct 2022 21:12:19 +0200 Subject: [PATCH 318/597] #3036 Fix compile errors when intersection types are used in lifecycle methods --- ...eatureCompilationExclusionCliEnhancer.java | 4 + .../test/resources/fullFeatureTest/pom.xml | 1 + .../ap/internal/model/common/Type.java | 29 ++++++ .../internal/model/source/MethodMatcher.java | 3 +- .../LifecycleIntersectionMapper.java | 96 +++++++++++++++++++ .../wildcards/WildCardTest.java | 20 ++++ 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java index e017d1b003..e64b207990 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -32,6 +32,10 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/annotatewith/deprecated/jdk11/*.java" ); + if ( processorType == ProcessorTest.ProcessorType.ECLIPSE_JDT ) { + additionalExcludes.add( + "org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java" ); + } break; case JAVA_9: // TODO find out why this fails: diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index 8a62f48588..9c17f6c7d5 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -26,6 +26,7 @@ x x x + x diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index fd7f3d6904..6d86aad452 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -28,6 +28,7 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.IntersectionType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -114,6 +115,7 @@ public class Type extends ModelElement implements Comparable { private List alternativeTargetAccessors = null; private Type boundingBase = null; + private List boundTypes = null; private Type boxedEquivalent = null; @@ -354,6 +356,10 @@ public boolean isTypeVar() { return (typeMirror.getKind() == TypeKind.TYPEVAR); } + public boolean isIntersection() { + return typeMirror.getKind() == TypeKind.INTERSECTION; + } + public boolean isJavaLangType() { return packageName != null && packageName.startsWith( "java." ); } @@ -1264,6 +1270,29 @@ public Type getTypeBound() { return boundingBase; } + public List getTypeBounds() { + if ( this.boundTypes != null ) { + return boundTypes; + } + Type bound = getTypeBound(); + if ( bound == null ) { + this.boundTypes = Collections.emptyList(); + } + else if ( !bound.isIntersection() ) { + this.boundTypes = Collections.singletonList( bound ); + } + else { + List bounds = ( (IntersectionType) bound.typeMirror ).getBounds(); + this.boundTypes = new ArrayList<>( bounds.size() ); + for ( TypeMirror mirror : bounds ) { + boundTypes.add( typeFactory.getType( mirror ) ); + } + } + + return this.boundTypes; + + } + public boolean hasAccessibleConstructor() { if ( hasAccessibleConstructor == null ) { hasAccessibleConstructor = false; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index 42c26f09a0..3d8bfea135 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -325,8 +325,7 @@ else if ( resolved.getParameter().isWildCardBoundByTypeVar() */ private boolean candidatesWithinBounds(Map methodParCandidates ) { for ( Map.Entry entry : methodParCandidates.entrySet() ) { - Type bound = entry.getKey().getTypeBound(); - if ( bound != null ) { + for ( Type bound : entry.getKey().getTypeBounds() ) { for ( Type.ResolvedPair pair : entry.getValue().pairs ) { if ( entry.getKey().hasUpperBound() ) { if ( !pair.getMatch().asRawType().isAssignableTo( bound.asRawType() ) ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java new file mode 100644 index 0000000000..c466ee0d91 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java @@ -0,0 +1,96 @@ +/* + * 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.selection.methodgenerics.wildcards; + +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface LifecycleIntersectionMapper { + + LifecycleIntersectionMapper INSTANCE = Mappers.getMapper( LifecycleIntersectionMapper.class ); + + @Mapping(target = "realm", ignore = true) + RealmTarget mapRealm(String source); + + @Mapping(target = "uniqueRealm", ignore = true) + UniqueRealmTarget mapUniqueRealm(String source); + + @Mapping(target = "realm", ignore = true) + @Mapping(target = "uniqueRealm", ignore = true) + BothRealmsTarget mapBothRealms(String source); + + @AfterMapping + default void afterMapping(String source, @MappingTarget T target) { + target.setRealm( "realm_" + source ); + target.setUniqueRealm( "uniqueRealm_" + source ); + } + + interface RealmObject { + void setRealm(String realm); + } + + interface UniqueRealmObject { + void setUniqueRealm(String realm); + } + + class RealmTarget implements RealmObject { + + protected String realm; + + public String getRealm() { + return realm; + } + + @Override + public void setRealm(String realm) { + this.realm = realm; + } + } + + class UniqueRealmTarget implements UniqueRealmObject { + protected String uniqueRealm; + + @Override + public void setUniqueRealm(String uniqueRealm) { + this.uniqueRealm = uniqueRealm; + } + + public String getUniqueRealm() { + return uniqueRealm; + } + } + + class BothRealmsTarget implements RealmObject, UniqueRealmObject { + + protected String realm; + protected String uniqueRealm; + + public String getRealm() { + return realm; + } + + @Override + public void setRealm(String realm) { + this.realm = realm; + } + + public String getUniqueRealm() { + return uniqueRealm; + } + + @Override + public void setUniqueRealm(String uniqueRealm) { + this.uniqueRealm = uniqueRealm; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java index a228304c72..9e27129635 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.selection.methodgenerics.wildcards; +import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.runner.Compiler; @@ -54,4 +55,23 @@ public void testIntersectionRelation() { assertThat( target ).isNotNull(); assertThat( target.getProp() ).isEqualTo( typeC ); } + + // Eclipse does not handle intersection types correctly (TODO: worthwhile to investigate?) + @ProcessorTest(Compiler.JDK) + @WithClasses(LifecycleIntersectionMapper.class) + @IssueKey("3036") + public void testLifecycleIntersection() { + + LifecycleIntersectionMapper.RealmTarget realmTarget = LifecycleIntersectionMapper.INSTANCE.mapRealm( "test" ); + assertThat( realmTarget.getRealm() ).isNull(); + + LifecycleIntersectionMapper.UniqueRealmTarget uniqueRealmTarget = + LifecycleIntersectionMapper.INSTANCE.mapUniqueRealm( "test" ); + assertThat( uniqueRealmTarget.getUniqueRealm() ).isNull(); + + LifecycleIntersectionMapper.BothRealmsTarget bothRealmsTarget = + LifecycleIntersectionMapper.INSTANCE.mapBothRealms( "test" ); + assertThat( bothRealmsTarget.getRealm() ).isEqualTo( "realm_test" ); + assertThat( bothRealmsTarget.getUniqueRealm() ).isEqualTo( "uniqueRealm_test" ); + } } From 481ab36ca35f53a6160bac179c495d6a1774388e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 3 Oct 2022 21:16:25 +0200 Subject: [PATCH 319/597] #3036 Add missing exclude to full feature test --- integrationtest/src/test/resources/fullFeatureTest/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index 9c17f6c7d5..1a31b28221 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -48,6 +48,7 @@ ${additionalExclude3} ${additionalExclude4} ${additionalExclude5} + ${additionalExclude6} From a5d3542c24419ff2a66132f3ce2bae12182e3ac0 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 7 Oct 2022 20:43:15 +0200 Subject: [PATCH 320/597] Update latest release version to 1.5.3.Final --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index e1ee8f9deb..58d1c515bb 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.2.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.3.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.5.2.Final + 1.5.3.Final ... @@ -114,10 +114,10 @@ plugins { dependencies { ... - implementation 'org.mapstruct:mapstruct:1.5.2.Final' + implementation 'org.mapstruct:mapstruct:1.5.3.Final' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.2.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.2.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final' // if you are using mapstruct in test code } ... ``` From 81b2f70dac328d96d788d7746a57fbeb3978e543 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 2 Oct 2022 19:36:20 +0200 Subject: [PATCH 321/597] #3039 Upgrade FreeMarker to 2.3.31 --- distribution/pom.xml | 2 +- distribution/src/main/assembly/dist.xml | 6 +----- parent/pom.xml | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/distribution/pom.xml b/distribution/pom.xml index 11fb7c9f37..be82f52b3c 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -66,7 +66,7 @@ org.freemarker freemarker ${project.build.directory}/freemarker-unpacked - META-INF/LICENSE.txt,META-INF/NOTICE.txt + META-INF/LICENSE diff --git a/distribution/src/main/assembly/dist.xml b/distribution/src/main/assembly/dist.xml index f519e5fc9c..f0c727f8da 100644 --- a/distribution/src/main/assembly/dist.xml +++ b/distribution/src/main/assembly/dist.xml @@ -42,11 +42,7 @@ / - target/freemarker-unpacked/META-INF/NOTICE.txt - / - - - target/freemarker-unpacked/META-INF/LICENSE.txt + target/freemarker-unpacked/META-INF/LICENSE etc/freemarker diff --git a/parent/pom.xml b/parent/pom.xml index 3f536034c7..e87db14551 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -109,7 +109,7 @@ org.freemarker freemarker - 2.3.21 + 2.3.31 org.assertj From bb099a55ee3c1b9ae7fc2fbad6c6f9c8fe3ae911 Mon Sep 17 00:00:00 2001 From: Orange Add <48479242+chenzijia12300@users.noreply.github.com> Date: Fri, 4 Nov 2022 05:02:58 +0800 Subject: [PATCH 322/597] #3040: Allow using only `BeanMapping#mappingControl` --- .../mapstruct/ap/internal/model/source/BeanMappingOptions.java | 1 + .../ap/test/subclassmapping/DeepCloneMethodMapper.java | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index b73b4084fd..90ad0aabb4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -92,6 +92,7 @@ public static BeanMappingOptions getInstanceOn(BeanMappingGem beanMapping, Mappe private static boolean isConsistent(BeanMappingGem gem, ExecutableElement method, FormattingMessager messager) { if ( !gem.resultType().hasValue() + && !gem.mappingControl().hasValue() && !gem.qualifiedBy().hasValue() && !gem.qualifiedByName().hasValue() && !gem.ignoreUnmappedSourceProperties().hasValue() diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMethodMapper.java index 3f276b24cd..036b98a318 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMethodMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/DeepCloneMethodMapper.java @@ -7,7 +7,6 @@ import org.mapstruct.BeanMapping; import org.mapstruct.Mapper; -import org.mapstruct.NullValueMappingStrategy; import org.mapstruct.SubclassMapping; import org.mapstruct.ap.test.subclassmapping.mappables.Bike; import org.mapstruct.ap.test.subclassmapping.mappables.Car; @@ -21,6 +20,6 @@ public interface DeepCloneMethodMapper { @SubclassMapping( source = Car.class, target = Car.class ) @SubclassMapping( source = Bike.class, target = Bike.class ) - @BeanMapping( mappingControl = DeepClone.class, nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL ) + @BeanMapping( mappingControl = DeepClone.class ) Vehicle map(Vehicle vehicle); } From 6a394ad466b5bda8b17e2406b56be34be3312e21 Mon Sep 17 00:00:00 2001 From: Orange Add <48479242+chenzijia12300@users.noreply.github.com> Date: Fri, 4 Nov 2022 06:21:43 +0800 Subject: [PATCH 323/597] #3037 Support `@ValueMapping` in meta annotations --- .../main/java/org/mapstruct/ValueMapping.java | 2 +- .../java/org/mapstruct/ValueMappings.java | 2 +- .../chapter-8-mapping-values.asciidoc | 39 +++++++++++++++ .../model/source/ValueMappingOptions.java | 4 +- .../processor/MethodRetrievalProcessor.java | 39 ++++++++++----- .../composition/CustomValueAnnotation.java | 21 ++++++++ .../composition/ValueCompositionTest.java | 48 +++++++++++++++++++ .../ValueMappingCompositionMapper.java | 25 ++++++++++ 8 files changed, 164 insertions(+), 16 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/composition/CustomValueAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/composition/ValueCompositionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/value/composition/ValueMappingCompositionMapper.java diff --git a/core/src/main/java/org/mapstruct/ValueMapping.java b/core/src/main/java/org/mapstruct/ValueMapping.java index 21ea5f4a83..7ad3726e48 100644 --- a/core/src/main/java/org/mapstruct/ValueMapping.java +++ b/core/src/main/java/org/mapstruct/ValueMapping.java @@ -84,7 +84,7 @@ */ @Repeatable(ValueMappings.class) @Retention(RetentionPolicy.CLASS) -@Target(ElementType.METHOD) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface ValueMapping { /** * The source value constant to use for this mapping. diff --git a/core/src/main/java/org/mapstruct/ValueMappings.java b/core/src/main/java/org/mapstruct/ValueMappings.java index faefbf7105..bb593f53d1 100644 --- a/core/src/main/java/org/mapstruct/ValueMappings.java +++ b/core/src/main/java/org/mapstruct/ValueMappings.java @@ -40,7 +40,7 @@ * * @author Sjaak Derksen */ -@Target(ElementType.METHOD) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.CLASS) public @interface ValueMappings { diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index 24c7324f1d..ce16b33ecd 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -266,3 +266,42 @@ MapStruct provides the following out of the box enum name transformation strateg It is also possible to register custom strategies. For more information on how to do that have a look at <> + +[[value-mapping-composition]] +=== ValueMapping Composition + +The `@ValueMapping` annotation supports now `@Target` with `ElementType#ANNOTATION_TYPE` in addition to `ElementType#METHOD`. +This allows `@ValueMapping` to be used on other (user defined) annotations for re-use purposes. +For example: + +.Custom value mapping annotations +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Retention( RetentionPolicy.CLASS ) +@ValueMapping(source = "EXTRA", target = "SPECIAL") +@ValueMapping(source = MappingConstants.ANY_REMAINING, target = "DEFAULT") +public @interface CustomValueAnnotation { +} +---- +==== +It can be used to describe some common value mapping relationships to avoid duplicate declarations, as in the following example: + +.Using custom combination annotations +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface ValueMappingCompositionMapper { + + @CustomValueAnnotation + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); + + @CustomValueAnnotation + @ValueMapping(source = "STANDARD", target = "SPECIAL") + ExternalOrderType duplicateAnnotation(OrderType orderType); +} +---- +==== diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java index 963683b5c5..9c412733c4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMappingOptions.java @@ -5,8 +5,8 @@ */ package org.mapstruct.ap.internal.model.source; -import java.util.List; import java.util.Objects; +import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; @@ -34,7 +34,7 @@ public class ValueMappingOptions { private final AnnotationValue targetAnnotationValue; public static void fromMappingsGem(ValueMappingsGem mappingsGem, ExecutableElement method, - FormattingMessager messager, List mappings) { + FormattingMessager messager, Set mappings) { boolean anyFound = false; for ( ValueMappingGem mappingGem : mappingsGem.value().get() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index bda33b6a92..4ad531d334 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -69,7 +69,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor getValueMappings(ExecutableElement method) { - List valueMappings = new ArrayList<>(); + Set processedAnnotations = new RepeatValueMappings().getProcessedAnnotations( method ); + return new ArrayList<>(processedAnnotations); + } - ValueMappingGem mappingAnnotation = ValueMappingGem.instanceOn( method ); - ValueMappingsGem mappingsAnnotation = ValueMappingsGem.instanceOn( method ); + private class RepeatValueMappings + extends RepeatableAnnotations { - if ( mappingAnnotation != null ) { - ValueMappingOptions valueMapping = ValueMappingOptions.fromMappingGem( mappingAnnotation ); - if ( valueMapping != null ) { - valueMappings.add( valueMapping ); - } + protected RepeatValueMappings() { + super( elementUtils, VALUE_MAPPING_FQN, VALUE_MAPPINGS_FQN ); + } + + @Override + protected ValueMappingGem singularInstanceOn(Element element) { + return ValueMappingGem.instanceOn( element ); } - if ( mappingsAnnotation != null ) { - ValueMappingOptions.fromMappingsGem( mappingsAnnotation, method, messager, valueMappings ); + @Override + protected ValueMappingsGem multipleInstanceOn(Element element) { + return ValueMappingsGem.instanceOn( element ); + } + + @Override + protected void addInstance(ValueMappingGem gem, Element source, Set mappings) { + ValueMappingOptions valueMappingOptions = ValueMappingOptions.fromMappingGem( gem ); + mappings.add( valueMappingOptions ); } - return valueMappings; + @Override + protected void addInstances(ValueMappingsGem gems, Element source, Set mappings) { + ValueMappingOptions.fromMappingsGem( gems, (ExecutableElement) source, messager, mappings ); + } } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/composition/CustomValueAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/value/composition/CustomValueAnnotation.java new file mode 100644 index 0000000000..f9a7500a35 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/composition/CustomValueAnnotation.java @@ -0,0 +1,21 @@ +/* + * 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.value.composition; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; + +/** + * @author orange add + */ +@Retention( RetentionPolicy.CLASS ) +@ValueMapping(source = "EXTRA", target = "SPECIAL") +@ValueMapping(source = MappingConstants.ANY_REMAINING, target = "DEFAULT") +public @interface CustomValueAnnotation { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/composition/ValueCompositionTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/composition/ValueCompositionTest.java new file mode 100644 index 0000000000..d6d8ce0bed --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/composition/ValueCompositionTest.java @@ -0,0 +1,48 @@ +/* + * 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.value.composition; + +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.factory.Mappers; + +import static org.assertj.core.api.Assertions.assertThat; +/** + * @author orange add + */ +@IssueKey("3037") +@WithClasses({ + ValueMappingCompositionMapper.class, + ExternalOrderType.class, + OrderType.class, + CustomValueAnnotation.class +}) +public class ValueCompositionTest { + + @ProcessorTest + public void shouldValueCompositionSuccess() { + ValueMappingCompositionMapper compositionMapper = Mappers.getMapper( ValueMappingCompositionMapper.class ); + assertThat( compositionMapper.orderTypeToExternalOrderType( OrderType.EXTRA ) ) + .isEqualTo( ExternalOrderType.SPECIAL ); + assertThat( compositionMapper.orderTypeToExternalOrderType( OrderType.NORMAL ) ) + .isEqualTo( ExternalOrderType.DEFAULT ); + } + + @ProcessorTest + public void duplicateValueMappingAnnotation() { + ValueMappingCompositionMapper compositionMapper = Mappers.getMapper( ValueMappingCompositionMapper.class ); + assertThat( compositionMapper.duplicateAnnotation( OrderType.EXTRA ) ) + .isEqualTo( ExternalOrderType.SPECIAL ); + assertThat( compositionMapper.duplicateAnnotation( OrderType.STANDARD ) ) + .isEqualTo( ExternalOrderType.SPECIAL ); + assertThat( compositionMapper.duplicateAnnotation( OrderType.NORMAL ) ) + .isEqualTo( ExternalOrderType.DEFAULT ); + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/composition/ValueMappingCompositionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/composition/ValueMappingCompositionMapper.java new file mode 100644 index 0000000000..c3e0e64d69 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/composition/ValueMappingCompositionMapper.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.ap.test.value.composition; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ap.test.value.ExternalOrderType; +import org.mapstruct.ap.test.value.OrderType; + +/** + * @author orange add + */ +@Mapper +public interface ValueMappingCompositionMapper { + + @CustomValueAnnotation + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); + + @CustomValueAnnotation + @ValueMapping(source = "STANDARD", target = "SPECIAL") + ExternalOrderType duplicateAnnotation(OrderType orderType); +} From 93f7c3b8eadef4ad37b35ae1bb4e6ac41b09a781 Mon Sep 17 00:00:00 2001 From: Orange Add <48479242+chenzijia12300@users.noreply.github.com> Date: Fri, 4 Nov 2022 06:22:35 +0800 Subject: [PATCH 324/597] #3015 Fix annotations to the forged methods --- .../model/AbstractMappingMethodBuilder.java | 4 + .../ap/internal/model/ValueMappingMethod.java | 20 ++- .../ap/test/bugs/_3015/Issue3015Mapper.java | 153 ++++++++++++++++++ .../ap/test/bugs/_3015/Issue3015Test.java | 38 +++++ 4 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3015/Issue3015Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3015/Issue3015Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index 6efb67ba9c..c23dd50430 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -14,6 +14,7 @@ import org.mapstruct.ap.internal.util.Strings; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -121,6 +122,9 @@ public ForgedMethodHistory getDescription() { } public List getMethodAnnotations() { + if ( method instanceof ForgedMethod ) { + return Collections.emptyList(); + } AdditionalAnnotationsBuilder additionalAnnotationsBuilder = new AdditionalAnnotationsBuilder( ctx.getElementUtils(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index f61e80c2d4..6b33faa464 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.model; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -117,13 +118,20 @@ else if ( sourceType.isString() && targetType.isEnumType() ) { LifecycleMethodResolver.beforeMappingMethods( method, selectionParameters, ctx, existingVariables ); List afterMappingMethods = LifecycleMethodResolver.afterMappingMethods( method, selectionParameters, ctx, existingVariables ); - AdditionalAnnotationsBuilder additionalAnnotationsBuilder = + List annotations; + if ( method instanceof ForgedMethod ) { + annotations = Collections.emptyList(); + } + else { + annotations = new ArrayList<>(); + AdditionalAnnotationsBuilder additionalAnnotationsBuilder = new AdditionalAnnotationsBuilder( - ctx.getElementUtils(), - ctx.getTypeFactory(), - ctx.getMessager() ); - List annotations = new ArrayList<>(); - annotations.addAll( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); + ctx.getElementUtils(), + ctx.getTypeFactory(), + ctx.getMessager() ); + + annotations.addAll( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); + } // finally return a mapping return new ValueMappingMethod( method, annotations, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3015/Issue3015Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3015/Issue3015Mapper.java new file mode 100644 index 0000000000..1683db8f39 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3015/Issue3015Mapper.java @@ -0,0 +1,153 @@ +/* + * 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._3015; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.annotatewith.CustomMethodOnlyAnnotation; + +/** + * @author orange add + */ +@Mapper +public interface Issue3015Mapper { + + @AnnotateWith( CustomMethodOnlyAnnotation.class ) + Target map(Source source); + + class Source { + + private NestedSource nested; + private List list; + private Stream stream; + private AnnotateSourceEnum annotateWithEnum; + private Map map; + + public NestedSource getNested() { + return nested; + } + + public void setNested(NestedSource nested) { + this.nested = nested; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + public Stream getStream() { + return stream; + } + + public void setStream(Stream stream) { + this.stream = stream; + } + + public AnnotateSourceEnum getAnnotateWithEnum() { + return annotateWithEnum; + } + + public void setAnnotateWithEnum(AnnotateSourceEnum annotateWithEnum) { + this.annotateWithEnum = annotateWithEnum; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } + } + + class Target { + private NestedTarget nested; + private List list; + private Stream stream; + private AnnotateTargetEnum annotateWithEnum; + private Map map; + + public NestedTarget getNested() { + return nested; + } + + public void setNested(NestedTarget nested) { + this.nested = nested; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + public Stream getStream() { + return stream; + } + + public void setStream(Stream stream) { + this.stream = stream; + } + + public AnnotateTargetEnum getAnnotateWithEnum() { + return annotateWithEnum; + } + + public void setAnnotateWithEnum(AnnotateTargetEnum annotateWithEnum) { + this.annotateWithEnum = annotateWithEnum; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } + } + + enum AnnotateSourceEnum { + EXISTING; + } + + enum AnnotateTargetEnum { + EXISTING; + } + + class NestedSource { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class NestedTarget { + private Integer value; + + public Integer getValue() { + return value; + } + + public void setValue(Integer value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3015/Issue3015Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3015/Issue3015Test.java new file mode 100644 index 0000000000..96004b8c2d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3015/Issue3015Test.java @@ -0,0 +1,38 @@ +/* + * 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._3015; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.mapstruct.ap.test.annotatewith.CustomMethodOnlyAnnotation; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.factory.Mappers; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author orange add + */ +@WithClasses({ + Issue3015Mapper.class, + CustomMethodOnlyAnnotation.class +}) +class Issue3015Test { + + @ProcessorTest + void noNeedPassAnnotationToForgeMethod() { + Issue3015Mapper mapper = Mappers.getMapper( Issue3015Mapper.class ); + Method[] declaredMethods = mapper.getClass().getDeclaredMethods(); + List annotationMethods = Arrays.stream( declaredMethods ) + .filter( method -> method.getAnnotation( CustomMethodOnlyAnnotation.class ) != null ) + .collect( Collectors.toList() ); + assertThat( annotationMethods ).hasSize( 1 ); + } +} From 16e3ceadec76ad4f3657e8af2f99172241b8dfd3 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 3 Nov 2022 23:29:47 +0100 Subject: [PATCH 325/597] #2952 Do not treat a getter as an alternative write accessor when using `CollectionMappingStrategy#TARGET_IMMUTABLE` --- .../mapstruct/CollectionMappingStrategy.java | 48 ++++++++++++++ .../ap/internal/model/common/Type.java | 7 +++ .../ap/test/bugs/_2952/Issue2952Mapper.java | 62 +++++++++++++++++++ .../ap/test/bugs/_2952/Issue2952Test.java | 29 +++++++++ .../CupboardEntityOnlyGetter.java | 3 +- ...apper.java => CupboardNoSetterMapper.java} | 4 +- .../immutabletarget/ImmutableProductTest.java | 23 +++---- 7 files changed, 159 insertions(+), 17 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Test.java rename processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/{ErroneousCupboardMapper.java => CupboardNoSetterMapper.java} (80%) diff --git a/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java b/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java index afaba97370..0ea3ee7df5 100644 --- a/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java +++ b/core/src/main/java/org/mapstruct/CollectionMappingStrategy.java @@ -7,6 +7,54 @@ /** * Strategy for propagating the value of collection-typed properties from source to target. + *

        + * In the table below, the dash {@code -} indicates a property name. + * Next, the trailing {@code s} indicates the plural form. + * The table explains the options and how they are applied to the presence / absence of a + * {@code set-s}, {@code add-} and / or {@code get-s} method on the target object. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
        Collection mapping strategy options
        OptionOnly target set-s AvailableOnly target add- AvailableBoth set-s/add- AvailableNo set-s/add- AvailableExisting Target ({@code @TargetType})
        {@link #ACCESSOR_ONLY}set-sget-sset-sget-sget-s
        {@link #SETTER_PREFERRED}set-sadd-set-sget-sget-s
        {@link #ADDER_PREFERRED}set-sadd-add-get-sget-s
        {@link #TARGET_IMMUTABLE}set-sexceptionset-sexceptionset-s
        * * @author Sjaak Derksen */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 6d86aad452..6894906266 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -788,6 +788,13 @@ else if ( candidate.getAccessorType() == AccessorType.GETTER ) { // an adder has been found (according strategy) so overrule current choice. candidate = adderMethod; } + + if ( cmStrategy == CollectionMappingStrategyGem.TARGET_IMMUTABLE + && candidate.getAccessorType() == AccessorType.GETTER ) { + // If the collection mapping strategy is target immutable + // then the getter method cannot be used as a setter + continue; + } } else if ( candidate.getAccessorType() == AccessorType.FIELD && ( Executables.isFinal( candidate ) || result.containsKey( targetPropertyName ) ) ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Mapper.java new file mode 100644 index 0000000000..885d74448f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Mapper.java @@ -0,0 +1,62 @@ +/* + * 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._2952; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE) +public interface Issue2952Mapper { + + Issue2952Mapper INSTANCE = Mappers.getMapper( Issue2952Mapper.class ); + + Target map(Source source); + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Target { + private final Map attributes = new HashMap<>(); + private final List values = new ArrayList<>(); + private String value; + + public Map getAttributes() { + return Collections.unmodifiableMap( attributes ); + } + + public List getValues() { + return Collections.unmodifiableList( values ); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Test.java new file mode 100644 index 0000000000..7a517ec979 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2952/Issue2952Test.java @@ -0,0 +1,29 @@ +/* + * 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._2952; + +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 Filip Hrisafov + */ +@IssueKey("2952") +@WithClasses({ + Issue2952Mapper.class +}) +class Issue2952Test { + + @ProcessorTest + void shouldCorrectIgnoreImmutableIterable() { + Issue2952Mapper.Target target = Issue2952Mapper.INSTANCE.map( new Issue2952Mapper.Source( "test" ) ); + + assertThat( target.getValue() ).isEqualTo( "test" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardEntityOnlyGetter.java b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardEntityOnlyGetter.java index bbf3a6d24e..1f2f2f00b7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardEntityOnlyGetter.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardEntityOnlyGetter.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.collection.immutabletarget; +import java.util.ArrayList; import java.util.List; /** @@ -13,7 +14,7 @@ */ public class CupboardEntityOnlyGetter { - private List content; + private List content = new ArrayList<>(); public List getContent() { return content; diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ErroneousCupboardMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardNoSetterMapper.java similarity index 80% rename from processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ErroneousCupboardMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardNoSetterMapper.java index 028ac4d765..a6674e629f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ErroneousCupboardMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/CupboardNoSetterMapper.java @@ -15,9 +15,9 @@ * @author Sjaak Derksen */ @Mapper( collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE ) -public interface ErroneousCupboardMapper { +public interface CupboardNoSetterMapper { - ErroneousCupboardMapper INSTANCE = Mappers.getMapper( ErroneousCupboardMapper.class ); + CupboardNoSetterMapper INSTANCE = Mappers.getMapper( CupboardNoSetterMapper.class ); void map( CupboardDto in, @MappingTarget CupboardEntityOnlyGetter out ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java index b3d2096bc3..de22436296 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java @@ -11,9 +11,6 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; -import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; -import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import static org.assertj.core.api.Assertions.assertThat; @@ -41,19 +38,17 @@ public void shouldHandleImmutableTarget() { @ProcessorTest @WithClasses({ - ErroneousCupboardMapper.class, + CupboardNoSetterMapper.class, CupboardEntityOnlyGetter.class }) - @ExpectedCompilationOutcome( - value = CompilationResult.FAILED, - diagnostics = { - @Diagnostic(type = ErroneousCupboardMapper.class, - kind = javax.tools.Diagnostic.Kind.ERROR, - line = 22, - message = "No write accessor found for property \"content\" in target type.") - } - ) - public void testShouldFailOnPropertyMappingNoPropertySetterOnlyGetter() { + public void shouldIgnoreImmutableTarget() { + CupboardDto in = new CupboardDto(); + in.setContent( Arrays.asList( "flour", "peas" ) ); + CupboardEntityOnlyGetter out = new CupboardEntityOnlyGetter(); + out.getContent().add( "bread" ); + CupboardNoSetterMapper.INSTANCE.map( in, out ); + + assertThat( out.getContent() ).containsExactly( "bread" ); } } From 8894cd5935f238ae32338f49cd4595e4562f87f2 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Fri, 4 Nov 2022 14:21:05 +0100 Subject: [PATCH 326/597] #3057: limit do not allow self to subclassmappings. (#3063) * #3057: limit do not allow self to subclassmappings. * #3057: determine method candidates after all other fields are set in the constructor. Co-authored-by: Ben Zegveld Co-authored-by: Filip Hrisafov --- .../ap/internal/model/BeanMappingMethod.java | 6 +- .../source/selector/SelectionCriteria.java | 10 ++++ .../creation/MappingResolverImpl.java | 8 ++- .../ap/test/bugs/_3057/Issue3057Mapper.java | 55 +++++++++++++++++++ .../test/bugs/_3057/Issue3057MapperTest.java | 34 ++++++++++++ 5 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3057/Issue3057Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3057/Issue3057MapperTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 24e160b9de..0ea4ecd197 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -399,15 +399,13 @@ private SubclassMapping createSubclassMapping(SubclassMappingOptions subclassMap "SubclassMapping for " + sourceType.getFullyQualifiedName() ); SelectionCriteria criteria = SelectionCriteria - .forMappingMethods( + .forSubclassMappingMethods( new SelectionParameters( Collections.emptyList(), Collections.emptyList(), subclassMappingOptions.getTarget(), ctx.getTypeUtils() ).withSourceRHS( rightHandSide ), - subclassMappingOptions.getMappingControl( ctx.getElementUtils() ), - null, - false ); + subclassMappingOptions.getMappingControl( ctx.getElementUtils() ) ); Assignment assignment = ctx .getMappingResolver() .getTargetAssignment( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java index 7e12bbd188..2d288dd56e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java @@ -149,6 +149,10 @@ public boolean isAllow2Steps() { return allow2Steps; } + public boolean isSelfAllowed() { + return type != Type.SELF_NOT_ALLOWED; + } + public static SelectionCriteria forMappingMethods(SelectionParameters selectionParameters, MappingControl mappingControl, String targetPropertyName, boolean preferUpdateMapping) { @@ -173,10 +177,16 @@ public static SelectionCriteria forPresenceCheckMethods(SelectionParameters sele return new SelectionCriteria( selectionParameters, null, null, Type.PRESENCE_CHECK ); } + public static SelectionCriteria forSubclassMappingMethods(SelectionParameters selectionParameters, + MappingControl mappingControl) { + return new SelectionCriteria( selectionParameters, mappingControl, null, Type.SELF_NOT_ALLOWED ); + } + public enum Type { PREFER_UPDATE_MAPPING, OBJECT_FACTORY, LIFECYCLE_CALLBACK, PRESENCE_CHECK, + SELF_NOT_ALLOWED, } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 5a2af176f6..12b345d238 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -196,7 +196,6 @@ private ResolvingAttempt(List sourceModel, Method mappingMethod, ForgedM this.mappingMethod = mappingMethod; this.description = description; - this.methods = filterPossibleCandidateMethods( sourceModel, mappingMethod ); this.formattingParameters = formattingParameters == null ? FormattingParameters.EMPTY : formattingParameters; this.sourceRHS = sourceRHS; @@ -207,13 +206,14 @@ private ResolvingAttempt(List sourceModel, Method mappingMethod, ForgedM this.builtIns = builtIns; this.messager = messager; this.reportingLimitAmbiguous = verboseLogging ? Integer.MAX_VALUE : LIMIT_REPORTING_AMBIGUOUS; + this.methods = filterPossibleCandidateMethods( sourceModel, mappingMethod ); } // CHECKSTYLE:ON private List filterPossibleCandidateMethods(List candidateMethods, T mappingMethod) { List result = new ArrayList<>( candidateMethods.size() ); for ( T candidate : candidateMethods ) { - if ( isCandidateForMapping( candidate ) && !candidate.equals( mappingMethod )) { + if ( isCandidateForMapping( candidate ) && isNotSelfOrSelfAllowed( mappingMethod, candidate )) { result.add( candidate ); } } @@ -221,6 +221,10 @@ private List filterPossibleCandidateMethods(List candid return result; } + private boolean isNotSelfOrSelfAllowed(T mappingMethod, T candidate) { + return selectionCriteria == null || selectionCriteria.isSelfAllowed() || !candidate.equals( mappingMethod ); + } + private Assignment getTargetAssignment(Type sourceType, Type targetType) { Assignment assignment; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3057/Issue3057Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3057/Issue3057Mapper.java new file mode 100644 index 0000000000..d8c444d8c5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3057/Issue3057Mapper.java @@ -0,0 +1,55 @@ +/* + * 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._3057; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@Mapper +public interface Issue3057Mapper { + + Issue3057Mapper INSTANCE = Mappers.getMapper( Issue3057Mapper.class ); + + class Source { + private Source self; + + public Source getSelf() { + return self; + } + + public void setSelf(Source self) { + this.self = self; + } + } + + class Target { + private Target self; + private String value; + + public Target getSelf() { + return self; + } + + public void setSelf(Target self) { + this.self = self; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + @Mapping( target = "value", constant = "constantValue" ) + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3057/Issue3057MapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3057/Issue3057MapperTest.java new file mode 100644 index 0000000000..7e64f529f3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3057/Issue3057MapperTest.java @@ -0,0 +1,34 @@ +/* + * 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._3057; + +import org.mapstruct.ap.test.bugs._3057.Issue3057Mapper.Source; +import org.mapstruct.ap.test.bugs._3057.Issue3057Mapper.Target; +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 Ben Zegveld + */ +@WithClasses(Issue3057Mapper.class) +@IssueKey("3057") +class Issue3057MapperTest { + + @ProcessorTest + void mapsSelf() { + Source sourceOuter = new Issue3057Mapper.Source(); + Source sourceInner = new Issue3057Mapper.Source(); + sourceOuter.setSelf( sourceInner ); + + Target targetOuter = Issue3057Mapper.INSTANCE.map( sourceOuter ); + + assertThat( targetOuter.getValue() ).isEqualTo( "constantValue" ); + assertThat( targetOuter.getSelf().getValue() ).isEqualTo( "constantValue" ); + } +} From 82b19b0d8a7addcb137b1d53e9e753fde0957304 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 12 Nov 2022 10:04:03 +0100 Subject: [PATCH 327/597] #3077 Add test case --- .../ap/test/bugs/_3077/Issue3077Mapper.java | 65 +++++++++++++++++++ .../test/bugs/_3077/Issue3077MapperTest.java | 31 +++++++++ 2 files changed, 96 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3077/Issue3077Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3077/Issue3077MapperTest.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3077/Issue3077Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3077/Issue3077Mapper.java new file mode 100644 index 0000000000..f1a630a444 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3077/Issue3077Mapper.java @@ -0,0 +1,65 @@ +/* + * 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._3077; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3077Mapper { + + Issue3077Mapper INSTANCE = Mappers.getMapper( Issue3077Mapper.class ); + + class Source { + private final String source; + private final Source self; + + public Source(String source, Source self) { + this.source = source; + this.self = self; + } + + public String getSource() { + return source; + } + + public Source getSelf() { + return self; + } + } + + class Target { + private String value; + private Target self; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Target getSelf() { + return self; + } + + public void setSelf(Target self) { + this.self = self; + } + + } + + @Named("self") + @Mapping(target = "value", source = "source") + @Mapping(target = "self", qualifiedByName = "self") + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3077/Issue3077MapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3077/Issue3077MapperTest.java new file mode 100644 index 0000000000..bfba7988aa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3077/Issue3077MapperTest.java @@ -0,0 +1,31 @@ +/* + * 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._3077; + +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 Filip Hrisafov + */ +@WithClasses(Issue3077Mapper.class) +@IssueKey("3057") +class Issue3077MapperTest { + + @ProcessorTest + void mapsSelf() { + Issue3077Mapper.Source sourceInner = new Issue3077Mapper.Source( "inner", null ); + Issue3077Mapper.Source sourceOuter = new Issue3077Mapper.Source( "outer", sourceInner ); + + Issue3077Mapper.Target targetOuter = Issue3077Mapper.INSTANCE.map( sourceOuter ); + + assertThat( targetOuter.getValue() ).isEqualTo( "outer" ); + assertThat( targetOuter.getSelf().getValue() ).isEqualTo( "inner" ); + } +} From fd27380185fe83feb6d26308c7d358025783ff8f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 13 Nov 2022 14:22:25 +0100 Subject: [PATCH 328/597] #2953 Add support for globally defining nullValueMapMappingStrategy and nullValueIterableMappingStrategy --- .../main/asciidoc/chapter-2-set-up.asciidoc | 24 ++++++++++ .../org/mapstruct/ap/MappingProcessor.java | 18 +++++++- .../internal/model/source/DefaultOptions.java | 8 ++++ .../mapstruct/ap/internal/option/Options.java | 20 ++++++++- .../test/nullvaluemapping/CarListMapper.java | 30 +++++++++++++ .../CarListMapperSettingOnMapper.java | 31 +++++++++++++ .../test/nullvaluemapping/CarMapMapper.java | 30 +++++++++++++ .../CarMapMapperSettingOnMapper.java | 31 +++++++++++++ .../NullValueIterableMappingStrategyTest.java | 45 +++++++++++++++++++ .../NullValueMapMappingStrategyTest.java | 45 +++++++++++++++++++ 10 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarListMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarListMapperSettingOnMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapMapperSettingOnMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueIterableMappingStrategyTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMapMappingStrategyTest.java diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 601bc42a99..12c399c1f5 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -266,6 +266,30 @@ If a policy is given for a specific bean mapping via `@BeanMapping#ignoreUnmappe disableBuilders` |If set to `true`, then MapStruct will not use builder patterns when doing the mapping. This is equivalent to doing `@Mapper( builder = @Builder( disableBuilder = true ) )` for all of your mappers. |`false` + +|`mapstruct.nullValueIterableMappingStrategy` +|The strategy to be applied when `null` is passed as a source value to an iterable mapping. + +Supported values are: + +* `RETURN_NULL`: if `null` is passed as a source value, then `null` will be returned +* `RETURN_DEFAULT`: if `null` is passed then a default value (empty collection) will be returned + +If a strategy is given for a specific mapper via `@Mapper#nullValueIterableMappingStrategy()`, the value from the annotation takes precedence. +If a strategy is given for a specific iterable mapping via `@IterableMapping#nullValueMappingStrategy()`, it takes precedence over both `@Mapper#nullValueIterableMappingStrategy()` and the option. +|`RETURN_NULL` + +|`mapstruct.nullValueMapMappingStrategy` +|The strategy to be applied when `null` is passed as a source value to a map mapping. + +Supported values are: + +* `RETURN_NULL`: if `null` is passed as a source value, then `null` will be returned +* `RETURN_DEFAULT`: if `null` is passed then a default value (empty map) will be returned + +If a strategy is given for a specific mapper via `@Mapper#nullValueMapMappingStrategy()`, the value from the annotation takes precedence. +If a strategy is given for a specific map mapping via `@MapMapping#nullValueMappingStrategy()`, it takes precedence over both `@Mapper#nullValueMapMappingStrategy()` and the option. +|`RETURN_NULL` |=== === Using MapStruct with the Java Module System diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 2a4af558f2..8c842f007b 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -12,6 +12,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; @@ -32,6 +33,7 @@ import javax.lang.model.util.ElementKindVisitor6; import javax.tools.Diagnostic.Kind; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.gem.MapperGem; @@ -87,7 +89,9 @@ MappingProcessor.DEFAULT_COMPONENT_MODEL, MappingProcessor.DEFAULT_INJECTION_STRATEGY, MappingProcessor.DISABLE_BUILDERS, - MappingProcessor.VERBOSE + MappingProcessor.VERBOSE, + MappingProcessor.NULL_VALUE_ITERABLE_MAPPING_STRATEGY, + MappingProcessor.NULL_VALUE_MAP_MAPPING_STRATEGY, }) public class MappingProcessor extends AbstractProcessor { @@ -106,6 +110,8 @@ public class MappingProcessor extends AbstractProcessor { protected static final String ALWAYS_GENERATE_SERVICE_FILE = "mapstruct.alwaysGenerateServicesFile"; protected static final String DISABLE_BUILDERS = "mapstruct.disableBuilders"; protected static final String VERBOSE = "mapstruct.verbose"; + protected static final String NULL_VALUE_ITERABLE_MAPPING_STRATEGY = "mapstruct.nullValueIterableMappingStrategy"; + protected static final String NULL_VALUE_MAP_MAPPING_STRATEGY = "mapstruct.nullValueMapMappingStrategy"; private Options options; @@ -139,6 +145,9 @@ public synchronized void init(ProcessingEnvironment processingEnv) { private Options createOptions() { String unmappedTargetPolicy = processingEnv.getOptions().get( UNMAPPED_TARGET_POLICY ); String unmappedSourcePolicy = processingEnv.getOptions().get( UNMAPPED_SOURCE_POLICY ); + String nullValueIterableMappingStrategy = processingEnv.getOptions() + .get( NULL_VALUE_ITERABLE_MAPPING_STRATEGY ); + String nullValueMapMappingStrategy = processingEnv.getOptions().get( NULL_VALUE_MAP_MAPPING_STRATEGY ); return new Options( Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ), @@ -149,7 +158,12 @@ private Options createOptions() { processingEnv.getOptions().get( DEFAULT_INJECTION_STRATEGY ), Boolean.parseBoolean( processingEnv.getOptions().get( ALWAYS_GENERATE_SERVICE_FILE ) ), Boolean.parseBoolean( processingEnv.getOptions().get( DISABLE_BUILDERS ) ), - Boolean.parseBoolean( processingEnv.getOptions().get( VERBOSE ) ) + Boolean.parseBoolean( processingEnv.getOptions().get( VERBOSE ) ), + nullValueIterableMappingStrategy != null ? + NullValueMappingStrategyGem.valueOf( nullValueIterableMappingStrategy.toUpperCase( Locale.ROOT ) ) : + null, + nullValueMapMappingStrategy != null ? + NullValueMappingStrategyGem.valueOf( nullValueMapMappingStrategy.toUpperCase( Locale.ROOT ) ) : null ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index 5c76e7face..c754d3c395 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -132,10 +132,18 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { } public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { + NullValueMappingStrategyGem nullValueIterableMappingStrategy = options.getNullValueIterableMappingStrategy(); + if ( nullValueIterableMappingStrategy != null ) { + return nullValueIterableMappingStrategy; + } return NullValueMappingStrategyGem.valueOf( mapper.nullValueIterableMappingStrategy().getDefaultValue() ); } public NullValueMappingStrategyGem getNullValueMapMappingStrategy() { + NullValueMappingStrategyGem nullValueMapMappingStrategy = options.getNullValueMapMappingStrategy(); + if ( nullValueMapMappingStrategy != null ) { + return nullValueMapMappingStrategy; + } return NullValueMappingStrategyGem.valueOf( mapper.nullValueMapMappingStrategy().getDefaultValue() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java index 65089cc139..a544374c0e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.internal.option; +import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; /** @@ -23,14 +24,21 @@ public class Options { private final String defaultInjectionStrategy; private final boolean disableBuilders; private final boolean verbose; + private final NullValueMappingStrategyGem nullValueIterableMappingStrategy; + private final NullValueMappingStrategyGem nullValueMapMappingStrategy; + //CHECKSTYLE:OFF public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVersionComment, ReportingPolicyGem unmappedTargetPolicy, ReportingPolicyGem unmappedSourcePolicy, String defaultComponentModel, String defaultInjectionStrategy, boolean alwaysGenerateSpi, boolean disableBuilders, - boolean verbose) { + boolean verbose, + NullValueMappingStrategyGem nullValueIterableMappingStrategy, + NullValueMappingStrategyGem nullValueMapMappingStrategy + ) { + //CHECKSTYLE:ON this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; this.suppressGeneratorVersionComment = suppressGeneratorVersionComment; this.unmappedTargetPolicy = unmappedTargetPolicy; @@ -40,6 +48,8 @@ public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVers this.alwaysGenerateSpi = alwaysGenerateSpi; this.disableBuilders = disableBuilders; this.verbose = verbose; + this.nullValueIterableMappingStrategy = nullValueIterableMappingStrategy; + this.nullValueMapMappingStrategy = nullValueMapMappingStrategy; } public boolean isSuppressGeneratorTimestamp() { @@ -77,4 +87,12 @@ public boolean isDisableBuilders() { public boolean isVerbose() { return verbose; } + + public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { + return nullValueIterableMappingStrategy; + } + + public NullValueMappingStrategyGem getNullValueMapMappingStrategy() { + return nullValueMapMappingStrategy; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarListMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarListMapper.java new file mode 100644 index 0000000000..d239f9ace7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarListMapper.java @@ -0,0 +1,30 @@ +/* + * 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.nullvaluemapping; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CarListMapper { + + CarListMapper INSTANCE = Mappers.getMapper( CarListMapper.class ); + + @Mapping(target = "seatCount", ignore = true) + @Mapping(target = "model", ignore = true) + @Mapping(target = "catalogId", ignore = true) + CarDto map(Car car); + + List carsToCarDtoList(List cars); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarListMapperSettingOnMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarListMapperSettingOnMapper.java new file mode 100644 index 0000000000..860b17654a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarListMapperSettingOnMapper.java @@ -0,0 +1,31 @@ +/* + * 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.nullvaluemapping; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueMappingStrategy; +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(nullValueIterableMappingStrategy = NullValueMappingStrategy.RETURN_NULL) +public interface CarListMapperSettingOnMapper { + + CarListMapperSettingOnMapper INSTANCE = Mappers.getMapper( CarListMapperSettingOnMapper.class ); + + @Mapping(target = "seatCount", ignore = true) + @Mapping(target = "model", ignore = true) + @Mapping(target = "catalogId", ignore = true) + CarDto map(Car car); + + List carsToCarDtoList(List cars); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapMapper.java new file mode 100644 index 0000000000..0227949f4d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapMapper.java @@ -0,0 +1,30 @@ +/* + * 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.nullvaluemapping; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CarMapMapper { + + CarMapMapper INSTANCE = Mappers.getMapper( CarMapMapper.class ); + + @Mapping(target = "seatCount", ignore = true) + @Mapping(target = "model", ignore = true) + @Mapping(target = "catalogId", ignore = true) + CarDto map(Car car); + + Map carsToCarDtoMap(Map cars); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapMapperSettingOnMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapMapperSettingOnMapper.java new file mode 100644 index 0000000000..adb99887bd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/CarMapMapperSettingOnMapper.java @@ -0,0 +1,31 @@ +/* + * 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.nullvaluemapping; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueMappingStrategy; +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(nullValueMapMappingStrategy = NullValueMappingStrategy.RETURN_NULL) +public interface CarMapMapperSettingOnMapper { + + CarMapMapperSettingOnMapper INSTANCE = Mappers.getMapper( CarMapMapperSettingOnMapper.class ); + + @Mapping(target = "seatCount", ignore = true) + @Mapping(target = "model", ignore = true) + @Mapping(target = "catalogId", ignore = true) + CarDto map(Car car); + + Map carsToCarDtoMap(Map cars); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueIterableMappingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueIterableMappingStrategyTest.java new file mode 100644 index 0000000000..c4acecd64f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueIterableMappingStrategyTest.java @@ -0,0 +1,45 @@ +/* + * 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.nullvaluemapping; + +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + CarDto.class, + Car.class +}) +@IssueKey("2953") +public class NullValueIterableMappingStrategyTest { + + @ProcessorTest + @ProcessorOption(name = "mapstruct.nullValueIterableMappingStrategy", value = "return_default") + @WithClasses({ + CarListMapper.class + }) + void globalNullIterableMappingStrategy() { + assertThat( CarListMapper.INSTANCE.carsToCarDtoList( null ) ).isEmpty(); + } + + @ProcessorTest + @ProcessorOption(name = "mapstruct.nullValueIterableMappingStrategy", value = "return_default") + @WithClasses({ + CarListMapperSettingOnMapper.class + }) + void globalNullMapMappingStrategyWithOverrideInMapper() { + // Explicit definition in @Mapper should override global + assertThat( CarListMapperSettingOnMapper.INSTANCE.carsToCarDtoList( null ) ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMapMappingStrategyTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMapMappingStrategyTest.java new file mode 100644 index 0000000000..33b5942f85 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluemapping/NullValueMapMappingStrategyTest.java @@ -0,0 +1,45 @@ +/* + * 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.nullvaluemapping; + +import org.mapstruct.ap.test.nullvaluemapping._target.CarDto; +import org.mapstruct.ap.test.nullvaluemapping.source.Car; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + CarDto.class, + Car.class +}) +@IssueKey("2953") +public class NullValueMapMappingStrategyTest { + + @ProcessorTest + @ProcessorOption(name = "mapstruct.nullValueMapMappingStrategy", value = "return_default") + @WithClasses({ + CarMapMapper.class + }) + void globalNullMapMappingStrategy() { + assertThat( CarMapMapper.INSTANCE.carsToCarDtoMap( null ) ).isEmpty(); + } + + @ProcessorTest + @ProcessorOption(name = "mapstruct.nullValueMapMappingStrategy", value = "return_default") + @WithClasses({ + CarMapMapperSettingOnMapper.class + }) + void globalNullMapMappingStrategyWithOverrideInMapper() { + // Explicit definition in @Mapper should override global + assertThat( CarMapMapperSettingOnMapper.INSTANCE.carsToCarDtoMap( null ) ).isNull(); + } +} From a7ba12676d237957be0753076f436d7974c9f7f4 Mon Sep 17 00:00:00 2001 From: Claudio Nave Date: Sun, 5 Feb 2023 12:17:02 +0100 Subject: [PATCH 329/597] #3110 Fix throws declaration for ValueMapping annotated methods (#3122) #3110 Fix throws declaration for ValueMapping annotated methods --- .../ap/internal/model/ValueMappingMethod.ftl | 10 ++++++- .../ap/test/bugs/_3110/Issue3110Mapper.java | 29 +++++++++++++++++++ .../test/bugs/_3110/Issue3110MapperTest.java | 27 +++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3110/Issue3110Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3110/Issue3110MapperTest.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl index 2c3e2b6410..016cfcd182 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -10,7 +10,7 @@ <#nt><@includeModel object=annotation/> <#if overridden>@Override -<#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, ) { +<#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#list beforeMappingReferencesWithoutMappingTarget as callback> <@includeModel object=callback targetBeanName=resultName targetType=resultType/> <#if !callback_has_next> @@ -69,3 +69,11 @@ +<#macro throws> + <#if (thrownTypes?size > 0)><#lt> throws <@compress single_line=true> + <#list thrownTypes as exceptionType> + <@includeModel object=exceptionType/> + <#if exceptionType_has_next>, <#t> + + + diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3110/Issue3110Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3110/Issue3110Mapper.java new file mode 100644 index 0000000000..12dd823760 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3110/Issue3110Mapper.java @@ -0,0 +1,29 @@ +/* + * 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._3110; + +import org.mapstruct.EnumMapping; +import org.mapstruct.Mapper; + +@Mapper +public interface Issue3110Mapper { + enum SourceEnum { + FOO, BAR + } + + enum TargetEnum { + FOO, BAR + } + + class CustomCheckedException extends Exception { + public CustomCheckedException(String message) { + super( message ); + } + } + + @EnumMapping(unexpectedValueMappingException = CustomCheckedException.class) + TargetEnum map(SourceEnum sourceEnum) throws CustomCheckedException; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3110/Issue3110MapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3110/Issue3110MapperTest.java new file mode 100644 index 0000000000..3b256ba100 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3110/Issue3110MapperTest.java @@ -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._3110; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +@WithClasses({ + Issue3110Mapper.class +}) +@IssueKey("3110") +class Issue3110MapperTest { + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + void throwsException() { + generatedSource.forMapper( Issue3110Mapper.class ).content() + .contains( "throws CustomCheckedException" ); + } +} From 89db26a1af35fb759a2fa74b8bc7586cb743c454 Mon Sep 17 00:00:00 2001 From: Claudio Nave Date: Fri, 17 Mar 2023 09:06:13 +0100 Subject: [PATCH 330/597] #3119 Add qualifiedBy and qualifiedByName to SubclassMapping annotation --- .../main/java/org/mapstruct/Qualifier.java | 1 + .../java/org/mapstruct/SubclassMapping.java | 26 ++ ...apter-10-advanced-mapping-options.asciidoc | 2 + .../ap/internal/model/BeanMappingMethod.java | 20 +- .../model/source/SubclassMappingOptions.java | 33 +- .../subclassmapping/qualifier/Composer.java | 18 ++ .../qualifier/ComposerDto.java | 18 ++ .../ErroneousSubclassQualifiedByMapper.java | 16 + ...rroneousSubclassQualifiedByNameMapper.java | 16 + .../test/subclassmapping/qualifier/Light.java | 19 ++ .../qualifier/NonExistent.java | 19 ++ .../subclassmapping/qualifier/Rossini.java | 20 ++ .../subclassmapping/qualifier/RossiniDto.java | 20 ++ .../qualifier/RossiniMapper.java | 50 +++ .../qualifier/SubclassQualifiedByMapper.java | 37 +++ .../SubclassQualifiedByNameMapper.java | 37 +++ .../SubclassQualifierMapperTest.java | 297 ++++++++++++++++++ .../subclassmapping/qualifier/Unused.java | 19 ++ .../subclassmapping/qualifier/Vivaldi.java | 20 ++ .../subclassmapping/qualifier/VivaldiDto.java | 20 ++ .../qualifier/VivaldiMapper.java | 50 +++ 21 files changed, 740 insertions(+), 18 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Composer.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ComposerDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByNameMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Light.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/NonExistent.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Rossini.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/RossiniDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/RossiniMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifiedByMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifiedByNameMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifierMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Unused.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Vivaldi.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/VivaldiDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/VivaldiMapper.java diff --git a/core/src/main/java/org/mapstruct/Qualifier.java b/core/src/main/java/org/mapstruct/Qualifier.java index be51f49e04..9e18c3e420 100644 --- a/core/src/main/java/org/mapstruct/Qualifier.java +++ b/core/src/main/java/org/mapstruct/Qualifier.java @@ -21,6 +21,7 @@ *

      • {@link IterableMapping#qualifiedBy() }
      • *
      • {@link MapMapping#keyQualifiedBy() }
      • *
      • {@link MapMapping#valueQualifiedBy() }
      • + *
      • {@link SubclassMapping#qualifiedBy() }
      • *
      *

      Example:

      *
      
      diff --git a/core/src/main/java/org/mapstruct/SubclassMapping.java b/core/src/main/java/org/mapstruct/SubclassMapping.java
      index bfd4f9bec5..ccf8d4d472 100644
      --- a/core/src/main/java/org/mapstruct/SubclassMapping.java
      +++ b/core/src/main/java/org/mapstruct/SubclassMapping.java
      @@ -5,6 +5,7 @@
        */
       package org.mapstruct;
       
      +import java.lang.annotation.Annotation;
       import java.lang.annotation.ElementType;
       import java.lang.annotation.Repeatable;
       import java.lang.annotation.Retention;
      @@ -81,4 +82,29 @@
            * @return the target subclass to map the source to.
            */
           Class target();
      +
      +    /**
      +     * A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple
      +     * mapping methods (hand written or generated) qualify and thus would result in an 'Ambiguous mapping methods found'
      +     * error. A qualifier is a custom annotation and can be placed on a hand written mapper class or a method.
      +     *
      +     * @return the qualifiers
      +     * @see Qualifier
      +     */
      +    Class[] qualifiedBy() default {};
      +
      +    /**
      +     * String-based form of qualifiers; When looking for a suitable mapping method for a given property, MapStruct will
      +     * only consider those methods carrying directly or indirectly (i.e. on the class-level) a {@link Named} annotation
      +     * for each of the specified qualifier names.
      +     * 

      + * Note that annotation-based qualifiers are generally preferable as they allow more easily to find references and + * are safe for refactorings, but name-based qualifiers can be a less verbose alternative when requiring a large + * number of qualifiers as no custom annotation types are needed. + * + * @return One or more qualifier name(s) + * @see #qualifiedBy() + * @see Named + */ + String[] qualifiedByName() default {}; } diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index a6e42b6d48..87dffce31c 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -141,6 +141,8 @@ In the case that the `Fruit` is an abstract class or an interface, you would get To allow mappings for abstract classes or interfaces you need to set the `subclassExhaustiveStrategy` to `RUNTIME_EXCEPTION`, you can do this at the `@MapperConfig`, `@Mapper` or `@BeanMapping` annotations. If you then pass a `GrapeDto` an `IllegalArgumentException` will be thrown because it is unknown how to map a `GrapeDto`. Adding the missing (`@SubclassMapping`) for it will fix that. +<> can be used to further control which methods may be chosen to map a specific subclass. For that, you will need to use one of `SubclassMapping#qualifiedByName` or `SubclassMapping#qualifiedBy`. + [TIP] ==== If the mapping method for the subclasses does not exist it will be created and any other annotations on the fruit mapping method will be inherited by the newly generated mappings. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 0ea4ecd197..29d27c8d68 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -399,13 +399,10 @@ private SubclassMapping createSubclassMapping(SubclassMappingOptions subclassMap "SubclassMapping for " + sourceType.getFullyQualifiedName() ); SelectionCriteria criteria = SelectionCriteria - .forSubclassMappingMethods( - new SelectionParameters( - Collections.emptyList(), - Collections.emptyList(), - subclassMappingOptions.getTarget(), - ctx.getTypeUtils() ).withSourceRHS( rightHandSide ), - subclassMappingOptions.getMappingControl( ctx.getElementUtils() ) ); + .forSubclassMappingMethods( + subclassMappingOptions.getSelectionParameters().withSourceRHS( rightHandSide ), + subclassMappingOptions.getMappingControl( ctx.getElementUtils() ) + ); Assignment assignment = ctx .getMappingResolver() .getTargetAssignment( @@ -424,10 +421,13 @@ private SubclassMapping createSubclassMapping(SubclassMappingOptions subclassMap String sourceArgument = null; for ( Parameter parameter : method.getSourceParameters() ) { if ( ctx - .getTypeUtils() - .isAssignable( sourceType.getTypeMirror(), parameter.getType().getTypeMirror() ) ) { + .getTypeUtils() + .isAssignable( sourceType.getTypeMirror(), parameter.getType().getTypeMirror() ) ) { sourceArgument = parameter.getName(); - assignment.setSourceLocalVarName( "(" + sourceType.createReferenceName() + ") " + sourceArgument ); + if ( assignment != null ) { + assignment.setSourceLocalVarName( + "(" + sourceType.createReferenceName() + ") " + sourceArgument ); + } } } return new SubclassMapping( sourceType, sourceArgument, targetType, assignment ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java index 08df8083bc..6ed1117ebd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java @@ -33,12 +33,15 @@ public class SubclassMappingOptions extends DelegatingOptions { private final TypeMirror source; private final TypeMirror target; private final TypeUtils typeUtils; + private final SelectionParameters selectionParameters; - public SubclassMappingOptions(TypeMirror source, TypeMirror target, TypeUtils typeUtils, DelegatingOptions next) { + public SubclassMappingOptions(TypeMirror source, TypeMirror target, TypeUtils typeUtils, DelegatingOptions next, + SelectionParameters selectionParameters) { super( next ); this.source = source; this.target = target; this.typeUtils = typeUtils; + this.selectionParameters = selectionParameters; } @Override @@ -117,6 +120,10 @@ public TypeMirror getTarget() { return target; } + public SelectionParameters getSelectionParameters() { + return selectionParameters; + } + public static void addInstances(SubclassMappingsGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, FormattingMessager messager, TypeUtils typeUtils, Set mappings, @@ -154,6 +161,12 @@ public static void addInstance(SubclassMappingGem subclassMapping, ExecutableEle TypeMirror sourceSubclass = subclassMapping.source().getValue(); TypeMirror targetSubclass = subclassMapping.target().getValue(); + SelectionParameters selectionParameters = new SelectionParameters( + subclassMapping.qualifiedBy().get(), + subclassMapping.qualifiedByName().get(), + targetSubclass, + typeUtils + ); mappings .add( @@ -161,20 +174,24 @@ public static void addInstance(SubclassMappingGem subclassMapping, ExecutableEle sourceSubclass, targetSubclass, typeUtils, - beanMappingOptions ) ); + beanMappingOptions, + selectionParameters + ) ); } public static List copyForInverseInheritance(Set subclassMappings, - BeanMappingOptions beanMappingOptions) { + BeanMappingOptions beanMappingOptions) { // we are not interested in keeping it unique at this point. List mappings = new ArrayList<>(); for ( SubclassMappingOptions subclassMapping : subclassMappings ) { mappings.add( - new SubclassMappingOptions( - subclassMapping.target, - subclassMapping.source, - subclassMapping.typeUtils, - beanMappingOptions ) ); + new SubclassMappingOptions( + subclassMapping.target, + subclassMapping.source, + subclassMapping.typeUtils, + beanMappingOptions, + subclassMapping.selectionParameters + ) ); } return mappings; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Composer.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Composer.java new file mode 100644 index 0000000000..116304ae5d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Composer.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.qualifier; + +public class Composer { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ComposerDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ComposerDto.java new file mode 100644 index 0000000000..1f9d27681f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ComposerDto.java @@ -0,0 +1,18 @@ +/* + * 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.subclassmapping.qualifier; + +public class ComposerDto { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByMapper.java new file mode 100644 index 0000000000..26cdd4715e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByMapper.java @@ -0,0 +1,16 @@ +/* + * 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.subclassmapping.qualifier; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassMapping; + +@Mapper(uses = { RossiniMapper.class, VivaldiMapper.class }) +public interface ErroneousSubclassQualifiedByMapper { + @SubclassMapping(source = Rossini.class, target = RossiniDto.class, qualifiedBy = NonExistent.class) + @SubclassMapping(source = Vivaldi.class, target = VivaldiDto.class) + ComposerDto toDto(Composer composer); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByNameMapper.java new file mode 100644 index 0000000000..23887baf0f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByNameMapper.java @@ -0,0 +1,16 @@ +/* + * 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.subclassmapping.qualifier; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassMapping; + +@Mapper(uses = { RossiniMapper.class, VivaldiMapper.class }) +public interface ErroneousSubclassQualifiedByNameMapper { + @SubclassMapping(source = Rossini.class, target = RossiniDto.class, qualifiedByName = "non-existent") + @SubclassMapping(source = Vivaldi.class, target = VivaldiDto.class) + ComposerDto toDto(Composer composer); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Light.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Light.java new file mode 100644 index 0000000000..0a24c561c9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Light.java @@ -0,0 +1,19 @@ +/* + * 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.subclassmapping.qualifier; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mapstruct.Qualifier; + +@Qualifier +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface Light { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/NonExistent.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/NonExistent.java new file mode 100644 index 0000000000..d1d74f76f7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/NonExistent.java @@ -0,0 +1,19 @@ +/* + * 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.subclassmapping.qualifier; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mapstruct.Qualifier; + +@Qualifier +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface NonExistent { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Rossini.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Rossini.java new file mode 100644 index 0000000000..cfd41847d0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Rossini.java @@ -0,0 +1,20 @@ +/* + * 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.subclassmapping.qualifier; + +import java.util.List; + +public class Rossini extends Composer { + private List crescendo; + + public List getCrescendo() { + return crescendo; + } + + public void setCrescendo(List crescendo) { + this.crescendo = crescendo; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/RossiniDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/RossiniDto.java new file mode 100644 index 0000000000..355de1876e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/RossiniDto.java @@ -0,0 +1,20 @@ +/* + * 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.subclassmapping.qualifier; + +import java.util.List; + +public class RossiniDto extends ComposerDto { + private List crescendo; + + public List getCrescendo() { + return crescendo; + } + + public void setCrescendo(List crescendo) { + this.crescendo = crescendo; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/RossiniMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/RossiniMapper.java new file mode 100644 index 0000000000..c4bb5e2497 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/RossiniMapper.java @@ -0,0 +1,50 @@ +/* + * 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.subclassmapping.qualifier; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +@Mapper +public interface RossiniMapper { + RossiniDto toDto(Rossini rossini); + + @Light + @Mapping(target = "crescendo", ignore = true) + RossiniDto toDtoLight(Rossini source); + + @Named("light") + @Mapping(target = "crescendo", ignore = true) + RossiniDto toDtoLightNamed(Rossini source); + + @Unused + @BeanMapping(ignoreByDefault = true) + RossiniDto toDtoUnused(Rossini source); + + @Named("unused") + @BeanMapping(ignoreByDefault = true) + RossiniDto toDtoUnusedNamed(Rossini source); + + Rossini fromDto(RossiniDto rossini); + + @Light + @Mapping(target = "crescendo", ignore = true) + Rossini fromDtoLight(RossiniDto source); + + @Named("light") + @Mapping(target = "crescendo", ignore = true) + Rossini fromDtoLightNamed(RossiniDto source); + + @Unused + @BeanMapping(ignoreByDefault = true) + Rossini fromDtoUnused(RossiniDto source); + + @Named("unused") + @BeanMapping(ignoreByDefault = true) + Rossini fromDtoUnusedNamed(RossiniDto source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifiedByMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifiedByMapper.java new file mode 100644 index 0000000000..7f486804fd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifiedByMapper.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.subclassmapping.qualifier; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper(uses = { RossiniMapper.class, VivaldiMapper.class }) +public interface SubclassQualifiedByMapper { + SubclassQualifiedByMapper INSTANCE = Mappers.getMapper( SubclassQualifiedByMapper.class ); + + @SubclassMapping(source = Rossini.class, target = RossiniDto.class) + @SubclassMapping(source = Vivaldi.class, target = VivaldiDto.class) + ComposerDto toDto(Composer composer); + + @SubclassMapping(source = Rossini.class, target = RossiniDto.class, qualifiedBy = Light.class) + @SubclassMapping(source = Vivaldi.class, target = VivaldiDto.class, qualifiedBy = Light.class) + ComposerDto toDtoLight(Composer composer); + + @SubclassMapping(source = Rossini.class, target = RossiniDto.class) + @SubclassMapping(source = Vivaldi.class, target = VivaldiDto.class, qualifiedBy = Light.class) + ComposerDto toDtoLightJustVivaldi(Composer composer); + + @InheritInverseConfiguration(name = "toDto") + Composer fromDto(ComposerDto composer); + + @InheritInverseConfiguration(name = "toDtoLight") + Composer fromDtoLight(ComposerDto composer); + + @InheritInverseConfiguration(name = "toDtoLightJustVivaldi") + Composer fromDtoLightJustVivaldi(ComposerDto composer); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifiedByNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifiedByNameMapper.java new file mode 100644 index 0000000000..e4816ef732 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifiedByNameMapper.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.subclassmapping.qualifier; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper(uses = { RossiniMapper.class, VivaldiMapper.class }) +public interface SubclassQualifiedByNameMapper { + SubclassQualifiedByNameMapper INSTANCE = Mappers.getMapper( SubclassQualifiedByNameMapper.class ); + + @SubclassMapping(source = Rossini.class, target = RossiniDto.class) + @SubclassMapping(source = Vivaldi.class, target = VivaldiDto.class) + ComposerDto toDto(Composer composer); + + @SubclassMapping(source = Rossini.class, target = RossiniDto.class, qualifiedByName = "light") + @SubclassMapping(source = Vivaldi.class, target = VivaldiDto.class, qualifiedByName = "light") + ComposerDto toDtoLight(Composer composer); + + @SubclassMapping(source = Rossini.class, target = RossiniDto.class) + @SubclassMapping(source = Vivaldi.class, target = VivaldiDto.class, qualifiedByName = "light") + ComposerDto toDtoLightJustVivaldi(Composer composer); + + @InheritInverseConfiguration(name = "toDto") + Composer fromDto(ComposerDto composer); + + @InheritInverseConfiguration(name = "toDtoLight") + Composer fromDtoLight(ComposerDto composer); + + @InheritInverseConfiguration(name = "toDtoLightJustVivaldi") + Composer fromDtoLightJustVivaldi(ComposerDto composer); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifierMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifierMapperTest.java new file mode 100644 index 0000000000..df67c27013 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifierMapperTest.java @@ -0,0 +1,297 @@ +/* + * 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.subclassmapping.qualifier; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +@WithClasses({ + Composer.class, + ComposerDto.class, + Rossini.class, + RossiniDto.class, + Vivaldi.class, + VivaldiDto.class, + VivaldiDto.class, + VivaldiDto.class, + Light.class, + Unused.class, + NonExistent.class, + RossiniMapper.class, + VivaldiMapper.class +}) +@IssueKey("3119") +public class SubclassQualifierMapperTest { + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses(SubclassQualifiedByMapper.class) + void subclassQualifiedBy() { + Rossini rossini = buildRossini(); + + Vivaldi vivaldi = buildVivaldi(); + + RossiniDto rossiniDto = (RossiniDto) SubclassQualifiedByMapper.INSTANCE.toDto( rossini ); + RossiniDto rossiniDtoLight = (RossiniDto) SubclassQualifiedByMapper.INSTANCE.toDtoLight( rossini ); + + VivaldiDto vivaldiDto = (VivaldiDto) SubclassQualifiedByMapper.INSTANCE.toDto( vivaldi ); + VivaldiDto vivaldiDtoLight = (VivaldiDto) SubclassQualifiedByMapper.INSTANCE.toDtoLight( vivaldi ); + + assertThat( rossiniDto.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossiniDto.getCrescendo() ).containsExactly( "andante", "allegro", "vivace" ); + assertThat( rossiniDtoLight.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossiniDtoLight.getCrescendo() ).isNull(); + + assertThat( vivaldiDto.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldiDto.getSeasons() ).containsExactly( "spring", "winter" ); + assertThat( vivaldiDtoLight.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldiDtoLight.getSeasons() ).isNull(); + } + + @ProcessorTest + @WithClasses(SubclassQualifiedByMapper.class) + void subclassQualifiedByOnlyOne() { + Rossini rossini = buildRossini(); + + Vivaldi vivaldi = buildVivaldi(); + + RossiniDto rossiniDto = (RossiniDto) SubclassQualifiedByMapper.INSTANCE.toDtoLightJustVivaldi( rossini ); + + VivaldiDto vivaldiDto = (VivaldiDto) SubclassQualifiedByMapper.INSTANCE.toDtoLightJustVivaldi( vivaldi ); + + assertThat( rossiniDto.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossiniDto.getCrescendo() ).containsExactly( "andante", "allegro", "vivace" ); + + assertThat( vivaldiDto.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldiDto.getSeasons() ).isNull(); + } + + @ProcessorTest + @WithClasses(SubclassQualifiedByNameMapper.class) + void subclassQualifiedByName() { + Rossini rossini = buildRossini(); + + Vivaldi vivaldi = buildVivaldi(); + + RossiniDto rossiniDto = (RossiniDto) SubclassQualifiedByNameMapper.INSTANCE.toDto( rossini ); + RossiniDto rossiniDtoLight = (RossiniDto) SubclassQualifiedByNameMapper.INSTANCE.toDtoLight( rossini ); + + VivaldiDto vivaldiDto = (VivaldiDto) SubclassQualifiedByNameMapper.INSTANCE.toDto( vivaldi ); + VivaldiDto vivaldiDtoLight = (VivaldiDto) SubclassQualifiedByNameMapper.INSTANCE.toDtoLight( vivaldi ); + + assertThat( rossiniDto.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossiniDto.getCrescendo() ).containsExactly( "andante", "allegro", "vivace" ); + assertThat( rossiniDtoLight.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossiniDtoLight.getCrescendo() ).isNull(); + + assertThat( vivaldiDto.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldiDto.getSeasons() ).containsExactly( "spring", "winter" ); + assertThat( vivaldiDtoLight.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldiDtoLight.getSeasons() ).isNull(); + } + + @ProcessorTest + @WithClasses(SubclassQualifiedByNameMapper.class) + void subclassQualifiedByNameOnlyOne() { + Rossini rossini = buildRossini(); + + Vivaldi vivaldi = buildVivaldi(); + + RossiniDto rossiniDto = (RossiniDto) SubclassQualifiedByNameMapper.INSTANCE.toDtoLightJustVivaldi( rossini ); + + VivaldiDto vivaldiDto = (VivaldiDto) SubclassQualifiedByNameMapper.INSTANCE.toDtoLightJustVivaldi( vivaldi ); + + assertThat( rossiniDto.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossiniDto.getCrescendo() ).containsExactly( "andante", "allegro", "vivace" ); + + assertThat( vivaldiDto.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldiDto.getSeasons() ).isNull(); + } + + @ProcessorTest + @WithClasses(SubclassQualifiedByMapper.class) + void subclassQualifiedByInverse() { + RossiniDto rossiniDto = buildRossiniDto(); + + VivaldiDto vivaldiDto = buildVivaldiDto(); + + Rossini rossini = (Rossini) SubclassQualifiedByMapper.INSTANCE.fromDto( rossiniDto ); + Rossini rossiniLight = (Rossini) SubclassQualifiedByMapper.INSTANCE.fromDtoLight( rossiniDto ); + + Vivaldi vivaldi = (Vivaldi) SubclassQualifiedByMapper.INSTANCE.fromDto( vivaldiDto ); + Vivaldi vivaldiLight = (Vivaldi) SubclassQualifiedByMapper.INSTANCE.fromDtoLight( vivaldiDto ); + + assertThat( rossini.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossini.getCrescendo() ).containsExactly( "andante", "allegro", "vivace" ); + assertThat( rossiniLight.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossiniLight.getCrescendo() ).isNull(); + + assertThat( vivaldi.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldi.getSeasons() ).containsExactly( "spring", "winter" ); + assertThat( vivaldiLight.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldiLight.getSeasons() ).isNull(); + } + + @ProcessorTest + @WithClasses(SubclassQualifiedByMapper.class) + void subclassQualifiedByOnlyOneInverse() { + RossiniDto rossiniDto = buildRossiniDto(); + + VivaldiDto vivaldiDto = buildVivaldiDto(); + + Rossini rossini = (Rossini) SubclassQualifiedByMapper.INSTANCE.fromDtoLightJustVivaldi( rossiniDto ); + + Vivaldi vivaldi = (Vivaldi) SubclassQualifiedByMapper.INSTANCE.fromDtoLightJustVivaldi( vivaldiDto ); + + assertThat( rossini.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossini.getCrescendo() ).containsExactly( "andante", "allegro", "vivace" ); + + assertThat( vivaldi.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldi.getSeasons() ).isNull(); + } + + @ProcessorTest + @WithClasses(SubclassQualifiedByNameMapper.class) + void subclassQualifiedByNameInverse() { + RossiniDto rossiniDto = buildRossiniDto(); + + VivaldiDto vivaldiDto = buildVivaldiDto(); + + Rossini rossini = (Rossini) SubclassQualifiedByNameMapper.INSTANCE.fromDto( rossiniDto ); + Rossini rossiniLight = (Rossini) SubclassQualifiedByNameMapper.INSTANCE.fromDtoLight( rossiniDto ); + + Vivaldi vivaldi = (Vivaldi) SubclassQualifiedByNameMapper.INSTANCE.fromDto( vivaldiDto ); + Vivaldi vivaldiLight = (Vivaldi) SubclassQualifiedByNameMapper.INSTANCE.fromDtoLight( vivaldiDto ); + + assertThat( rossini.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossini.getCrescendo() ).containsExactly( "andante", "allegro", "vivace" ); + assertThat( rossiniLight.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossiniLight.getCrescendo() ).isNull(); + + assertThat( vivaldi.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldi.getSeasons() ).containsExactly( "spring", "winter" ); + assertThat( vivaldiLight.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldiLight.getSeasons() ).isNull(); + } + + @ProcessorTest + @WithClasses(SubclassQualifiedByNameMapper.class) + void subclassQualifiedByNameOnlyOneInverse() { + RossiniDto rossiniDto = buildRossiniDto(); + + VivaldiDto vivaldiDto = buildVivaldiDto(); + + Rossini rossini = (Rossini) SubclassQualifiedByNameMapper.INSTANCE.fromDtoLightJustVivaldi( rossiniDto ); + + Vivaldi vivaldi = (Vivaldi) SubclassQualifiedByNameMapper.INSTANCE.fromDtoLightJustVivaldi( vivaldiDto ); + + assertThat( rossini.getName() ).isEqualTo( "gioacchino" ); + assertThat( rossini.getCrescendo() ).containsExactly( "andante", "allegro", "vivace" ); + + assertThat( vivaldi.getName() ).isEqualTo( "antonio" ); + assertThat( vivaldi.getSeasons() ).isNull(); + } + + @ProcessorTest + @WithClasses(SubclassQualifiedByMapper.class) + void subclassQualifiedByDoesNotContainUnused() { + generatedSource.forMapper( SubclassQualifiedByMapper.class ) + .content() + .doesNotContain( "DtoUnused" ); + } + + @ProcessorTest + @WithClasses(SubclassQualifiedByNameMapper.class) + void subclassQualifiedByNameDoesNotContainUnused() { + generatedSource.forMapper( SubclassQualifiedByNameMapper.class ) + .content() + .doesNotContain( "DtoUnused" ); + } + + @ProcessorTest + @WithClasses(ErroneousSubclassQualifiedByMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = @Diagnostic( + type = ErroneousSubclassQualifiedByMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 15, + message = "Qualifier error. No method found annotated with: [ @NonExistent ]. " + + "See https://mapstruct.org/faq/#qualifier for more info." + ) + ) + void subclassQualifiedByErroneous() { + } + + @ProcessorTest + @WithClasses(ErroneousSubclassQualifiedByNameMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = @Diagnostic( + type = ErroneousSubclassQualifiedByNameMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 15, + message = "Qualifier error. No method found annotated with @Named#value: [ non-existent ]. " + + "See https://mapstruct.org/faq/#qualifier for more info." + ) + ) + void subclassQualifiedByNameErroneous() { + } + + private Rossini buildRossini() { + Rossini rossini = new Rossini(); + rossini.setName( "gioacchino" ); + List crescendo = new ArrayList<>(); + crescendo.add( "andante" ); + crescendo.add( "allegro" ); + crescendo.add( "vivace" ); + rossini.setCrescendo( crescendo ); + return rossini; + } + + private Vivaldi buildVivaldi() { + Vivaldi vivaldi = new Vivaldi(); + vivaldi.setName( "antonio" ); + List season = new ArrayList<>(); + season.add( "spring" ); + season.add( "winter" ); + vivaldi.setSeasons( season ); + return vivaldi; + } + + private RossiniDto buildRossiniDto() { + RossiniDto rossiniDto = new RossiniDto(); + rossiniDto.setName( "gioacchino" ); + List crescendo = new ArrayList<>(); + crescendo.add( "andante" ); + crescendo.add( "allegro" ); + crescendo.add( "vivace" ); + rossiniDto.setCrescendo( crescendo ); + return rossiniDto; + } + + private VivaldiDto buildVivaldiDto() { + VivaldiDto vivaldiDto = new VivaldiDto(); + vivaldiDto.setName( "antonio" ); + List season = new ArrayList<>(); + season.add( "spring" ); + season.add( "winter" ); + vivaldiDto.setSeasons( season ); + return vivaldiDto; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Unused.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Unused.java new file mode 100644 index 0000000000..10da0434e2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Unused.java @@ -0,0 +1,19 @@ +/* + * 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.subclassmapping.qualifier; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mapstruct.Qualifier; + +@Qualifier +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface Unused { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Vivaldi.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Vivaldi.java new file mode 100644 index 0000000000..75df3d66f7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/Vivaldi.java @@ -0,0 +1,20 @@ +/* + * 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.subclassmapping.qualifier; + +import java.util.List; + +public class Vivaldi extends Composer { + private List seasons; + + public List getSeasons() { + return seasons; + } + + public void setSeasons(List seasons) { + this.seasons = seasons; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/VivaldiDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/VivaldiDto.java new file mode 100644 index 0000000000..79cd5fef33 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/VivaldiDto.java @@ -0,0 +1,20 @@ +/* + * 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.subclassmapping.qualifier; + +import java.util.List; + +public class VivaldiDto extends ComposerDto { + private List seasons; + + public List getSeasons() { + return seasons; + } + + public void setSeasons(List seasons) { + this.seasons = seasons; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/VivaldiMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/VivaldiMapper.java new file mode 100644 index 0000000000..2b3b1fa3e7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/VivaldiMapper.java @@ -0,0 +1,50 @@ +/* + * 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.subclassmapping.qualifier; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +@Mapper +public interface VivaldiMapper { + VivaldiDto toDto(Vivaldi vivaldi); + + @Light + @Mapping(target = "seasons", ignore = true) + VivaldiDto toDtoLight(Vivaldi vivaldi); + + @Named("light") + @Mapping(target = "seasons", ignore = true) + VivaldiDto toDtoLightNamed(Vivaldi vivaldi); + + @Unused + @BeanMapping(ignoreByDefault = true) + VivaldiDto toDtoUnused(Vivaldi vivaldi); + + @Named("unused") + @BeanMapping(ignoreByDefault = true) + VivaldiDto toDtoUnusedNamed(Vivaldi vivaldi); + + Vivaldi fromDto(VivaldiDto vivaldi); + + @Light + @Mapping(target = "seasons", ignore = true) + Vivaldi fromDtoLight(VivaldiDto vivaldi); + + @Named("light") + @Mapping(target = "seasons", ignore = true) + Vivaldi fromDtoLightNamed(VivaldiDto vivaldi); + + @Unused + @BeanMapping(ignoreByDefault = true) + Vivaldi fromDtoUnused(VivaldiDto vivaldi); + + @Named("unused") + @BeanMapping(ignoreByDefault = true) + Vivaldi fromDtoUnusedNamed(VivaldiDto vivaldi); +} From 62c7ce1cdf0ccc5528dca84decaf07cbecadaed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Campanero=20Ortiz?= Date: Thu, 13 Apr 2023 21:48:13 +0200 Subject: [PATCH 331/597] #3112 Document in the reference guide --- .../chapter-8-mapping-values.asciidoc | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index ce16b33ecd..b9cb308d4f 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -65,7 +65,7 @@ public class OrderMapperImpl implements OrderMapper { ---- ==== By default an error will be raised by MapStruct in case a constant of the source enum type does not have a corresponding constant with the same name in the target type and also is not mapped to another constant via `@ValueMapping`. This ensures that all constants are mapped in a safe and predictable manner. The generated -mapping method will throw an IllegalStateException if for some reason an unrecognized source value occurs. +mapping method will throw an `IllegalStateException` if for some reason an unrecognized source value occurs. MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings and only applies to the source. It comes in two flavors: `` and ``. They cannot be used at the same time. @@ -75,14 +75,18 @@ MapStruct will *not* attempt such name based mapping for `` and di MapStruct is able to handle `null` sources and `null` targets by means of the `` keyword. +In addition, the constant value `` can be used for throwing an exception for particular value mappings. This value is only applicable to `ValueMapping#target()` and not `ValueMapping#source()` since MapStruct can't map from exceptions. + [TIP] ==== -Constants for ``, `` and `` are available in the `MappingConstants` class. +Constants for ``, ``, `` and `` are available in the `MappingConstants` class. ==== Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. `` and `` will be ignored in that case. -.Enum mapping method, and +The following code snippets exemplify the use of the aforementioned constants. + +.Enum mapping method, `` and `` ==== [source, java, linenums] [subs="verbatim,attributes"] @@ -102,7 +106,7 @@ public interface SpecialOrderMapper { ---- ==== -.Enum mapping method result, and +.Enum mapping method result, `` and `` ==== [source, java, linenums] [subs="verbatim,attributes"] @@ -137,6 +141,55 @@ public class SpecialOrderMapperImpl implements SpecialOrderMapper { *Note:* MapStruct would have refrained from mapping the `RETAIL` and `B2B` when `` was used instead of ``. +.Enum mapping method with `` +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface SpecialOrderMapper { + + SpecialOrderMapper INSTANCE = Mappers.getMapper( SpecialOrderMapper.class ); + + @ValueMappings({ + @ValueMapping( source = "STANDARD", target = "DEFAULT" ), + @ValueMapping( source = "C2C", target = MappingConstants.THROW_EXCEPTION ) + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); +} +---- +==== + +.Enum mapping method with `` result +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class SpecialOrderMapperImpl implements SpecialOrderMapper { + + @Override + public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType; + + switch ( orderType ) { + case STANDARD: externalOrderType = ExternalOrderType.DEFAULT; + break; + case C2C: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType ); + default: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return externalOrderType; + } +} +---- +==== + + [WARNING] ==== The mapping of enum to enum via the `@Mapping` annotation is *DEPRECATED*. It will be removed from future versions of MapStruct. Please adapt existing enum mapping methods to make use of `@ValueMapping` instead. @@ -152,6 +205,7 @@ MapStruct supports enum to a String mapping along the same lines as is described 2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. 3. Difference: `` will result in an error. It acts on the premise that there is name similarity between enum constants in source and target which does not make sense for a String type. 4. Difference: Given 1. and 3. there will never be unmapped values. +5. Similarity: `THROW_EXCEPTION` can be used for throwing an exception for particular enum values. *`String` to enum* @@ -159,6 +213,7 @@ MapStruct supports enum to a String mapping along the same lines as is described 2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. 3. Similarity: `` will create a mapping for each target enum constant and proceed to the switch/default clause value. 4. Difference: A switch/default value needs to be provided to have a determined outcome (enum has a limited set of values, `String` has unlimited options). Failing to specify `` or ` will result in a warning. +5. Similarity: `THROW_EXCEPTION` can be used for throwing an exception for any arbitrary `String` value. === Custom name transformation From 2be1f306a1179b437e850f8ac99ceb492373dced Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 17 Mar 2023 11:23:53 +0100 Subject: [PATCH 332/597] #3135 BeanMapping#mappingControl should be inherited by forged methods --- .../model/source/BeanMappingOptions.java | 11 ++++++++++ .../model/source/MappingMethodOptions.java | 2 +- .../model/source/SelectionParameters.java | 4 ++++ .../CloningBeanMappingMapper.java | 21 +++++++++++++++++++ .../mappingcontrol/MappingControlTest.java | 15 +++++++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningBeanMappingMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index 90ad0aabb4..24811768e7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -54,6 +54,17 @@ public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) return options; } + public static BeanMappingOptions forForgedMethods(BeanMappingOptions beanMapping) { + BeanMappingOptions options = new BeanMappingOptions( + beanMapping.selectionParameters != null ? + SelectionParameters.withoutResultType( beanMapping.selectionParameters ) : null, + Collections.emptyList(), + beanMapping.beanMapping, + beanMapping + ); + return options; + } + public static BeanMappingOptions empty(DelegatingOptions delegatingOptions) { return new BeanMappingOptions( null, Collections.emptyList(), null, delegatingOptions ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index a1a64872a9..9f5da7b8de 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -365,7 +365,7 @@ public static MappingMethodOptions getForgedMethodInheritedOptions(MappingMethod options.mappings, options.iterableMapping, options.mapMapping, - BeanMappingOptions.empty( options.beanMapping.next() ), + BeanMappingOptions.forForgedMethods( options.beanMapping ), options.enumMappingOptions, options.valueMappings, Collections.emptySet(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java index fb76c13629..7237668e2c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java @@ -39,6 +39,10 @@ public class SelectionParameters { * @return the selection parameters based on the given ones */ public static SelectionParameters forInheritance(SelectionParameters selectionParameters) { + return withoutResultType( selectionParameters ); + } + + public static SelectionParameters withoutResultType(SelectionParameters selectionParameters) { return new SelectionParameters( selectionParameters.qualifiers, selectionParameters.qualifyingNames, diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningBeanMappingMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningBeanMappingMapper.java new file mode 100644 index 0000000000..a5c05d974d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/CloningBeanMappingMapper.java @@ -0,0 +1,21 @@ +/* + * 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.mappingcontrol; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.control.DeepClone; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface CloningBeanMappingMapper { + + CloningBeanMappingMapper INSTANCE = Mappers.getMapper( CloningBeanMappingMapper.class ); + + @BeanMapping(mappingControl = DeepClone.class) + FridgeDTO clone(FridgeDTO in); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java index d542801799..c9c2e095f5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java @@ -69,6 +69,21 @@ public void testDeepCloning() { assertThat( out.getShelve().getCoolBeer().getBeerCount() ).isEqualTo( "5" ); } + @ProcessorTest + @IssueKey("3135") + @WithClasses(CloningBeanMappingMapper.class) + public void testDeepCloningViaBeanMapping() { + + FridgeDTO in = createFridgeDTO(); + FridgeDTO out = CloningBeanMappingMapper.INSTANCE.clone( in ); + + assertThat( out ).isNotNull(); + assertThat( out.getShelve() ).isNotNull(); + assertThat( out.getShelve() ).isNotSameAs( in.getShelve() ); + assertThat( out.getShelve().getCoolBeer() ).isNotSameAs( in.getShelve().getCoolBeer() ); + assertThat( out.getShelve().getCoolBeer().getBeerCount() ).isEqualTo( "5" ); + } + /** * Test the deep cloning annotation with lists */ From 6e9fa87ba99bb8637ae7f525636610fc77ad6e22 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 13 Apr 2023 21:52:15 +0200 Subject: [PATCH 333/597] #3142 Nested forged methods should declare throws from lifecycle methods --- .../ap/internal/model/BeanMappingMethod.java | 8 ++++ .../test/bugs/_3142/Issue3142Exception.java | 16 +++++++ .../ap/test/bugs/_3142/Issue3142Test.java | 48 +++++++++++++++++++ ...ue3142WithAfterMappingExceptionMapper.java | 32 +++++++++++++ ...e3142WithBeforeMappingExceptionMapper.java | 32 +++++++++++++ .../mapstruct/ap/test/bugs/_3142/Source.java | 33 +++++++++++++ .../mapstruct/ap/test/bugs/_3142/Target.java | 42 ++++++++++++++++ 7 files changed, 211 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142Exception.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142WithAfterMappingExceptionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142WithBeforeMappingExceptionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Target.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 29d27c8d68..8942c62789 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -340,6 +340,14 @@ else if ( !method.isUpdateMethod() ) { if ( factoryMethod != null ) { forgedMethod.addThrownTypes( factoryMethod.getThrownTypes() ); } + for ( LifecycleCallbackMethodReference beforeMappingMethod : beforeMappingMethods ) { + forgedMethod.addThrownTypes( beforeMappingMethod.getThrownTypes() ); + } + + for ( LifecycleCallbackMethodReference afterMappingMethod : afterMappingMethods ) { + forgedMethod.addThrownTypes( afterMappingMethod.getThrownTypes() ); + } + for ( PropertyMapping propertyMapping : propertyMappings ) { if ( propertyMapping.getAssignment() != null ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142Exception.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142Exception.java new file mode 100644 index 0000000000..54ed903ee7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142Exception.java @@ -0,0 +1,16 @@ +/* + * 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._3142; + +/** + * @author Filip Hrisafov + */ +public class Issue3142Exception extends Exception { + + public Issue3142Exception(String message) { + super( message ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142Test.java new file mode 100644 index 0000000000..a8b44874bb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142Test.java @@ -0,0 +1,48 @@ +/* + * 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._3142; + +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.assertThatThrownBy; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + Issue3142Exception.class, + Source.class, + Target.class, +}) +@IssueKey("3142") +class Issue3142Test { + + @ProcessorTest + @WithClasses({ + Issue3142WithBeforeMappingExceptionMapper.class + }) + void exceptionThrownInBeforeMapping() { + assertThatThrownBy( () -> Issue3142WithBeforeMappingExceptionMapper.INSTANCE.map( + new Source( new Source.Nested( "throwException" ) ), "test" ) + ) + .isInstanceOf( Issue3142Exception.class ) + .hasMessage( "Source nested exception" ); + } + + @ProcessorTest + @WithClasses({ + Issue3142WithAfterMappingExceptionMapper.class + }) + void exceptionThrownInAfterMapping() { + assertThatThrownBy( () -> Issue3142WithAfterMappingExceptionMapper.INSTANCE.map( + new Source( new Source.Nested( "throwException" ) ), "test" ) + ) + .isInstanceOf( Issue3142Exception.class ) + .hasMessage( "Source nested exception" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142WithAfterMappingExceptionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142WithAfterMappingExceptionMapper.java new file mode 100644 index 0000000000..e188626424 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142WithAfterMappingExceptionMapper.java @@ -0,0 +1,32 @@ +/* + * 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._3142; + +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3142WithAfterMappingExceptionMapper { + + Issue3142WithAfterMappingExceptionMapper INSTANCE = + Mappers.getMapper( Issue3142WithAfterMappingExceptionMapper.class ); + + Target map(Source source, String id) throws Issue3142Exception; + + @AfterMapping + default void afterMappingValidation(Object source) throws Issue3142Exception { + if ( source instanceof Source.Nested ) { + Source.Nested nested = (Source.Nested) source; + if ( "throwException".equals( nested.getValue() ) ) { + throw new Issue3142Exception( "Source nested exception" ); + } + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142WithBeforeMappingExceptionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142WithBeforeMappingExceptionMapper.java new file mode 100644 index 0000000000..ee27d87b7f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Issue3142WithBeforeMappingExceptionMapper.java @@ -0,0 +1,32 @@ +/* + * 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._3142; + +import org.mapstruct.BeforeMapping; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3142WithBeforeMappingExceptionMapper { + + Issue3142WithBeforeMappingExceptionMapper INSTANCE = + Mappers.getMapper( Issue3142WithBeforeMappingExceptionMapper.class ); + + Target map(Source source, String id) throws Issue3142Exception; + + @BeforeMapping + default void preMappingValidation(Object source) throws Issue3142Exception { + if ( source instanceof Source.Nested ) { + Source.Nested nested = (Source.Nested) source; + if ( "throwException".equals( nested.getValue() ) ) { + throw new Issue3142Exception( "Source nested exception" ); + } + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Source.java new file mode 100644 index 0000000000..2b78ffa022 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Source.java @@ -0,0 +1,33 @@ +/* + * 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._3142; + +/** + * @author Filip Hrisafov + */ +public class Source { + private final Nested nested; + + public Source(Nested nested) { + this.nested = nested; + } + + public Nested getNested() { + return nested; + } + + public static class Nested { + private final String value; + + public Nested(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Target.java new file mode 100644 index 0000000000..6f832b6ecf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3142/Target.java @@ -0,0 +1,42 @@ +/* + * 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._3142; + +/** + * @author Filip Hrisafov + */ +public class Target { + private String id; + private Nested nested; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Nested getNested() { + return nested; + } + + public void setNested(Nested nested) { + this.nested = nested; + } + + public static class Nested { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} From 03563d8ffea0ca5ff4eadb297795f2407001c5dd Mon Sep 17 00:00:00 2001 From: Ben Zegveld Date: Sat, 25 Feb 2023 21:29:23 +0100 Subject: [PATCH 334/597] #3174 Also allow multiple SubclassMapping annotations on an annotation @interface. --- .../java/org/mapstruct/SubclassMappings.java | 2 +- .../CompositeSubclassMappingAnnotation.java | 19 ++++++++ .../SubclassCompositeMapper.java | 29 ++++++++++++ .../subclassmapping/SubclassMappingTest.java | 45 +++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/CompositeSubclassMappingAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassCompositeMapper.java diff --git a/core/src/main/java/org/mapstruct/SubclassMappings.java b/core/src/main/java/org/mapstruct/SubclassMappings.java index 938b836f77..2ddafaf516 100644 --- a/core/src/main/java/org/mapstruct/SubclassMappings.java +++ b/core/src/main/java/org/mapstruct/SubclassMappings.java @@ -45,7 +45,7 @@ * @author Ben Zegveld * @since 1.5 */ -@Target(ElementType.METHOD) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.CLASS) @Experimental public @interface SubclassMappings { diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/CompositeSubclassMappingAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/CompositeSubclassMappingAnnotation.java new file mode 100644 index 0000000000..a121c08799 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/CompositeSubclassMappingAnnotation.java @@ -0,0 +1,19 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; + +@SubclassMapping( source = Car.class, target = CarDto.class ) +@SubclassMapping( source = Bike.class, target = BikeDto.class ) +@Mapping( source = "vehicleManufacturingCompany", target = "maker") +public @interface CompositeSubclassMappingAnnotation { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassCompositeMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassCompositeMapper.java new file mode 100644 index 0000000000..c660cd2390 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassCompositeMapper.java @@ -0,0 +1,29 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollection; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleCollectionDto; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SubclassCompositeMapper { + SubclassCompositeMapper INSTANCE = Mappers.getMapper( SubclassCompositeMapper.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + @CompositeSubclassMappingAnnotation + VehicleDto map(Vehicle vehicle); + + VehicleCollection mapInverse(VehicleCollectionDto vehicles); + + @InheritInverseConfiguration + Vehicle mapInverse(VehicleDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java index 6f9b6e160c..5a5ce1ef99 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java @@ -143,6 +143,51 @@ void subclassMappingOverridesInverseInheritsMapping() { .containsExactly( Car.class ); } + @ProcessorTest + @WithClasses( { SubclassCompositeMapper.class, CompositeSubclassMappingAnnotation.class }) + void mappingIsDoneUsingSubclassMappingWithCompositeMapping() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Car() ); + vehicles.getVehicles().add( new Bike() ); + + VehicleCollectionDto result = SubclassCompositeMapper.INSTANCE.map( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( CarDto.class, BikeDto.class ); + } + + @ProcessorTest + @WithClasses( { SubclassCompositeMapper.class, CompositeSubclassMappingAnnotation.class }) + void inverseMappingIsDoneUsingSubclassMappingWithCompositeMapping() { + VehicleCollectionDto vehicles = new VehicleCollectionDto(); + vehicles.getVehicles().add( new CarDto() ); + vehicles.getVehicles().add( new BikeDto() ); + + VehicleCollection result = SubclassCompositeMapper.INSTANCE.mapInverse( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( Car.class, Bike.class ); + } + + @ProcessorTest + @WithClasses( { SubclassCompositeMapper.class, CompositeSubclassMappingAnnotation.class }) + void subclassMappingInheritsInverseMappingWithCompositeMapping() { + VehicleCollectionDto vehiclesDto = new VehicleCollectionDto(); + CarDto carDto = new CarDto(); + carDto.setMaker( "BenZ" ); + vehiclesDto.getVehicles().add( carDto ); + + VehicleCollection result = SubclassCompositeMapper.INSTANCE.mapInverse( vehiclesDto ); + + assertThat( result.getVehicles() ) + .extracting( Vehicle::getVehicleManufacturingCompany ) + .containsExactly( "BenZ" ); + } + @ProcessorTest @WithClasses({ HatchBack.class, From 162fdb44f402e47d549778d57f369db144ce5e0d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 16 Apr 2023 10:17:32 +0200 Subject: [PATCH 335/597] #3195 Update the location for asking questions --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 58d1c515bb..4807b5fba0 100644 --- a/readme.md +++ b/readme.md @@ -126,7 +126,7 @@ If you don't work with a dependency management tool, you can obtain a distributi ## Documentation and getting help -To learn more about MapStruct, refer to the [project homepage](http://mapstruct.org). The [reference documentation](http://mapstruct.org/documentation/reference-guide/) covers all provided functionality in detail. If you need help, come and join the [mapstruct-users](https://groups.google.com/forum/?hl=en#!forum/mapstruct-users) group. +To learn more about MapStruct, refer to the [project homepage](http://mapstruct.org). The [reference documentation](http://mapstruct.org/documentation/reference-guide/) covers all provided functionality in detail. If you need help please ask it in the [Discussions](https://github.com/mapstruct/mapstruct/discussions). ## Building from Source From 3c81d36810caf052f1d8bc73e30d9e3a8c8622f4 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Mon, 17 Apr 2023 04:51:33 +0900 Subject: [PATCH 336/597] Polish links in docs (#3214) --- core/src/main/java/org/mapstruct/Mapper.java | 2 +- core/src/main/java/org/mapstruct/MapperConfig.java | 2 +- core/src/main/java/org/mapstruct/package-info.java | 2 +- distribution/pom.xml | 2 +- parent/pom.xml | 2 +- readme.md | 14 +++++++------- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 0436c0cf69..60725cc441 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -298,7 +298,7 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default * Can be configured by the {@link MapperConfig#disableSubMappingMethodsGeneration()} as well. *

      * Note: If you need to use {@code disableSubMappingMethodsGeneration} please contact the MapStruct team at - * mapstruct.org or + * mapstruct.org or * github.com/mapstruct/mapstruct to share what problem you * are facing with the automatic sub-mapping generation. * diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java index 893801cdaa..915f3dd120 100644 --- a/core/src/main/java/org/mapstruct/MapperConfig.java +++ b/core/src/main/java/org/mapstruct/MapperConfig.java @@ -269,7 +269,7 @@ MappingInheritanceStrategy mappingInheritanceStrategy() * Can be overridden by {@link Mapper#disableSubMappingMethodsGeneration()} *

      * Note: If you need to use {@code disableSubMappingMethodsGeneration} please contact the MapStruct team at - * mapstruct.org or + * mapstruct.org or * github.com/mapstruct/mapstruct to share what problem you * are facing with the automatic sub-mapping generation. * diff --git a/core/src/main/java/org/mapstruct/package-info.java b/core/src/main/java/org/mapstruct/package-info.java index 02f2b31a42..5a53b5a6d3 100644 --- a/core/src/main/java/org/mapstruct/package-info.java +++ b/core/src/main/java/org/mapstruct/package-info.java @@ -13,6 +13,6 @@ * This package contains several annotations which allow to configure how mapper interfaces are generated. *

      * - * @see MapStruct reference documentation + * @see MapStruct reference documentation */ package org.mapstruct; diff --git a/distribution/pom.xml b/distribution/pom.xml index be82f52b3c..bcfa1f3db0 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -100,7 +100,7 @@ MapStruct ${project.version} MapStruct ${project.version} - MapStruct Authors; All rights reserved. Released under the Apache Software License 2.0.]]> + MapStruct Authors; All rights reserved. Released under the Apache Software License 2.0.]]> diff --git a/parent/pom.xml b/parent/pom.xml index e87db14551..b306446b65 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -16,7 +16,7 @@ MapStruct Parent An annotation processor for generating type-safe bean mappers - http://mapstruct.org/ + https://mapstruct.org/ 2012 diff --git a/readme.md b/readme.md index 4807b5fba0..4e0c2f6ad4 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.3.Final-blue.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct%20AND%20v%3A1.*.Final) -[![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.mapstruct) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.3.Final-blue.svg)](https://search.maven.org/search?q=g:org.mapstruct%20AND%20v:1.*.Final) +[![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](https://search.maven.org/search?q=g:org.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) [![Build Status](https://github.com/mapstruct/mapstruct/workflows/CI/badge.svg?branch=master)](https://github.com/mapstruct/mapstruct/actions?query=branch%3Amaster+workflow%3ACI) @@ -22,7 +22,7 @@ ## What is MapStruct? -MapStruct is a Java [annotation processor](http://docs.oracle.com/javase/6/docs/technotes/guides/apt/index.html) for the generation of type-safe and performant mappers for Java bean classes. It saves you from writing mapping code by hand, which is a tedious and error-prone task. The generator comes with sensible defaults and many built-in type conversions, but it steps out of your way when it comes to configuring or implementing special behavior. +MapStruct is a Java [annotation processor](https://docs.oracle.com/javase/6/docs/technotes/guides/apt/index.html) for the generation of type-safe and performant mappers for Java bean classes. It saves you from writing mapping code by hand, which is a tedious and error-prone task. The generator comes with sensible defaults and many built-in type conversions, but it steps out of your way when it comes to configuring or implementing special behavior. Compared to mapping frameworks working at runtime, MapStruct offers the following advantages: @@ -126,7 +126,7 @@ If you don't work with a dependency management tool, you can obtain a distributi ## Documentation and getting help -To learn more about MapStruct, refer to the [project homepage](http://mapstruct.org). The [reference documentation](http://mapstruct.org/documentation/reference-guide/) covers all provided functionality in detail. If you need help please ask it in the [Discussions](https://github.com/mapstruct/mapstruct/discussions). +To learn more about MapStruct, refer to the [project homepage](https://mapstruct.org). The [reference documentation](https://mapstruct.org/documentation/reference-guide/) covers all provided functionality in detail. If you need help please ask it in the [Discussions](https://github.com/mapstruct/mapstruct/discussions). ## Building from Source @@ -154,13 +154,13 @@ Make sure that you have the [m2e_apt](https://marketplace.eclipse.org/content/m2 ## Links -* [Homepage](http://mapstruct.org) +* [Homepage](https://mapstruct.org) * [Source code](https://github.com/mapstruct/mapstruct/) -* [Downloads](https://sourceforge.net/projects/mapstruct/files/) +* [Downloads](https://github.com/mapstruct/mapstruct/releases) * [Issue tracker](https://github.com/mapstruct/mapstruct/issues) * [User group](https://groups.google.com/forum/?hl=en#!forum/mapstruct-users) * [CI build](https://github.com/mapstruct/mapstruct/actions/) ## Licensing -MapStruct is licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. +MapStruct is licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0. From 00f891be5834da6d81db330cde7ddaab6abe6bf8 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 22 Apr 2023 17:14:46 +0200 Subject: [PATCH 337/597] #3112 Add missing brackets --- .../src/main/asciidoc/chapter-8-mapping-values.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index b9cb308d4f..965479541e 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -205,7 +205,7 @@ MapStruct supports enum to a String mapping along the same lines as is described 2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. 3. Difference: `` will result in an error. It acts on the premise that there is name similarity between enum constants in source and target which does not make sense for a String type. 4. Difference: Given 1. and 3. there will never be unmapped values. -5. Similarity: `THROW_EXCEPTION` can be used for throwing an exception for particular enum values. +5. Similarity: `` can be used for throwing an exception for particular enum values. *`String` to enum* @@ -213,7 +213,7 @@ MapStruct supports enum to a String mapping along the same lines as is described 2. Similarity: ` stops after handling defined mapping and proceeds to the switch/default clause value. 3. Similarity: `` will create a mapping for each target enum constant and proceed to the switch/default clause value. 4. Difference: A switch/default value needs to be provided to have a determined outcome (enum has a limited set of values, `String` has unlimited options). Failing to specify `` or ` will result in a warning. -5. Similarity: `THROW_EXCEPTION` can be used for throwing an exception for any arbitrary `String` value. +5. Similarity: `` can be used for throwing an exception for any arbitrary `String` value. === Custom name transformation From 9adcb06c3486ea53ab37020b3080f776efe9fb48 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 22 Apr 2023 17:22:26 +0200 Subject: [PATCH 338/597] #3236 Add missing jakarta-cdi to the documentation --- documentation/src/main/asciidoc/chapter-2-set-up.asciidoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 12c399c1f5..666954c4b1 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -215,10 +215,11 @@ suppressGeneratorVersionInfoComment` Supported values are: * `default`: the mapper uses no component model, instances are typically retrieved via `Mappers#getMapper(Class)` -* `cdi`: the generated mapper is an application-scoped CDI bean and can be retrieved via `@Inject` +* `cdi`: the generated mapper is an application-scoped (from javax.enterprise.context or jakarta.enterprise.context, depending on which one is available with javax.inject having priority) CDI bean and can be retrieved via `@Inject` * `spring`: the generated mapper is a singleton-scoped Spring bean and can be retrieved via `@Autowired` * `jsr330`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject` (from javax.inject or jakarta.inject, depending which one is available with javax.inject having priority), e.g. using Spring * `jakarta`: the generated mapper is annotated with {@code @Named} and can be retrieved via `@Inject` (from jakarta.inject), e.g. using Spring +* `jakarta-cdi`: the generated mapper is an application-scoped (from jakarta.enterprise.context) CDI bean and can be retrieved via `@Inject` If a component model is given for a specific mapper via `@Mapper#componentModel()`, the value from the annotation takes precedence. |`default` From b1034e67035fea9329b13e288aa4503533f6912c Mon Sep 17 00:00:00 2001 From: Claudio Nave Date: Sat, 18 Mar 2023 13:01:49 +0100 Subject: [PATCH 339/597] #3202 Improve line location report for invalid qualifier for SubclassMapping --- .../ap/internal/model/BeanMappingMethod.java | 2 +- .../model/source/SubclassMappingOptions.java | 40 +++++++++++-------- .../ErroneousSubclassQualifiedByMapper.java | 1 - .../SubclassQualifierMapperTest.java | 5 ++- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 8942c62789..c8fbe0205b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -420,7 +420,7 @@ private SubclassMapping createSubclassMapping(SubclassMappingOptions subclassMap FormattingParameters.EMPTY, criteria, rightHandSide, - null, + subclassMappingOptions.getMirror(), () -> forgeSubclassMapping( rightHandSide, sourceType, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java index 6ed1117ebd..11b4b283c2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java @@ -7,7 +7,10 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.TypeMirror; @@ -34,14 +37,16 @@ public class SubclassMappingOptions extends DelegatingOptions { private final TypeMirror target; private final TypeUtils typeUtils; private final SelectionParameters selectionParameters; + private final SubclassMappingGem subclassMapping; public SubclassMappingOptions(TypeMirror source, TypeMirror target, TypeUtils typeUtils, DelegatingOptions next, - SelectionParameters selectionParameters) { + SelectionParameters selectionParameters, SubclassMappingGem subclassMapping) { super( next ); this.source = source; this.target = target; this.typeUtils = typeUtils; this.selectionParameters = selectionParameters; + this.subclassMapping = subclassMapping; } @Override @@ -124,14 +129,18 @@ public SelectionParameters getSelectionParameters() { return selectionParameters; } + public AnnotationMirror getMirror() { + return Optional.ofNullable( subclassMapping ).map( SubclassMappingGem::mirror ).orElse( null ); + } + public static void addInstances(SubclassMappingsGem gem, ExecutableElement method, BeanMappingOptions beanMappingOptions, FormattingMessager messager, TypeUtils typeUtils, Set mappings, List sourceParameters, Type resultType, SubclassValidator subclassValidator) { - for ( SubclassMappingGem subclassMappingGem : gem.value().get() ) { + for ( SubclassMappingGem subclassMapping : gem.value().get() ) { addInstance( - subclassMappingGem, + subclassMapping, method, beanMappingOptions, messager, @@ -175,25 +184,22 @@ public static void addInstance(SubclassMappingGem subclassMapping, ExecutableEle targetSubclass, typeUtils, beanMappingOptions, - selectionParameters + selectionParameters, + subclassMapping ) ); } - public static List copyForInverseInheritance(Set subclassMappings, + public static List copyForInverseInheritance(Set mappings, BeanMappingOptions beanMappingOptions) { // we are not interested in keeping it unique at this point. - List mappings = new ArrayList<>(); - for ( SubclassMappingOptions subclassMapping : subclassMappings ) { - mappings.add( - new SubclassMappingOptions( - subclassMapping.target, - subclassMapping.source, - subclassMapping.typeUtils, - beanMappingOptions, - subclassMapping.selectionParameters - ) ); - } - return mappings; + return mappings.stream().map( mapping -> new SubclassMappingOptions( + mapping.target, + mapping.source, + mapping.typeUtils, + beanMappingOptions, + mapping.selectionParameters, + mapping.subclassMapping + ) ).collect( Collectors.toCollection( ArrayList::new ) ); } @Override diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByMapper.java index 26cdd4715e..fb5dcca11c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/ErroneousSubclassQualifiedByMapper.java @@ -11,6 +11,5 @@ @Mapper(uses = { RossiniMapper.class, VivaldiMapper.class }) public interface ErroneousSubclassQualifiedByMapper { @SubclassMapping(source = Rossini.class, target = RossiniDto.class, qualifiedBy = NonExistent.class) - @SubclassMapping(source = Vivaldi.class, target = VivaldiDto.class) ComposerDto toDto(Composer composer); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifierMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifierMapperTest.java index df67c27013..2b301d8981 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifierMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/qualifier/SubclassQualifierMapperTest.java @@ -230,7 +230,7 @@ void subclassQualifiedByNameDoesNotContainUnused() { diagnostics = @Diagnostic( type = ErroneousSubclassQualifiedByMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 15, + line = 13, message = "Qualifier error. No method found annotated with: [ @NonExistent ]. " + "See https://mapstruct.org/faq/#qualifier for more info." ) @@ -245,7 +245,8 @@ void subclassQualifiedByErroneous() { diagnostics = @Diagnostic( type = ErroneousSubclassQualifiedByNameMapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 15, + line = 13, + alternativeLine = 15, message = "Qualifier error. No method found annotated with @Named#value: [ non-existent ]. " + "See https://mapstruct.org/faq/#qualifier for more info." ) From d10d48ccff8e3715b44ddfd3ad3f5a54e1238479 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 22 Apr 2023 18:36:42 +0200 Subject: [PATCH 340/597] #3248 BeanMapping#ignoreUnmappedSourceProperties should be inherited for `@InheritConfiguration` --- .../model/source/BeanMappingOptions.java | 5 +- .../model/source/MappingMethodOptions.java | 2 +- .../ap/test/bugs/_3248/Issue3248Mapper.java | 52 +++++++++++++++++++ .../ap/test/bugs/_3248/Issue3248Test.java | 25 +++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3248/Issue3248Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3248/Issue3248Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index 24811768e7..e8f19f91f6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -41,13 +41,14 @@ public class BeanMappingOptions extends DelegatingOptions { * creates a mapping for inheritance. Will set * * @param beanMapping the bean mapping options that should be used + * @param isInverse whether the inheritance is inverse * * @return new mapping */ - public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping) { + public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping, boolean isInverse) { BeanMappingOptions options = new BeanMappingOptions( SelectionParameters.forInheritance( beanMapping.selectionParameters ), - Collections.emptyList(), + isInverse ? Collections.emptyList() : beanMapping.ignoreUnmappedSourceProperties, beanMapping.beanMapping, beanMapping ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 9f5da7b8de..2124dd974d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -167,7 +167,7 @@ public void applyInheritedOptions(SourceMethod sourceMethod, SourceMethod templa } if ( !getBeanMapping().hasAnnotation() && templateOptions.getBeanMapping().hasAnnotation() ) { - setBeanMapping( BeanMappingOptions.forInheritance( templateOptions.getBeanMapping( ) ) ); + setBeanMapping( BeanMappingOptions.forInheritance( templateOptions.getBeanMapping( ), isInverse ) ); } if ( !getEnumMappingOptions().hasAnnotation() && templateOptions.getEnumMappingOptions().hasAnnotation() ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3248/Issue3248Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3248/Issue3248Mapper.java new file mode 100644 index 0000000000..e0cbba40f7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3248/Issue3248Mapper.java @@ -0,0 +1,52 @@ +/* + * 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._3248; + +import org.mapstruct.BeanMapping; +import org.mapstruct.InheritConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface Issue3248Mapper { + + @BeanMapping(ignoreUnmappedSourceProperties = "otherValue") + Target map(Source source); + + @InheritConfiguration + Target secondMap(Source source); + + class Target { + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public String getOtherValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3248/Issue3248Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3248/Issue3248Test.java new file mode 100644 index 0000000000..ad8cf2e499 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3248/Issue3248Test.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.ap.test.bugs._3248; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Filip Hrisafov + */ +@IssueKey("3248") +@WithClasses({ + Issue3248Mapper.class +}) +class Issue3248Test { + + @ProcessorTest + void shouldCompileCode() { + + } +} From c6ea69eaf9bba177a52f6ddebebc1378797b0eb2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 15 Apr 2023 23:18:32 +0200 Subject: [PATCH 341/597] #3186 Do not use conversions in 2-step mapping when they are disabled --- .../creation/MappingResolverImpl.java | 54 ++++++----- .../ErroneousBuiltInAndBuiltInMapper.java | 44 +++++++++ .../ErroneousBuiltInAndMethodMapper.java | 50 +++++++++++ .../ErroneousConversionAndMethodMapper.java | 48 ++++++++++ .../ErroneousMethodAndBuiltInMapper.java | 49 ++++++++++ .../ErroneousMethodAndConversionMapper.java | 48 ++++++++++ .../mappingcontrol/MappingControlTest.java | 90 +++++++++++++++++++ .../ap/test/mappingcontrol/NoConversion.java | 20 +++++ 8 files changed, 380 insertions(+), 23 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousBuiltInAndBuiltInMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousBuiltInAndMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousConversionAndMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodAndBuiltInMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodAndConversionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/NoConversion.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 12b345d238..0c8e2cc4c4 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -295,20 +295,24 @@ && allowDirect( sourceType, targetType ) ) { } // 2 step method, then: method(conversion(source)) - assignment = ConversionMethod.getBestMatch( this, sourceType, targetType ); - if ( assignment != null ) { - usedSupportedMappings.addAll( supportingMethodCandidates ); - return assignment; + if ( allowConversion() ) { + assignment = ConversionMethod.getBestMatch( this, sourceType, targetType ); + if ( assignment != null ) { + usedSupportedMappings.addAll( supportingMethodCandidates ); + return assignment; + } } // stop here when looking for update methods. selectionCriteria.setPreferUpdateMapping( false ); // 2 step method, finally: conversion(method(source)) - assignment = MethodConversion.getBestMatch( this, sourceType, targetType ); - if ( assignment != null ) { - usedSupportedMappings.addAll( supportingMethodCandidates ); - return assignment; + if ( allowConversion() ) { + assignment = MethodConversion.getBestMatch( this, sourceType, targetType ); + if ( assignment != null ) { + usedSupportedMappings.addAll( supportingMethodCandidates ); + return assignment; + } } } @@ -763,22 +767,26 @@ static Assignment getBestMatch(ResolvingAttempt att, Type sourceType, Type targe return mmAttempt.result; } } - MethodMethod mbAttempt = - new MethodMethod<>( att, att.methods, att.builtIns, att::toMethodRef, att::toBuildInRef ) - .getBestMatch( sourceType, targetType ); - if ( mbAttempt.hasResult ) { - return mbAttempt.result; - } - MethodMethod bmAttempt = - new MethodMethod<>( att, att.builtIns, att.methods, att::toBuildInRef, att::toMethodRef ) - .getBestMatch( sourceType, targetType ); - if ( bmAttempt.hasResult ) { - return bmAttempt.result; + if ( att.allowConversion() ) { + MethodMethod mbAttempt = + new MethodMethod<>( att, att.methods, att.builtIns, att::toMethodRef, att::toBuildInRef ) + .getBestMatch( sourceType, targetType ); + if ( mbAttempt.hasResult ) { + return mbAttempt.result; + } + MethodMethod bmAttempt = + new MethodMethod<>( att, att.builtIns, att.methods, att::toBuildInRef, att::toMethodRef ) + .getBestMatch( sourceType, targetType ); + if ( bmAttempt.hasResult ) { + return bmAttempt.result; + } + MethodMethod bbAttempt = + new MethodMethod<>( att, att.builtIns, att.builtIns, att::toBuildInRef, att::toBuildInRef ) + .getBestMatch( sourceType, targetType ); + return bbAttempt.result; } - MethodMethod bbAttempt = - new MethodMethod<>( att, att.builtIns, att.builtIns, att::toBuildInRef, att::toBuildInRef ) - .getBestMatch( sourceType, targetType ); - return bbAttempt.result; + + return null; } MethodMethod(ResolvingAttempt attempt, List xMethods, List yMethods, diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousBuiltInAndBuiltInMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousBuiltInAndBuiltInMapper.java new file mode 100644 index 0000000000..eb9f7ea9ce --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousBuiltInAndBuiltInMapper.java @@ -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.mappingcontrol; + +import java.time.ZonedDateTime; +import java.util.Date; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper(mappingControl = NoConversion.class) +public interface ErroneousBuiltInAndBuiltInMapper { + + Target map(Source source); + + class Source { + private final ZonedDateTime time; + + public Source(ZonedDateTime time) { + this.time = time; + } + + public ZonedDateTime getTime() { + return time; + } + } + + class Target { + private final Date time; + + public Target(Date time) { + this.time = time; + } + + public Date getTime() { + return time; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousBuiltInAndMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousBuiltInAndMethodMapper.java new file mode 100644 index 0000000000..fdd7545da7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousBuiltInAndMethodMapper.java @@ -0,0 +1,50 @@ +/* + * 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.mappingcontrol; + +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Calendar; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper(mappingControl = NoConversion.class) +public interface ErroneousBuiltInAndMethodMapper { + + Target map(Source source); + + default ZonedDateTime fromInt(int time) { + return ZonedDateTime.ofInstant( Instant.ofEpochMilli( time ), ZoneOffset.UTC ); + } + + class Source { + private final int time; + + public Source(int time) { + this.time = time; + } + + public int getTime() { + return time; + } + } + + class Target { + private Calendar time; + + public Target(Calendar time) { + this.time = time; + } + + public Calendar getTime() { + return time; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousConversionAndMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousConversionAndMethodMapper.java new file mode 100644 index 0000000000..91a94aeca2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousConversionAndMethodMapper.java @@ -0,0 +1,48 @@ +/* + * 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.mappingcontrol; + +import java.time.Instant; +import java.util.Date; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper(mappingControl = NoConversion.class) +public interface ErroneousConversionAndMethodMapper { + + Target map(Source source); + + default Instant fromDate(int time) { + return Instant.ofEpochMilli( time ); + } + + class Source { + private final int time; + + public Source(int time) { + this.time = time; + } + + public int getTime() { + return time; + } + } + + class Target { + private Date time; + + public Target(Date time) { + this.time = time; + } + + public Date getTime() { + return time; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodAndBuiltInMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodAndBuiltInMapper.java new file mode 100644 index 0000000000..d7798161c1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodAndBuiltInMapper.java @@ -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.mappingcontrol; + +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.Date; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper(mappingControl = NoConversion.class) +public interface ErroneousMethodAndBuiltInMapper { + + Target map(Source source); + + default Date fromCalendar(Calendar calendar) { + return calendar != null ? calendar.getTime() : null; + } + + class Source { + private final ZonedDateTime time; + + public Source(ZonedDateTime time) { + this.time = time; + } + + public ZonedDateTime getTime() { + return time; + } + } + + class Target { + private final Date time; + + public Target(Date time) { + this.time = time; + } + + public Date getTime() { + return time; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodAndConversionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodAndConversionMapper.java new file mode 100644 index 0000000000..5a372addd9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/ErroneousMethodAndConversionMapper.java @@ -0,0 +1,48 @@ +/* + * 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.mappingcontrol; + +import java.time.Instant; +import java.util.Date; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper(mappingControl = NoConversion.class) +public interface ErroneousMethodAndConversionMapper { + + Target map(Source source); + + default long fromInstant(Instant value) { + return value != null ? value.getEpochSecond() : null; + } + + class Source { + private final Date time; + + public Source(Date time) { + this.time = time; + } + + public Date getTime() { + return time; + } + } + + class Target { + private long time; + + public Target(long time) { + this.time = time; + } + + public long getTime() { + return time; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java index c9c2e095f5..171fe4bc6a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/MappingControlTest.java @@ -240,6 +240,96 @@ public void complexSelectionNotAllowed() { public void complexSelectionNotAllowedWithConfig() { } + @ProcessorTest + @IssueKey("3186") + @WithClasses({ + ErroneousMethodAndBuiltInMapper.class, + NoConversion.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousMethodAndBuiltInMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = "Can't map property \"ZonedDateTime time\" to \"Date time\". " + + "Consider to declare/implement a mapping method: \"Date map(ZonedDateTime value)\"." + ) + }) + public void conversionSelectionNotAllowedInTwoStepMethodBuiltIdConversion() { + } + + @ProcessorTest + @IssueKey("3186") + @WithClasses({ + ErroneousBuiltInAndBuiltInMapper.class, + NoConversion.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousBuiltInAndBuiltInMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 19, + message = "Can't map property \"ZonedDateTime time\" to \"Date time\". " + + "Consider to declare/implement a mapping method: \"Date map(ZonedDateTime value)\"." + ) + }) + public void conversionSelectionNotAllowedInTwoStepBuiltInBuiltInConversion() { + } + + @ProcessorTest + @IssueKey("3186") + @WithClasses({ + ErroneousBuiltInAndMethodMapper.class, + NoConversion.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousBuiltInAndMethodMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 21, + message = "Can't map property \"int time\" to \"Calendar time\". " + + "Consider to declare/implement a mapping method: \"Calendar map(int value)\"." + ) + }) + public void conversionSelectionNotAllowedInTwoStepBuiltInMethodConversion() { + } + + @ProcessorTest + @IssueKey("3186") + @WithClasses({ + ErroneousMethodAndConversionMapper.class, + NoConversion.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousMethodAndConversionMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 19, + message = "Can't map property \"Date time\" to \"long time\". " + + "Consider to declare/implement a mapping method: \"long map(Date value)\"." + ) + }) + public void conversionSelectionNotAllowedInTwoStepMethodConversionConversion() { + } + + @ProcessorTest + @IssueKey("3186") + @WithClasses({ + ErroneousConversionAndMethodMapper.class, + NoConversion.class + }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousConversionAndMethodMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 19, + message = "Can't map property \"int time\" to \"Date time\". " + + "Consider to declare/implement a mapping method: \"Date map(int value)\"." + ) + }) + public void conversionSelectionNotAllowedInTwoStepConversionMethodConversion() { + } + private FridgeDTO createFridgeDTO() { FridgeDTO fridgeDTO = new FridgeDTO(); ShelveDTO shelveDTO = new ShelveDTO(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/NoConversion.java b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/NoConversion.java new file mode 100644 index 0000000000..6f05c16bb8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/mappingcontrol/NoConversion.java @@ -0,0 +1,20 @@ +/* + * 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.mappingcontrol; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.mapstruct.control.MappingControl; +import org.mapstruct.util.Experimental; + +@Retention(RetentionPolicy.CLASS) +@Experimental +@MappingControl( MappingControl.Use.DIRECT ) +@MappingControl( MappingControl.Use.MAPPING_METHOD ) +@MappingControl( MappingControl.Use.COMPLEX_MAPPING ) +public @interface NoConversion { +} From e69843f46e47332ea6899717e530266b853cee8f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 2 Apr 2023 19:47:08 +0200 Subject: [PATCH 342/597] #3158 BeanMapping#ignoreByDefault should work properly for constructor properties --- .../ap/internal/model/BeanMappingMethod.java | 10 ++++- .../ap/test/bugs/_2149/Issue2149Test.java | 6 --- .../ap/test/bugs/_3158/Issue3158Mapper.java | 43 +++++++++++++++++++ .../ap/test/bugs/_3158/Issue3158Test.java | 31 +++++++++++++ 4 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3158/Issue3158Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3158/Issue3158Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index c8fbe0205b..58b4640c95 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -285,7 +285,11 @@ else if ( !method.isUpdateMethod() ) { return null; } - if ( !mappingReferences.isRestrictToDefinedMappings() ) { + boolean applyImplicitMappings = !mappingReferences.isRestrictToDefinedMappings(); + if ( applyImplicitMappings ) { + applyImplicitMappings = beanMapping == null || !beanMapping.isignoreByDefault(); + } + if ( applyImplicitMappings ) { // apply name based mapping from a source reference applyTargetThisMapping(); @@ -1522,6 +1526,10 @@ private ReportingPolicyGem getUnmappedTargetPolicy() { if ( mappingReferences.isForForgedMethods() ) { return ReportingPolicyGem.IGNORE; } + // If we have ignoreByDefault = true, unprocessed target properties are not an issue. + if ( method.getOptions().getBeanMapping().isignoreByDefault() ) { + return ReportingPolicyGem.IGNORE; + } if ( method.getOptions().getBeanMapping() != null ) { return method.getOptions().getBeanMapping().unmappedTargetPolicy(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java index 8c19e2e090..39c415f340 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2149/Issue2149Test.java @@ -30,12 +30,6 @@ public class Issue2149Test { line = 18, message = "Using @BeanMapping( ignoreByDefault = true ) with @Mapping( target = \".\", ... ) is not " + "allowed. You'll need to explicitly ignore the target properties that should be ignored instead." - ), - @Diagnostic( - type = Erroneous2149Mapper.class, - kind = javax.tools.Diagnostic.Kind.WARNING, - line = 20, - message = "Unmapped target property: \"address\"." ) } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3158/Issue3158Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3158/Issue3158Mapper.java new file mode 100644 index 0000000000..25af6b3d44 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3158/Issue3158Mapper.java @@ -0,0 +1,43 @@ +/* + * 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._3158; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3158Mapper { + + Issue3158Mapper INSTANCE = Mappers.getMapper( Issue3158Mapper.class ); + + @BeanMapping(ignoreByDefault = true) + @Mapping(target = "name") + Target map(Target target); + + class Target { + private final String name; + private final String email; + + public Target(String name, String email) { + this.name = name; + this.email = email; + } + + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3158/Issue3158Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3158/Issue3158Test.java new file mode 100644 index 0000000000..e4832edfa9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3158/Issue3158Test.java @@ -0,0 +1,31 @@ +/* + * 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._3158; + +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 Filip Hrisafov + */ +@WithClasses(Issue3158Mapper.class) +@IssueKey("3158") +class Issue3158Test { + + @ProcessorTest + void beanMappingIgnoreByDefaultShouldBeRespectedForConstructorProperties() { + Issue3158Mapper.Target target = Issue3158Mapper.INSTANCE.map( new Issue3158Mapper.Target( + "tester", + "tester@test.com" + ) ); + + assertThat( target.getName() ).isEqualTo( "tester" ); + assertThat( target.getEmail() ).isNull(); + } +} From 979b35a2f46e75358af7029f0cd1ee7bbf40d8a7 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 2 Apr 2023 19:06:45 +0200 Subject: [PATCH 343/597] #3153 Do not use compress directive to strip whitespaces for value mapping switch method When using the compress directive it is going to strip whitespaces from the templates as well (i.e. what the user defined in `ValueMapping#source` and / or `ValueMapping#target --- .../ap/internal/model/ValueMappingMethod.ftl | 30 ++++++++--------- .../ap/test/bugs/_3153/Issue3153Mapper.java | 33 +++++++++++++++++++ .../ap/test/bugs/_3153/Issue3153Test.java | 30 +++++++++++++++++ 3 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3153/Issue3153Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3153/Issue3153Test.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl index 016cfcd182..104fd98437 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -48,26 +48,22 @@ } <#macro writeSource source=""> - <@compress single_line=true> - <#if sourceParameter.type.enumType> - ${source} - <#elseif sourceParameter.type.string> - "${source}" - - + <#if sourceParameter.type.enumType> + ${source}<#t> + <#elseif sourceParameter.type.string> + "${source}"<#t> + <#macro writeTarget target=""> - <@compress single_line=true> - <#if target?has_content> - <#if returnType.enumType> - <@includeModel object=returnType/>.${target} - <#elseif returnType.string> - "${target}" - - <#else> - null + <#if target?has_content> + <#if returnType.enumType> + <@includeModel object=returnType/>.${target}<#t> + <#elseif returnType.string> + "${target}"<#t> - + <#else> + null<#t> + <#macro throws> <#if (thrownTypes?size > 0)><#lt> throws <@compress single_line=true> diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3153/Issue3153Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3153/Issue3153Mapper.java new file mode 100644 index 0000000000..af54d23a8d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3153/Issue3153Mapper.java @@ -0,0 +1,33 @@ +/* + * 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._3153; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +interface Issue3153Mapper { + + Issue3153Mapper INSTANCE = Mappers.getMapper( Issue3153Mapper.class ); + + @ValueMapping(source = " PR", target = "PR") + @ValueMapping(source = " PR", target = "PR") + @ValueMapping(source = " PR", target = "PR") + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = MappingConstants.NULL) + Target mapToEnum(String value); + + @ValueMapping(source = "PR", target = " PR") + String mapFromEnum(Target value); + + enum Target { + PR, + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3153/Issue3153Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3153/Issue3153Test.java new file mode 100644 index 0000000000..658a497086 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3153/Issue3153Test.java @@ -0,0 +1,30 @@ +/* + * 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._3153; + +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 Filip Hrisafov + */ +@WithClasses(Issue3153Mapper.class) +@IssueKey("3153") +class Issue3153Test { + + @ProcessorTest + void shouldNotTrimStringValueSource() { + assertThat( Issue3153Mapper.INSTANCE.mapToEnum( "PR" ) ).isEqualTo( Issue3153Mapper.Target.PR ); + assertThat( Issue3153Mapper.INSTANCE.mapToEnum( " PR" ) ).isEqualTo( Issue3153Mapper.Target.PR ); + assertThat( Issue3153Mapper.INSTANCE.mapToEnum( " PR" ) ).isEqualTo( Issue3153Mapper.Target.PR ); + assertThat( Issue3153Mapper.INSTANCE.mapToEnum( " PR" ) ).isEqualTo( Issue3153Mapper.Target.PR ); + + assertThat( Issue3153Mapper.INSTANCE.mapFromEnum( Issue3153Mapper.Target.PR ) ).isEqualTo( " PR" ); + } +} From 1bc3436c5c64077503a1656eb8ef516c69d0e3b4 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 22 Apr 2023 22:30:52 +0200 Subject: [PATCH 344/597] #3239 Mapping composition is no longer experimental --- .../src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index e4a77f5391..8609ac4f77 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -126,7 +126,7 @@ Collection-typed attributes with the same element type will be copied by creatin MapStruct takes all public properties of the source and target types into account. This includes properties declared on super-types. [[mapping-composition]] -=== Mapping Composition (experimental) +=== Mapping Composition MapStruct supports the use of meta annotations. The `@Mapping` annotation supports now `@Target` with `ElementType#ANNOTATION_TYPE` in addition to `ElementType#METHOD`. This allows `@Mapping` to be used on other (user defined) annotations for re-use purposes. For example: ==== @@ -164,7 +164,7 @@ public interface StorageMapper { Still, they do have some properties in common. The `@ToEntity` assumes both target beans `ShelveEntity` and `BoxEntity` have properties: `"id"`, `"creationDate"` and `"name"`. It furthermore assumes that the source beans `ShelveDto` and `BoxDto` always have a property `"groupName"`. This concept is also known as "duck-typing". In other words, if it quacks like duck, walks like a duck its probably a duck. -This feature is still experimental. Error messages are not mature yet: the method on which the problem occurs is displayed, as well as the concerned values in the `@Mapping` annotation. However, the composition aspect is not visible. The messages are "as if" the `@Mapping` would be present on the concerned method directly. +Error messages are not mature yet: the method on which the problem occurs is displayed, as well as the concerned values in the `@Mapping` annotation. However, the composition aspect is not visible. The messages are "as if" the `@Mapping` would be present on the concerned method directly. Therefore, the user should use this feature with care, especially when uncertain when a property is always present. A more typesafe (but also more verbose) way would be to define base classes / interfaces on the target bean and the source bean and use `@InheritConfiguration` to achieve the same result (see <>). From 86a668661a45037adc795f003e6134f89faa3c3c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 22 Apr 2023 23:33:59 +0200 Subject: [PATCH 345/597] #3159 Do null value check for collections with default expression --- .../ap/internal/model/PropertyMapping.java | 8 ++- .../ap/test/bugs/_3159/Issue3159Mapper.java | 64 +++++++++++++++++++ .../ap/test/bugs/_3159/Issue3159Test.java | 27 ++++++++ 3 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3159/Issue3159Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3159/Issue3159Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index ad788de43b..7114ccf57b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -510,7 +510,7 @@ private boolean setterWrapperNeedsSourceNullCheck(Assignment rhs, Type targetTyp return true; } - if ( defaultValue != null || defaultJavaExpression != null ) { + if ( hasDefaultValueOrDefaultExpression() ) { // If there is default value defined then a check is needed return true; } @@ -518,6 +518,10 @@ private boolean setterWrapperNeedsSourceNullCheck(Assignment rhs, Type targetTyp return false; } + private boolean hasDefaultValueOrDefaultExpression() { + return defaultValue != null || defaultJavaExpression != null; + } + private Assignment assignToPlainViaAdder( Assignment rightHandSide) { Assignment result = rightHandSide; @@ -555,7 +559,7 @@ private Assignment assignToCollection(Type targetType, AccessorType targetAccess .targetAccessorType( targetAccessorType ) .rightHandSide( rightHandSide ) .assignment( rhs ) - .nullValueCheckStrategy( nvcs ) + .nullValueCheckStrategy( hasDefaultValueOrDefaultExpression() ? ALWAYS : nvcs ) .nullValuePropertyMappingStrategy( nvpms ) .build(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3159/Issue3159Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3159/Issue3159Mapper.java new file mode 100644 index 0000000000..42c77c71dd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3159/Issue3159Mapper.java @@ -0,0 +1,64 @@ +/* + * 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._3159; + +import java.util.Collection; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3159Mapper { + + Issue3159Mapper INSTANCE = Mappers.getMapper( Issue3159Mapper.class ); + + @Mapping(target = "elements", defaultExpression = "java(new ArrayList<>())") + Target map(Source source); + + default String elementName(Element element) { + return element != null ? element.getName() : null; + } + + class Target { + private final Collection elements; + + public Target(Collection elements) { + this.elements = elements; + } + + public Collection getElements() { + return elements; + } + } + + class Source { + private final Collection elements; + + public Source(Collection elements) { + this.elements = elements; + } + + public Collection getElements() { + return elements; + } + } + + class Element { + private final String name; + + public Element(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3159/Issue3159Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3159/Issue3159Test.java new file mode 100644 index 0000000000..77feba151c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3159/Issue3159Test.java @@ -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._3159; + +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 Filip Hrisafov + */ +@IssueKey("3159") +@WithClasses(Issue3159Mapper.class) +class Issue3159Test { + + @ProcessorTest + void shouldUseDefaultExpressionForCollection() { + Issue3159Mapper.Target target = Issue3159Mapper.INSTANCE.map( new Issue3159Mapper.Source( null ) ); + + assertThat( target.getElements() ).isEmpty(); + } +} From 1ab5db6a2774a58a6c41b5e4c2bfb23b2fa1bab7 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 23 Apr 2023 22:20:05 +0200 Subject: [PATCH 346/597] Update latest release version to 1.5.5.Final --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index 4e0c2f6ad4..8b8af9a69f 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.3.Final-blue.svg)](https://search.maven.org/search?q=g:org.mapstruct%20AND%20v:1.*.Final) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.5.Final-blue.svg)](https://search.maven.org/search?q=g:org.mapstruct%20AND%20v:1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](https://search.maven.org/search?q=g:org.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.5.3.Final + 1.5.5.Final ... @@ -114,10 +114,10 @@ plugins { dependencies { ... - implementation 'org.mapstruct:mapstruct:1.5.3.Final' + implementation 'org.mapstruct:mapstruct:1.5.5.Final' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' // if you are using mapstruct in test code } ... ``` From 2f78d3f4e2f493239ed15e6fe2bb85daab4bfed8 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sun, 30 Apr 2023 16:33:00 +0200 Subject: [PATCH 347/597] #3125: Allow subclassmapping inheritance for methods with identical signature Co-authored-by: Ben Zegveld --- .../model/source/MappingMethodOptions.java | 36 ++++++++++++++++-- .../model/source/SubclassMappingOptions.java | 17 +++++++++ .../InheritedSubclassMapper.java | 38 +++++++++++++++++++ .../subclassmapping/SubclassMappingTest.java | 20 +++++++++- 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/InheritedSubclassMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 2124dd974d..73474a8478 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -5,12 +5,15 @@ */ package org.mapstruct.ap.internal.model.source; +import static org.mapstruct.ap.internal.model.source.MappingOptions.getMappingTargetNamesBy; + import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; + import javax.lang.model.element.AnnotationMirror; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; @@ -20,8 +23,6 @@ import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.accessor.Accessor; -import static org.mapstruct.ap.internal.model.source.MappingOptions.getMappingTargetNamesBy; - /** * Encapsulates all options specifiable on a mapping method * @@ -205,12 +206,19 @@ public void applyInheritedOptions(SourceMethod sourceMethod, SourceMethod templa } if ( isInverse ) { - // normal inheritence of subclass mappings will result runtime in infinite loops. List inheritedMappings = SubclassMappingOptions.copyForInverseInheritance( templateOptions.getSubclassMappings(), getBeanMapping() ); addAllNonRedefined( sourceMethod, annotationMirror, inheritedMappings ); } + else if ( methodsHaveIdenticalSignature( templateMethod, sourceMethod ) ) { + List inheritedMappings = + SubclassMappingOptions + .copyForInheritance( + templateOptions.getSubclassMappings(), + getBeanMapping() ); + addAllNonRedefined( sourceMethod, annotationMirror, inheritedMappings ); + } Set newMappings = new LinkedHashSet<>(); for ( MappingOptions mapping : templateOptions.getMappings() ) { @@ -232,6 +240,28 @@ public void applyInheritedOptions(SourceMethod sourceMethod, SourceMethod templa } } + private boolean methodsHaveIdenticalSignature(SourceMethod templateMethod, SourceMethod sourceMethod) { + return parametersAreOfIdenticalTypeAndOrder( templateMethod, sourceMethod ) + && resultTypeIsTheSame( templateMethod, sourceMethod ); + } + + private boolean parametersAreOfIdenticalTypeAndOrder(SourceMethod templateMethod, SourceMethod sourceMethod) { + if (templateMethod.getParameters().size() != sourceMethod.getParameters().size()) { + return false; + } + for ( int i = 0; i < templateMethod.getParameters().size(); i++ ) { + if (!templateMethod.getParameters().get( i ).getType().equals( + sourceMethod.getParameters().get( i ).getType() ) ) { + return false; + } + } + return true; + } + + private boolean resultTypeIsTheSame(SourceMethod templateMethod, SourceMethod sourceMethod) { + return templateMethod.getResultType().equals( sourceMethod.getResultType() ); + } + private void addAllNonRedefined(SourceMethod sourceMethod, AnnotationMirror annotationMirror, List inheritedMappings) { Set redefinedSubclassMappings = new HashSet<>( subclassMappings ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java index 11b4b283c2..2b0e6dd13e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SubclassMappingOptions.java @@ -202,6 +202,23 @@ public static List copyForInverseInheritance(Set copyForInheritance(Set subclassMappings, + BeanMappingOptions beanMappingOptions) { + // we are not interested in keeping it unique at this point. + List mappings = new ArrayList<>(); + for ( SubclassMappingOptions subclassMapping : subclassMappings ) { + mappings.add( + new SubclassMappingOptions( + subclassMapping.source, + subclassMapping.target, + subclassMapping.typeUtils, + beanMappingOptions, + subclassMapping.selectionParameters, + subclassMapping.subclassMapping ) ); + } + return mappings; + } + @Override public boolean equals(Object obj) { if ( obj == null || !( obj instanceof SubclassMappingOptions ) ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/InheritedSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/InheritedSubclassMapper.java new file mode 100644 index 0000000000..6e4aca98ba --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/InheritedSubclassMapper.java @@ -0,0 +1,38 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.InheritConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; +import org.mapstruct.ap.test.subclassmapping.mappables.HatchBack; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.factory.Mappers; + +@Mapper( unmappedTargetPolicy = ReportingPolicy.IGNORE ) +public interface InheritedSubclassMapper { + InheritedSubclassMapper INSTANCE = Mappers.getMapper( InheritedSubclassMapper.class ); + + @SubclassMapping( source = HatchBack.class, target = CarDto.class ) + @SubclassMapping( source = Car.class, target = CarDto.class ) + @SubclassMapping( source = Bike.class, target = BikeDto.class ) + @Mapping( source = "vehicleManufacturingCompany", target = "maker" ) + VehicleDto map(Vehicle vehicle); + + @InheritConfiguration( name = "map" ) + VehicleDto mapInherited(Vehicle dto); + + @SubclassMapping( source = Bike.class, target = CarDto.class ) + @InheritConfiguration( name = "map" ) + VehicleDto mapInheritedOverride(Vehicle dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java index 5a5ce1ef99..ba64d2cc7f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java @@ -130,7 +130,7 @@ void subclassMappingInheritsInverseMapping() { HatchBack.class, InverseOrderSubclassMapper.class } ) - void subclassMappingOverridesInverseInheritsMapping() { + void subclassMappingOverridesInverseInheritedMapping() { VehicleCollectionDto vehicleDtos = new VehicleCollectionDto(); CarDto carDto = new CarDto(); carDto.setMaker( "BenZ" ); @@ -143,6 +143,24 @@ void subclassMappingOverridesInverseInheritsMapping() { .containsExactly( Car.class ); } + @IssueKey( "3125" ) + @ProcessorTest + @WithClasses( { + HatchBack.class, + InheritedSubclassMapper.class + } ) + void subclassMappingOverridesInheritedMapping() { + Vehicle bike = new Bike(); + + VehicleDto result = InheritedSubclassMapper.INSTANCE.map( bike ); + VehicleDto resultInherited = InheritedSubclassMapper.INSTANCE.mapInherited( bike ); + VehicleDto resultOverride = InheritedSubclassMapper.INSTANCE.mapInheritedOverride( bike ); + + assertThat( result ).isInstanceOf( BikeDto.class ); + assertThat( resultInherited ).isInstanceOf( BikeDto.class ); + assertThat( resultOverride ).isInstanceOf( CarDto.class ); + } + @ProcessorTest @WithClasses( { SubclassCompositeMapper.class, CompositeSubclassMappingAnnotation.class }) void mappingIsDoneUsingSubclassMappingWithCompositeMapping() { From 931591a385a352795090a8bf9db96778d9325a3b Mon Sep 17 00:00:00 2001 From: ro0sterjam Date: Sun, 30 Apr 2023 11:02:39 -0400 Subject: [PATCH 348/597] #3071 Support defining custom processor options by custom SPI --- .../chapter-13-using-mapstruct-spi.asciidoc | 51 +++++++++++ .../org/mapstruct/ap/MappingProcessor.java | 84 ++++++++++++++++++- .../util/AnnotationProcessorContext.java | 9 +- .../mapstruct/ap/internal/util/Services.java | 4 + .../AdditionalSupportedOptionsProvider.java | 23 +++++ .../spi/MapStructProcessingEnvironment.java | 9 ++ ...dditionalSupportedOptionsProviderTest.java | 56 +++++++++++++ ...tomAdditionalSupportedOptionsProvider.java | 22 +++++ .../EmptyMapper.java | 12 +++ ...lidAdditionalSupportedOptionsProvider.java | 20 +++++ .../test/additionalsupportedoptions/Pet.java | 14 ++++ .../PetWithMissing.java | 15 ++++ .../UnknownEnumMappingStrategy.java | 29 +++++++ .../UnknownEnumMappingStrategyMapper.java | 17 ++++ 14 files changed, 361 insertions(+), 4 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/AdditionalSupportedOptionsProvider.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/AdditionalSupportedOptionsProviderTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/CustomAdditionalSupportedOptionsProvider.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/EmptyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/InvalidAdditionalSupportedOptionsProvider.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/Pet.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/PetWithMissing.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/UnknownEnumMappingStrategy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/UnknownEnumMappingStrategyMapper.java diff --git a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc index 1095d41656..ce9b288428 100644 --- a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc +++ b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc @@ -370,4 +370,55 @@ A nice example is to provide support for a custom transformation strategy. ---- include::{processor-ap-test}/value/nametransformation/CustomEnumTransformationStrategy.java[tag=documentation] ---- +==== + +[[additional-supported-options-provider]] +=== Additional Supported Options Provider + +SPI name: `org.mapstruct.ap.spi.AdditionalSupportedOptionsProvider` + +MapStruct offers the ability to pass through declared compiler args (or "options") provided to the MappingProcessor +to the individual SPIs, by implementing `AdditionalSupportedOptionsProvider` via the Service Provider Interface (SPI). + +.Custom Additional Supported Options Provider that declares `myorg.custom.defaultNullEnumConstant` as an option to pass through +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +include::{processor-ap-test}/additionalsupportedoptions/CustomAdditionalSupportedOptionsProvider.java[tag=documentation] +---- +==== + +The value of this option is provided by including an `arg` to the `compilerArgs` tag when defining your custom SPI +implementation. + +.Example maven configuration with additional options +==== +[source, maven, linenums] +[subs="verbatim,attributes"] +---- + + + + org.myorg + custom-spi-impl + ${project.version} + + + + -Amyorg.custom.defaultNullEnumConstant=MISSING + + +---- +==== + +Your custom SPI implementations can then access this configured value via `MapStructProcessingEnvironment#getOptions()`. + +.Accessing your custom options +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +include::{processor-ap-test}/additionalsupportedoptions/UnknownEnumMappingStrategy.java[tag=documentation] +---- ==== \ No newline at end of file diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 8c842f007b..407166f6fa 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -8,6 +8,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; @@ -33,17 +34,19 @@ import javax.lang.model.util.ElementKindVisitor6; import javax.tools.Diagnostic.Kind; +import org.mapstruct.ap.internal.gem.MapperGem; import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.option.Options; -import org.mapstruct.ap.internal.gem.MapperGem; -import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext; import org.mapstruct.ap.internal.processor.ModelElementProcessor; import org.mapstruct.ap.internal.processor.ModelElementProcessor.ProcessorContext; import org.mapstruct.ap.internal.util.AnnotationProcessingException; import org.mapstruct.ap.internal.util.AnnotationProcessorContext; import org.mapstruct.ap.internal.util.RoundContext; +import org.mapstruct.ap.internal.util.Services; +import org.mapstruct.ap.spi.AdditionalSupportedOptionsProvider; import org.mapstruct.ap.spi.TypeHierarchyErroneousException; import static javax.lang.model.element.ElementKind.CLASS; @@ -113,6 +116,9 @@ public class MappingProcessor extends AbstractProcessor { protected static final String NULL_VALUE_ITERABLE_MAPPING_STRATEGY = "mapstruct.nullValueIterableMappingStrategy"; protected static final String NULL_VALUE_MAP_MAPPING_STRATEGY = "mapstruct.nullValueMapMappingStrategy"; + private final Set additionalSupportedOptions; + private final String additionalSupportedOptionsError; + private Options options; private AnnotationProcessorContext annotationProcessorContext; @@ -128,6 +134,21 @@ public class MappingProcessor extends AbstractProcessor { */ private Set deferredMappers = new HashSet<>(); + public MappingProcessor() { + Set additionalSupportedOptions; + String additionalSupportedOptionsError; + try { + additionalSupportedOptions = resolveAdditionalSupportedOptions(); + additionalSupportedOptionsError = null; + } + catch ( IllegalStateException ex ) { + additionalSupportedOptions = Collections.emptySet(); + additionalSupportedOptionsError = ex.getMessage(); + } + this.additionalSupportedOptions = additionalSupportedOptions; + this.additionalSupportedOptionsError = additionalSupportedOptionsError; + } + @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init( processingEnv ); @@ -138,8 +159,13 @@ public synchronized void init(ProcessingEnvironment processingEnv) { processingEnv.getTypeUtils(), processingEnv.getMessager(), options.isDisableBuilders(), - options.isVerbose() + options.isVerbose(), + resolveAdditionalOptions( processingEnv.getOptions() ) ); + + if ( additionalSupportedOptionsError != null ) { + processingEnv.getMessager().printMessage( Kind.ERROR, additionalSupportedOptionsError ); + } } private Options createOptions() { @@ -225,6 +251,17 @@ else if ( !deferredMappers.isEmpty() ) { return ANNOTATIONS_CLAIMED_EXCLUSIVELY; } + @Override + public Set getSupportedOptions() { + Set supportedOptions = super.getSupportedOptions(); + if ( additionalSupportedOptions.isEmpty() ) { + return supportedOptions; + } + Set allSupportedOptions = new HashSet<>( supportedOptions ); + allSupportedOptions.addAll( additionalSupportedOptions ); + return allSupportedOptions; + } + /** * Gets fresh copies of all mappers deferred from previous rounds (the originals may contain references to * erroneous source/target type elements). @@ -407,6 +444,35 @@ public TypeElement visitTypeAsClass(TypeElement e, Void p) { ); } + /** + * Fetch the additional supported options provided by the SPI {@link AdditionalSupportedOptionsProvider}. + * + * @return the additional supported options + */ + private static Set resolveAdditionalSupportedOptions() { + Set additionalSupportedOptions = null; + for ( AdditionalSupportedOptionsProvider optionsProvider : + Services.all( AdditionalSupportedOptionsProvider.class ) ) { + if ( additionalSupportedOptions == null ) { + additionalSupportedOptions = new HashSet<>(); + } + Set providerOptions = optionsProvider.getAdditionalSupportedOptions(); + + for ( String providerOption : providerOptions ) { + // Ensure additional options are not in the mapstruct namespace + if ( providerOption.startsWith( "mapstruct" ) ) { + throw new IllegalStateException( + "Additional SPI options cannot start with \"mapstruct\". Provider " + optionsProvider + + " provided option " + providerOption ); + } + additionalSupportedOptions.add( providerOption ); + } + + } + + return additionalSupportedOptions == null ? Collections.emptySet() : additionalSupportedOptions; + } + private static class ProcessorComparator implements Comparator> { @Override @@ -425,4 +491,16 @@ private DeferredMapper(TypeElement deferredMapperElement, Element erroneousEleme this.erroneousElement = erroneousElement; } } + + /** + * Filters only the options belonging to the declared additional supported options. + * + * @param options all processor environment options + * @return filtered options + */ + private Map resolveAdditionalOptions(Map options) { + return options.entrySet().stream() + .filter( entry -> additionalSupportedOptions.contains( entry.getKey() ) ) + .collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ) ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java index 7421dfde7a..81640848d0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AnnotationProcessorContext.java @@ -55,8 +55,10 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen private boolean disableBuilder; private boolean verbose; + private Map options; + public AnnotationProcessorContext(Elements elementUtils, Types typeUtils, Messager messager, boolean disableBuilder, - boolean verbose) { + boolean verbose, Map options) { astModifyingAnnotationProcessors = java.util.Collections.unmodifiableList( findAstModifyingAnnotationProcessors( messager ) ); this.elementUtils = elementUtils; @@ -64,6 +66,7 @@ public AnnotationProcessorContext(Elements elementUtils, Types typeUtils, Messag this.messager = messager; this.disableBuilder = disableBuilder; this.verbose = verbose; + this.options = java.util.Collections.unmodifiableMap( options ); } /** @@ -270,4 +273,8 @@ public Map getEnumTransformationStrategies() initialize(); return enumTransformationStrategies; } + + public Map getOptions() { + return this.options; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Services.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Services.java index 2d37c46db8..292512ff80 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Services.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Services.java @@ -18,6 +18,10 @@ public class Services { private Services() { } + public static Iterable all(Class serviceType) { + return ServiceLoader.load( serviceType, Services.class.getClassLoader() ); + } + public static T get(Class serviceType, T defaultValue) { Iterator services = ServiceLoader.load( serviceType, Services.class.getClassLoader() ).iterator(); diff --git a/processor/src/main/java/org/mapstruct/ap/spi/AdditionalSupportedOptionsProvider.java b/processor/src/main/java/org/mapstruct/ap/spi/AdditionalSupportedOptionsProvider.java new file mode 100644 index 0000000000..3638f70320 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/AdditionalSupportedOptionsProvider.java @@ -0,0 +1,23 @@ +/* + * 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.spi; + +import java.util.Set; + +/** + * Provider for any additional supported options required for custom SPI implementations. + * The resolved values are retrieved from {@link MapStructProcessingEnvironment#getOptions()}. + */ +public interface AdditionalSupportedOptionsProvider { + + /** + * Returns the supported options required for custom SPI implementations. + * + * @return the additional supported options. + */ + Set getAdditionalSupportedOptions(); + +} diff --git a/processor/src/main/java/org/mapstruct/ap/spi/MapStructProcessingEnvironment.java b/processor/src/main/java/org/mapstruct/ap/spi/MapStructProcessingEnvironment.java index 0471108a51..0cea4369eb 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/MapStructProcessingEnvironment.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/MapStructProcessingEnvironment.java @@ -7,6 +7,7 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; +import java.util.Map; /** * MapStruct will provide the implementations of its SPIs with on object implementing this interface so they can use @@ -36,4 +37,12 @@ public interface MapStructProcessingEnvironment { */ Types getTypeUtils(); + /** + * Returns the resolved options specified by the impl of + * {@link AdditionalSupportedOptionsProvider}. + * + * @return resolved options + */ + Map getOptions(); + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/AdditionalSupportedOptionsProviderTest.java b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/AdditionalSupportedOptionsProviderTest.java new file mode 100644 index 0000000000..ff2c85139f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/AdditionalSupportedOptionsProviderTest.java @@ -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.additionalsupportedoptions; + +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.mapstruct.ap.spi.EnumMappingStrategy; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithServiceImplementation; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; +import org.mapstruct.ap.testutil.runner.Compiler; + +import static org.assertj.core.api.Assertions.assertThat; + +@Execution( ExecutionMode.CONCURRENT ) +public class AdditionalSupportedOptionsProviderTest { + + @ProcessorTest + @WithClasses({ + Pet.class, + PetWithMissing.class, + UnknownEnumMappingStrategyMapper.class + }) + @WithServiceImplementation(CustomAdditionalSupportedOptionsProvider.class) + @WithServiceImplementation(value = UnknownEnumMappingStrategy.class, provides = EnumMappingStrategy.class) + @ProcessorOption(name = "myorg.custom.defaultNullEnumConstant", value = "MISSING") + public void shouldUseConfiguredPrefix() { + assertThat( UnknownEnumMappingStrategyMapper.INSTANCE.map( null ) ) + .isEqualTo( PetWithMissing.MISSING ); + } + + @ProcessorTest(Compiler.JDK) // The eclipse compiler does not parse the error message properly + @WithClasses({ + EmptyMapper.class + }) + @WithServiceImplementation(InvalidAdditionalSupportedOptionsProvider.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + messageRegExp = "Additional SPI options cannot start with \"mapstruct\". Provider " + + "org.mapstruct.ap.test.additionalsupportedoptions.InvalidAdditionalSupportedOptionsProvider@.*" + + " provided option mapstruct.custom.test" + ) + ) + public void shouldFailWhenOptionsProviderUsesMapstructPrefix() { + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/CustomAdditionalSupportedOptionsProvider.java b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/CustomAdditionalSupportedOptionsProvider.java new file mode 100644 index 0000000000..87a994bb4e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/CustomAdditionalSupportedOptionsProvider.java @@ -0,0 +1,22 @@ +/* + * 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.additionalsupportedoptions; + +// tag::documentation[] +import java.util.Collections; +import java.util.Set; + +import org.mapstruct.ap.spi.AdditionalSupportedOptionsProvider; + +public class CustomAdditionalSupportedOptionsProvider implements AdditionalSupportedOptionsProvider { + + @Override + public Set getAdditionalSupportedOptions() { + return Collections.singleton( "myorg.custom.defaultNullEnumConstant" ); + } + +} +// end::documentation[] diff --git a/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/EmptyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/EmptyMapper.java new file mode 100644 index 0000000000..13a6f0aece --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/EmptyMapper.java @@ -0,0 +1,12 @@ +/* + * 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.additionalsupportedoptions; + +import org.mapstruct.Mapper; + +@Mapper +public interface EmptyMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/InvalidAdditionalSupportedOptionsProvider.java b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/InvalidAdditionalSupportedOptionsProvider.java new file mode 100644 index 0000000000..1a387abba2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/InvalidAdditionalSupportedOptionsProvider.java @@ -0,0 +1,20 @@ +/* + * 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.additionalsupportedoptions; + +import java.util.Collections; +import java.util.Set; + +import org.mapstruct.ap.spi.AdditionalSupportedOptionsProvider; + +public class InvalidAdditionalSupportedOptionsProvider implements AdditionalSupportedOptionsProvider { + + @Override + public Set getAdditionalSupportedOptions() { + return Collections.singleton( "mapstruct.custom.test" ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/Pet.java b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/Pet.java new file mode 100644 index 0000000000..8fe9b39558 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/Pet.java @@ -0,0 +1,14 @@ +/* + * 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.additionalsupportedoptions; + +public enum Pet { + + DOG, + CAT, + BEAR + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/PetWithMissing.java b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/PetWithMissing.java new file mode 100644 index 0000000000..21d5f4a649 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/PetWithMissing.java @@ -0,0 +1,15 @@ +/* + * 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.additionalsupportedoptions; + +public enum PetWithMissing { + + DOG, + CAT, + BEAR, + MISSING + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/UnknownEnumMappingStrategy.java b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/UnknownEnumMappingStrategy.java new file mode 100644 index 0000000000..ca4403925e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/UnknownEnumMappingStrategy.java @@ -0,0 +1,29 @@ +/* + * 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.additionalsupportedoptions; + +// tag::documentation[] +import javax.lang.model.element.TypeElement; + +import org.mapstruct.ap.spi.DefaultEnumMappingStrategy; +import org.mapstruct.ap.spi.MapStructProcessingEnvironment; + +public class UnknownEnumMappingStrategy extends DefaultEnumMappingStrategy { + + private String defaultNullEnumConstant; + + @Override + public void init(MapStructProcessingEnvironment processingEnvironment) { + super.init( processingEnvironment ); + defaultNullEnumConstant = processingEnvironment.getOptions().get( "myorg.custom.defaultNullEnumConstant" ); + } + + @Override + public String getDefaultNullEnumConstant(TypeElement enumType) { + return defaultNullEnumConstant; + } +} +// end::documentation[] diff --git a/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/UnknownEnumMappingStrategyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/UnknownEnumMappingStrategyMapper.java new file mode 100644 index 0000000000..be8c74e227 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/additionalsupportedoptions/UnknownEnumMappingStrategyMapper.java @@ -0,0 +1,17 @@ +/* + * 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.additionalsupportedoptions; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface UnknownEnumMappingStrategyMapper { + + UnknownEnumMappingStrategyMapper INSTANCE = Mappers.getMapper( UnknownEnumMappingStrategyMapper.class ); + + PetWithMissing map(Pet pet); +} From d3b4a168b7abef6c31fea4f3506996ffe23ad718 Mon Sep 17 00:00:00 2001 From: Bragolgirith Date: Mon, 1 May 2023 09:11:05 +0200 Subject: [PATCH 349/597] #3199 Add support for implicit conversion between java.time.LocalDate and java.time.LocalDateTime --- .../chapter-5-data-type-conversions.asciidoc | 2 + .../internal/conversion/ConversionUtils.java | 12 +++++ .../ap/internal/conversion/Conversions.java | 5 +- ...avaLocalDateTimeToLocalDateConversion.java | 46 +++++++++++++++++++ .../java8time/Java8TimeConversionTest.java | 15 ++++++ .../ap/test/conversion/java8time/Source.java | 10 ++++ .../ap/test/conversion/java8time/Target.java | 11 +++++ .../java8time/SourceTargetMapperImpl.java | 36 +++++++++++++++ 8 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaLocalDateTimeToLocalDateConversion.java diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 51ccb21a46..258c7085f2 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -107,6 +107,8 @@ public interface CarMapper { * Between `java.time.Instant` from Java 8 Date-Time package and `java.util.Date`. +* Between `java.time.LocalDateTime` from Java 8 Date-Time package and `java.time.LocalDate` from the same package. + * Between `java.time.ZonedDateTime` from Java 8 Date-Time package and `java.util.Calendar`. * Between `java.sql.Date` and `java.util.Date` diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java index e88bb93b86..496d11676f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java @@ -12,6 +12,7 @@ import java.sql.Timestamp; import java.text.DecimalFormat; import java.text.SimpleDateFormat; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; @@ -190,6 +191,17 @@ public static String zoneId(ConversionContext conversionContext) { return typeReferenceName( conversionContext, ZoneId.class ); } + /** + * Name for {@link java.time.LocalDate}. + * + * @param conversionContext Conversion context + * + * @return Name or fully-qualified name. + */ + public static String localDate(ConversionContext conversionContext) { + return typeReferenceName( conversionContext, LocalDate.class ); + } + /** * Name for {@link java.time.LocalDateTime}. * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index e947c78571..9a4085df83 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -233,12 +233,15 @@ private void registerJava8TimeConversions() { register( Period.class, String.class, new StaticParseToStringConversion() ); register( Duration.class, String.class, new StaticParseToStringConversion() ); - // Java 8 to Date + // Java 8 time to Date register( ZonedDateTime.class, Date.class, new JavaZonedDateTimeToDateConversion() ); register( LocalDateTime.class, Date.class, new JavaLocalDateTimeToDateConversion() ); register( LocalDate.class, Date.class, new JavaLocalDateToDateConversion() ); register( Instant.class, Date.class, new JavaInstantToDateConversion() ); + // Java 8 time + register( LocalDateTime.class, LocalDate.class, new JavaLocalDateTimeToLocalDateConversion() ); + } private void registerJavaTimeSqlConversions() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaLocalDateTimeToLocalDateConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaLocalDateTimeToLocalDateConversion.java new file mode 100644 index 0000000000..ae571ad6b0 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaLocalDateTimeToLocalDateConversion.java @@ -0,0 +1,46 @@ +/* + * 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.internal.conversion; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Collections; + +/** + * SimpleConversion for mapping {@link LocalDateTime} to + * {@link LocalDate} and vice versa. + */ + +public class JavaLocalDateTimeToLocalDateConversion extends SimpleConversion { + + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toLocalDate()"; + } + + @Override + protected Set getToConversionImportTypes(ConversionContext conversionContext) { + return Collections.asSet( + conversionContext.getTypeFactory().getType( LocalDate.class ) + ); + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return ".atStartOfDay()"; + } + + @Override + protected Set getFromConversionImportTypes(ConversionContext conversionContext) { + return Collections.asSet( + conversionContext.getTypeFactory().getType( LocalDateTime.class ) + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java index aa30865425..0059630532 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java @@ -252,6 +252,21 @@ public void testInstantToDateMapping() { assertThat( source.getForDateConversionWithInstant() ).isEqualTo( instant ); } + @ProcessorTest + public void testLocalDateTimeToLocalDateMapping() { + LocalDate localDate = LocalDate.of( 2014, 1, 1 ); + + Source source = new Source(); + source.setForLocalDateTimeConversionWithLocalDate( localDate ); + Target target = SourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( source ); + LocalDateTime localDateTime = target.getForLocalDateTimeConversionWithLocalDate(); + assertThat( localDateTime ).isNotNull(); + assertThat( localDateTime ).isEqualTo( LocalDateTime.of( 2014, 1, 1, 0, 0 ) ); + + source = SourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source.getForLocalDateTimeConversionWithLocalDate() ).isEqualTo( localDate ); + } + @ProcessorTest @DefaultTimeZone("Australia/Melbourne") public void testLocalDateTimeToDateMapping() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Source.java index 91298617cd..93479f8a86 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Source.java @@ -38,6 +38,8 @@ public class Source { private Instant forDateConversionWithInstant; + private LocalDate forLocalDateTimeConversionWithLocalDate; + private Instant forInstantConversionWithString; private Period forPeriodConversionWithString; @@ -124,6 +126,14 @@ public void setForDateConversionWithInstant(Instant forDateConversionWithInstant this.forDateConversionWithInstant = forDateConversionWithInstant; } + public LocalDate getForLocalDateTimeConversionWithLocalDate() { + return forLocalDateTimeConversionWithLocalDate; + } + + public void setForLocalDateTimeConversionWithLocalDate(LocalDate forLocalDateTimeConversionWithLocalDate) { + this.forLocalDateTimeConversionWithLocalDate = forLocalDateTimeConversionWithLocalDate; + } + public Instant getForInstantConversionWithString() { return forInstantConversionWithString; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Target.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Target.java index 188a6d0e20..d2a4878731 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Target.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.conversion.java8time; +import java.time.LocalDateTime; import java.util.Calendar; import java.util.Date; @@ -33,6 +34,8 @@ public class Target { private Date forDateConversionWithInstant; + private LocalDateTime forLocalDateTimeConversionWithLocalDate; + private String forInstantConversionWithString; private String forPeriodConversionWithString; @@ -119,6 +122,14 @@ public void setForDateConversionWithInstant(Date forDateConversionWithInstant) { this.forDateConversionWithInstant = forDateConversionWithInstant; } + public LocalDateTime getForLocalDateTimeConversionWithLocalDate() { + return forLocalDateTimeConversionWithLocalDate; + } + + public void setForLocalDateTimeConversionWithLocalDate(LocalDateTime forLocalDateTimeConversionWithLocalDate) { + this.forLocalDateTimeConversionWithLocalDate = forLocalDateTimeConversionWithLocalDate; + } + public String getForInstantConversionWithString() { return forInstantConversionWithString; } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapperImpl.java index 58beaa19dd..3e0f5f84fe 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapperImpl.java @@ -68,6 +68,9 @@ public Target sourceToTarget(Source source) { if ( source.getForDateConversionWithInstant() != null ) { target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); } + if ( source.getForLocalDateTimeConversionWithLocalDate() != null ) { + target.setForLocalDateTimeConversionWithLocalDate( source.getForLocalDateTimeConversionWithLocalDate().atStartOfDay() ); + } if ( source.getForInstantConversionWithString() != null ) { target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); } @@ -117,6 +120,9 @@ public Target sourceToTargetDefaultMapping(Source source) { if ( source.getForDateConversionWithInstant() != null ) { target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); } + if ( source.getForLocalDateTimeConversionWithLocalDate() != null ) { + target.setForLocalDateTimeConversionWithLocalDate( source.getForLocalDateTimeConversionWithLocalDate().atStartOfDay() ); + } if ( source.getForInstantConversionWithString() != null ) { target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); } @@ -166,6 +172,9 @@ public Target sourceToTargetDateTimeMapped(Source source) { if ( source.getForDateConversionWithInstant() != null ) { target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); } + if ( source.getForLocalDateTimeConversionWithLocalDate() != null ) { + target.setForLocalDateTimeConversionWithLocalDate( source.getForLocalDateTimeConversionWithLocalDate().atStartOfDay() ); + } if ( source.getForInstantConversionWithString() != null ) { target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); } @@ -215,6 +224,9 @@ public Target sourceToTargetLocalDateTimeMapped(Source source) { if ( source.getForDateConversionWithInstant() != null ) { target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); } + if ( source.getForLocalDateTimeConversionWithLocalDate() != null ) { + target.setForLocalDateTimeConversionWithLocalDate( source.getForLocalDateTimeConversionWithLocalDate().atStartOfDay() ); + } if ( source.getForInstantConversionWithString() != null ) { target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); } @@ -264,6 +276,9 @@ public Target sourceToTargetLocalDateMapped(Source source) { if ( source.getForDateConversionWithInstant() != null ) { target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); } + if ( source.getForLocalDateTimeConversionWithLocalDate() != null ) { + target.setForLocalDateTimeConversionWithLocalDate( source.getForLocalDateTimeConversionWithLocalDate().atStartOfDay() ); + } if ( source.getForInstantConversionWithString() != null ) { target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); } @@ -313,6 +328,9 @@ public Target sourceToTargetLocalTimeMapped(Source source) { if ( source.getForDateConversionWithInstant() != null ) { target.setForDateConversionWithInstant( Date.from( source.getForDateConversionWithInstant() ) ); } + if ( source.getForLocalDateTimeConversionWithLocalDate() != null ) { + target.setForLocalDateTimeConversionWithLocalDate( source.getForLocalDateTimeConversionWithLocalDate().atStartOfDay() ); + } if ( source.getForInstantConversionWithString() != null ) { target.setForInstantConversionWithString( source.getForInstantConversionWithString().toString() ); } @@ -362,6 +380,9 @@ public Source targetToSource(Target target) { if ( target.getForDateConversionWithInstant() != null ) { source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); } + if ( target.getForLocalDateTimeConversionWithLocalDate() != null ) { + source.setForLocalDateTimeConversionWithLocalDate( target.getForLocalDateTimeConversionWithLocalDate().toLocalDate() ); + } if ( target.getForInstantConversionWithString() != null ) { source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); } @@ -411,6 +432,9 @@ public Source targetToSourceDateTimeMapped(Target target) { if ( target.getForDateConversionWithInstant() != null ) { source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); } + if ( target.getForLocalDateTimeConversionWithLocalDate() != null ) { + source.setForLocalDateTimeConversionWithLocalDate( target.getForLocalDateTimeConversionWithLocalDate().toLocalDate() ); + } if ( target.getForInstantConversionWithString() != null ) { source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); } @@ -460,6 +484,9 @@ public Source targetToSourceLocalDateTimeMapped(Target target) { if ( target.getForDateConversionWithInstant() != null ) { source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); } + if ( target.getForLocalDateTimeConversionWithLocalDate() != null ) { + source.setForLocalDateTimeConversionWithLocalDate( target.getForLocalDateTimeConversionWithLocalDate().toLocalDate() ); + } if ( target.getForInstantConversionWithString() != null ) { source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); } @@ -509,6 +536,9 @@ public Source targetToSourceLocalDateMapped(Target target) { if ( target.getForDateConversionWithInstant() != null ) { source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); } + if ( target.getForLocalDateTimeConversionWithLocalDate() != null ) { + source.setForLocalDateTimeConversionWithLocalDate( target.getForLocalDateTimeConversionWithLocalDate().toLocalDate() ); + } if ( target.getForInstantConversionWithString() != null ) { source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); } @@ -558,6 +588,9 @@ public Source targetToSourceLocalTimeMapped(Target target) { if ( target.getForDateConversionWithInstant() != null ) { source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); } + if ( target.getForLocalDateTimeConversionWithLocalDate() != null ) { + source.setForLocalDateTimeConversionWithLocalDate( target.getForLocalDateTimeConversionWithLocalDate().toLocalDate() ); + } if ( target.getForInstantConversionWithString() != null ) { source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); } @@ -607,6 +640,9 @@ public Source targetToSourceDefaultMapping(Target target) { if ( target.getForDateConversionWithInstant() != null ) { source.setForDateConversionWithInstant( target.getForDateConversionWithInstant().toInstant() ); } + if ( target.getForLocalDateTimeConversionWithLocalDate() != null ) { + source.setForLocalDateTimeConversionWithLocalDate( target.getForLocalDateTimeConversionWithLocalDate().toLocalDate() ); + } if ( target.getForInstantConversionWithString() != null ) { source.setForInstantConversionWithString( Instant.parse( target.getForInstantConversionWithString() ) ); } From 970984785d5d2714fc398255c5938ee539a0d8c0 Mon Sep 17 00:00:00 2001 From: todzhang's cloudsdocker Date: Mon, 1 May 2023 17:12:23 +1000 Subject: [PATCH 350/597] Update one typo in JavaDoc (#2938) --- core/src/main/java/org/mapstruct/Mapping.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 92ae29eb54..42384152d0 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -287,7 +287,7 @@ /** * Whether the property specified via {@link #target()} should be ignored by the generated mapping method or not. - * This can be useful when certain attributes should not be propagated from source or target or when properties in + * This can be useful when certain attributes should not be propagated from source to target or when properties in * the target object are populated using a decorator and thus would be reported as unmapped target property by * default. * From a8df94cc2049099dce5f42ead520965da563e9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Campanero=20Ortiz?= Date: Mon, 1 May 2023 09:22:59 +0200 Subject: [PATCH 351/597] #2987 Support for defining Javadoc in the generated mapper implementation --- core/src/main/java/org/mapstruct/Javadoc.java | 115 ++++++++++++++++++ core/src/main/java/org/mapstruct/Mapper.java | 1 + .../chapter-3-defining-a-mapper.asciidoc | 100 +++++++++++++++ .../ap/internal/gem/GemGenerator.java | 2 + .../ap/internal/model/GeneratedType.java | 4 + .../mapstruct/ap/internal/model/Javadoc.java | 92 ++++++++++++++ .../mapstruct/ap/internal/model/Mapper.java | 19 ++- .../processor/MapperCreationProcessor.java | 31 +++++ .../mapstruct/ap/internal/util/Message.java | 2 + .../ap/internal/model/GeneratedType.ftl | 1 + .../mapstruct/ap/internal/model/Javadoc.ftl | 25 ++++ .../test/javadoc/ErroneousJavadocMapper.java | 17 +++ .../JavadocAnnotatedWithAttributesMapper.java | 21 ++++ .../JavadocAnnotatedWithValueMapper.java | 22 ++++ .../ap/test/javadoc/JavadocTest.java | 54 ++++++++ ...adocAnnotatedWithAttributesMapperImpl.java | 27 ++++ .../JavadocAnnotatedWithValueMapperImpl.java | 27 ++++ 17 files changed, 558 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/org/mapstruct/Javadoc.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/Javadoc.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/Javadoc.ftl create mode 100644 processor/src/test/java/org/mapstruct/ap/test/javadoc/ErroneousJavadocMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithAttributesMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithValueMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocTest.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithAttributesMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithValueMapperImpl.java diff --git a/core/src/main/java/org/mapstruct/Javadoc.java b/core/src/main/java/org/mapstruct/Javadoc.java new file mode 100644 index 0000000000..4b5d2fb839 --- /dev/null +++ b/core/src/main/java/org/mapstruct/Javadoc.java @@ -0,0 +1,115 @@ +/* + * 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; + +/** + * Allows the definition of Javadoc comments in the MapStruct Mapper generated class. + * + *

      The annotation provides support for the usual Javadoc comments elements by defining analogous attributes.

      + * + * + *

      Please, note that at least one of these attributes must be specified.

      + * + *

      + * For instance, the following definition; + *

      + *
      
      + * @Javadoc(
      + *     value = "This is the description",
      + *     authors = { "author1", "author2" },
      + *     deprecated = "Use {@link OtherMapper} instead",
      + *     since = "0.1"
      + * )
      + * 
      + * + *

      + * will generate: + *

      + * + *
      
      + * /**
      + * * This is the description
      + * *
      + * * @author author1
      + * * @author author2
      + * *
      + * * @deprecated Use {@link OtherMapper} instead
      + * * @since 0.1
      + * */
      + * 
      + * + *

      + * The entire Javadoc comment block can be passed directly: + *

      + *
      
      + * @Javadoc("This is the description\n"
      + *            + "\n"
      + *            + "@author author1\n"
      + *            + "@author author2\n"
      + *            + "\n"
      + *            + "@deprecated Use {@link OtherMapper} instead\n"
      + *            + "@since 0.1\n"
      + * )
      + * 
      + * + *
      
      + * // or using Text Blocks
      + * @Javadoc(
      + *     """
      + *     This is the description
      + *
      + *     @author author1
      + *     @author author2
      + *
      + *     @deprecated Use {@link OtherMapper} instead
      + *     @since 0.1
      + *     """
      + * )
      + * 
      + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface Javadoc { + /** + * Main Javadoc comment text block. + * + * @return Main Javadoc comment text block. + */ + String value() default ""; + + /** + * List of authors of the code that it is being documented. + *

      + * It will generate a list of the Javadoc tool comment element @author + * with the different values and in the order provided. + * + * @return array of javadoc authors. + */ + String[] authors() default { }; + + /** + * Specifies that the functionality that is being documented is deprecated. + *

      + * Corresponds to the @deprecated Javadoc tool comment element. + * + * @return Deprecation message about the documented functionality + */ + String deprecated() default ""; + + /** + * Specifies the version since the functionality that is being documented is available. + *

      + * Corresponds to the @since Javadoc tool comment element. + * + * @return Version since the functionality is available + */ + String since() default ""; +} diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 60725cc441..8a6f48dad9 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -74,6 +74,7 @@ *

      * * @author Gunnar Morling + * @see Javadoc */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 8609ac4f77..eb3d5a80ec 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -761,3 +761,103 @@ public class MyConverterImpl implements MyConverter { } ---- ==== + + +[[javadoc]] +=== Adding Javadoc comments + +MapStruct provides support for defining Javadoc comments in the generated mapper implementation using the +`org.mapstruct.Javadoc` annotation. + +This functionality could be relevant especially in situations where certain Javadoc standards need to be met or +to deal with Javadoc validation constraints. + +The `@Javadoc` annotation defines attributes for the different Javadoc elements. + +Consider the following example: + +.Javadoc annotation example +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +@Javadoc( + value = "This is the description", + authors = { "author1", "author2" }, + deprecated = "Use {@link OtherMapper} instead", + since = "0.1" +) +public interface MyAnnotatedWithJavadocMapper { + //... +} +---- +==== + +.Javadoc annotated generated mapper +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +/** +* This is the description +* +* @author author1 +* @author author2 +* +* @deprecated Use {@link OtherMapper} instead +* @since 0.1 +*/ +public class MyAnnotatedWithJavadocMapperImpl implements MyAnnotatedWithJavadocMapper { + //... +} +---- +==== + +The entire Javadoc comment block can be provided directly as well. + +.Javadoc annotation example with the entire Javadoc comment block provided directly +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +@Javadoc( + "This is the description\n" + + "\n" + + "@author author1\n" + + "@author author2\n" + + "\n" + + "@deprecated Use {@link OtherMapper} instead\n" + + "@since 0.1\n" +) +public interface MyAnnotatedWithJavadocMapper { + //... +} +---- +==== + +Or using Text blocks: + +.Javadoc annotation example with the entire Javadoc comment block provided directly using Text blocks +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +@Javadoc( + """ + This is the description + + @author author1 + @author author2 + + @deprecated Use {@link OtherMapper} instead + @since 0.1 + """ +) +public interface MyAnnotatedWithJavadocMapper { + //... +} +---- +==== \ No newline at end of file diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index 5caea8a008..9ac13184cd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -21,6 +21,7 @@ import org.mapstruct.InheritConfiguration; import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.IterableMapping; +import org.mapstruct.Javadoc; import org.mapstruct.MapMapping; import org.mapstruct.Mapper; import org.mapstruct.MapperConfig; @@ -75,6 +76,7 @@ @GemDefinition(Context.class) @GemDefinition(Builder.class) @GemDefinition(Condition.class) +@GemDefinition(Javadoc.class) @GemDefinition(MappingControl.class) @GemDefinition(MappingControls.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java index df6ed9b2b1..8348ecd84b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java @@ -253,6 +253,10 @@ public void removeConstructor() { constructor = null; } + public Javadoc getJavadoc() { + return null; + } + protected void addIfImportRequired(Collection collection, Type typeToAdd) { if ( typeToAdd == null ) { return; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Javadoc.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Javadoc.java new file mode 100644 index 0000000000..a1efc8c022 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Javadoc.java @@ -0,0 +1,92 @@ +/* + * 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.internal.model; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.Type; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Represents the javadoc information that should be generated for a {@link Mapper}. + * + * @author Jose Carlos Campanero Ortiz + */ +public class Javadoc extends ModelElement { + + public static class Builder { + + private String value; + private List authors; + private String deprecated; + private String since; + + public Builder value(String value) { + this.value = value; + return this; + } + + public Builder authors(List authors) { + this.authors = authors; + return this; + } + + public Builder deprecated(String deprecated) { + this.deprecated = deprecated; + return this; + } + + public Builder since(String since) { + this.since = since; + return this; + } + + public Javadoc build() { + return new Javadoc( + value, + authors, + deprecated, + since + ); + } + } + + private final String value; + private final List authors; + private final String deprecated; + private final String since; + + private Javadoc(String value, List authors, String deprecated, String since) { + this.value = value; + this.authors = authors != null ? Collections.unmodifiableList( authors ) : Collections.emptyList(); + this.deprecated = deprecated; + this.since = since; + } + + public String getValue() { + return value; + } + + public List getAuthors() { + return authors; + } + + public String getDeprecated() { + return deprecated; + } + + public String getSince() { + return since; + } + + @Override + public Set getImportTypes() { + return Collections.emptySet(); + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java index 9b7729e8fa..cd092ca4f4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java @@ -43,6 +43,7 @@ public static class Builder extends GeneratedTypeBuilder { private boolean customPackage; private boolean suppressGeneratorTimestamp; private Set customAnnotations; + private Javadoc javadoc; public Builder() { super( Builder.class ); @@ -90,6 +91,11 @@ public Builder suppressGeneratorTimestamp(boolean suppressGeneratorTimestamp) { return this; } + public Builder javadoc(Javadoc javadoc) { + this.javadoc = javadoc; + return this; + } + public Mapper build() { String implementationName = implName.replace( CLASS_NAME_PLACEHOLDER, getFlatName( element ) ) + ( decorator == null ? "" : "_" ); @@ -119,7 +125,8 @@ public Mapper build() { fields, constructor, decorator, - extraImportedTypes + extraImportedTypes, + javadoc ); } @@ -128,6 +135,7 @@ public Mapper build() { private final boolean customPackage; private final boolean customImplName; private Decorator decorator; + private final Javadoc javadoc; @SuppressWarnings( "checkstyle:parameternumber" ) private Mapper(TypeFactory typeFactory, String packageName, String name, @@ -136,7 +144,7 @@ private Mapper(TypeFactory typeFactory, String packageName, String name, List methods, Options options, VersionInformation versionInformation, boolean suppressGeneratorTimestamp, Accessibility accessibility, List fields, Constructor constructor, - Decorator decorator, SortedSet extraImportedTypes ) { + Decorator decorator, SortedSet extraImportedTypes, Javadoc javadoc ) { super( typeFactory, @@ -157,6 +165,8 @@ private Mapper(TypeFactory typeFactory, String packageName, String name, customAnnotations.forEach( this::addAnnotation ); this.decorator = decorator; + + this.javadoc = javadoc; } public Decorator getDecorator() { @@ -171,6 +181,11 @@ public boolean hasCustomImplementation() { return customImplName || customPackage; } + @Override + public Javadoc getJavadoc() { + return javadoc; + } + @Override protected String getTemplateName() { return getTemplateNameForClass( GeneratedType.class ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index b69ba388b0..24dd528f6b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -28,6 +28,7 @@ import org.mapstruct.ap.internal.gem.DecoratedWithGem; import org.mapstruct.ap.internal.gem.InheritConfigurationGem; import org.mapstruct.ap.internal.gem.InheritInverseConfigurationGem; +import org.mapstruct.ap.internal.gem.JavadocGem; import org.mapstruct.ap.internal.gem.MapperGem; import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; @@ -40,6 +41,7 @@ import org.mapstruct.ap.internal.model.DelegatingMethod; import org.mapstruct.ap.internal.model.Field; import org.mapstruct.ap.internal.model.IterableMappingMethod; +import org.mapstruct.ap.internal.model.Javadoc; import org.mapstruct.ap.internal.model.MapMappingMethod; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.MapperReference; @@ -212,6 +214,7 @@ private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List< .implPackage( mapperOptions.implementationPackage() ) .suppressGeneratorTimestamp( mapperOptions.suppressTimestampInGenerated() ) .additionalAnnotations( additionalAnnotationsBuilder.getProcessedAnnotations( element ) ) + .javadoc( getJavadoc( element ) ) .build(); if ( !mappingContext.getForgedMethodsUnderCreation().isEmpty() ) { @@ -441,6 +444,23 @@ else if ( method.isStreamMapping() ) { return mappingMethods; } + private Javadoc getJavadoc(TypeElement element) { + JavadocGem javadocGem = JavadocGem.instanceOn( element ); + + if ( javadocGem == null || !isConsistent( javadocGem, element, messager ) ) { + return null; + } + + Javadoc javadoc = new Javadoc.Builder() + .value( javadocGem.value().getValue() ) + .authors( javadocGem.authors().getValue() ) + .deprecated( javadocGem.deprecated().getValue() ) + .since( javadocGem.since().getValue() ) + .build(); + + return javadoc; + } + private Type getUserDesiredReturnType(SourceMethod method) { SelectionParameters selectionParameters = method.getOptions().getBeanMapping().getSelectionParameters(); if ( selectionParameters != null && selectionParameters.getResultType() != null ) { @@ -810,4 +830,15 @@ private void reportErrorWhenNonMatchingName(SourceMethod onlyCandidate, SourceMe onlyCandidate.getName() ); } + + private boolean isConsistent( JavadocGem gem, TypeElement element, FormattingMessager messager ) { + if ( !gem.value().hasValue() + && !gem.authors().hasValue() + && !gem.deprecated().hasValue() + && !gem.since().hasValue() ) { + messager.printMessage( element, gem.mirror(), Message.JAVADOC_NO_ELEMENTS ); + return false; + } + return true; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 29600489fb..6f72f720f1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -130,6 +130,8 @@ public enum Message { DECORATOR_NO_SUBTYPE( "Specified decorator type is no subtype of the annotated mapper type." ), DECORATOR_CONSTRUCTOR( "Specified decorator type has no default constructor nor a constructor with a single parameter accepting the decorated mapper type." ), + JAVADOC_NO_ELEMENTS( "'value', 'authors', 'deprecated' and 'since' are undefined in @Javadoc, define at least one of them." ), + GENERAL_CANNOT_IMPLEMENT_PRIVATE_MAPPER("Cannot create an implementation for mapper %s, because it is a private %s."), GENERAL_NO_IMPLEMENTATION( "No implementation type is registered for return type %s." ), GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ), diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/GeneratedType.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/GeneratedType.ftl index 4981882403..c65b3e5f85 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/GeneratedType.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/GeneratedType.ftl @@ -14,6 +14,7 @@ package ${packageName}; import ${importedType}; +<#if javadoc??><#nt><@includeModel object=javadoc/> <#if !generatedTypeAvailable>/* @Generated( value = "org.mapstruct.ap.MappingProcessor"<#if suppressGeneratorTimestamp == false>, diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/Javadoc.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/Javadoc.ftl new file mode 100644 index 0000000000..89ac57b9ef --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/Javadoc.ftl @@ -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 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.Javadoc" --> +/** +<#list value?split("\n") as line><#nt>*<#if line?has_content> ${line?trim} + +<#if !authors.isEmpty()> +* +<#list authors as author> <#nt>* @author ${author?trim} + + +<#if deprecated?has_content> +* +<#nt>* @deprecated ${deprecated?trim} + +<#if since?has_content> +* +<#nt>* @since ${since?trim} + +<#nt> */ \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/javadoc/ErroneousJavadocMapper.java b/processor/src/test/java/org/mapstruct/ap/test/javadoc/ErroneousJavadocMapper.java new file mode 100644 index 0000000000..39f6da2890 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/javadoc/ErroneousJavadocMapper.java @@ -0,0 +1,17 @@ +/* + * 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.javadoc; + +import org.mapstruct.Javadoc; +import org.mapstruct.Mapper; + +/** + * @author Jose Carlos Campanero Ortiz + */ +@Mapper +@Javadoc +public interface ErroneousJavadocMapper { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithAttributesMapper.java b/processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithAttributesMapper.java new file mode 100644 index 0000000000..eb6285ff13 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithAttributesMapper.java @@ -0,0 +1,21 @@ +/* + * 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.javadoc; + +import org.mapstruct.Javadoc; +import org.mapstruct.Mapper; + +@Mapper +@Javadoc( + value = "This is the description", + authors = { "author1", "author2" }, + deprecated = "Use {@link OtherMapper} instead", + since = "0.1" +) +@Deprecated +public interface JavadocAnnotatedWithAttributesMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithValueMapper.java b/processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithValueMapper.java new file mode 100644 index 0000000000..150d7e5f76 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithValueMapper.java @@ -0,0 +1,22 @@ +/* + * 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.javadoc; + +import org.mapstruct.Javadoc; +import org.mapstruct.Mapper; + +@Mapper +@Javadoc("This is the description\n" + + "\n" + + "@author author1\n" + + "@author author2\n" + + "\n" + + "@deprecated Use {@link OtherMapper} instead\n" + + "@since 0.1\n") +@Deprecated +public interface JavadocAnnotatedWithValueMapper { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocTest.java b/processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocTest.java new file mode 100644 index 0000000000..316114389f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/javadoc/JavadocTest.java @@ -0,0 +1,54 @@ +/* + * 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.javadoc; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +/** + * @author Jose Carlos Campanero Ortiz + */ +@IssueKey("2987") +class JavadocTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses( { JavadocAnnotatedWithValueMapper.class } ) + void javadocAnnotatedWithValueMapper() { + generatedSource.addComparisonToFixtureFor( JavadocAnnotatedWithValueMapper.class ); + } + + @ProcessorTest + @WithClasses( { JavadocAnnotatedWithAttributesMapper.class } ) + void javadocAnnotatedWithAttributesMapper() { + generatedSource.addComparisonToFixtureFor( JavadocAnnotatedWithAttributesMapper.class ); + } + + @ProcessorTest + @IssueKey("2987") + @WithClasses({ ErroneousJavadocMapper.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousJavadocMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 15, + message = "'value', 'authors', 'deprecated' and 'since' are undefined in @Javadoc, " + + "define at least one of them.") + } + ) + void shouldFailOnEmptyJavadocAnnotation() { + } + +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithAttributesMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithAttributesMapperImpl.java new file mode 100644 index 0000000000..53b9a08e92 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithAttributesMapperImpl.java @@ -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.javadoc; + +import javax.annotation.processing.Generated; + +/** +* This is the description +* +* @author author1 +* @author author2 +* +* @deprecated Use {@link OtherMapper} instead +* +* @since 0.1 +*/ +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-04-30T17:36:38+0200", + comments = "version: , compiler: javac, environment: Java 11.0.18 (Ubuntu)" +) +@Deprecated +public class JavadocAnnotatedWithAttributesMapperImpl implements JavadocAnnotatedWithAttributesMapper { +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithValueMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithValueMapperImpl.java new file mode 100644 index 0000000000..7bcc54ca44 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/javadoc/JavadocAnnotatedWithValueMapperImpl.java @@ -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.javadoc; + +import javax.annotation.processing.Generated; + +/** +* This is the description +* +* @author author1 +* @author author2 +* +* @deprecated Use {@link OtherMapper} instead +* @since 0.1 +* +*/ +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-04-30T17:38:45+0200", + comments = "version: , compiler: javac, environment: Java 11.0.18 (Ubuntu)" +) +@Deprecated +public class JavadocAnnotatedWithValueMapperImpl implements JavadocAnnotatedWithValueMapper { +} From 4843123e6ef98589831a8cc8b7cc4330c7f9ed64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etien=20Ro=C5=BEnik?= <12816736+eroznik@users.noreply.github.com> Date: Mon, 1 May 2023 09:42:58 +0200 Subject: [PATCH 352/597] #3165 Support adders for array / iterable to collection --- .../ap/internal/model/PropertyMapping.java | 2 +- .../model/assignment/AdderWrapper.java | 10 ++- .../ap/internal/model/common/SourceRHS.java | 6 ++ .../ap/test/bugs/_3165/Issue3165Mapper.java | 65 +++++++++++++++++++ .../test/bugs/_3165/Issue3165MapperTest.java | 32 +++++++++ 5 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3165/Issue3165Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3165/Issue3165MapperTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 7114ccf57b..bedb37cc8f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -527,7 +527,7 @@ private Assignment assignToPlainViaAdder( Assignment rightHandSide) { Assignment result = rightHandSide; String adderIteratorName = sourcePropertyName == null ? targetPropertyName : sourcePropertyName; - if ( result.getSourceType().isCollectionType() ) { + if ( result.getSourceType().isIterableType() ) { result = new AdderWrapper( result, method.getThrownTypes(), isFieldAssignment(), adderIteratorName ); } else if ( result.getSourceType().isStreamType() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java index c5778628e9..13dfe832c9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java @@ -39,7 +39,15 @@ public AdderWrapper( Assignment rhs, // localVar is iteratorVariable String desiredName = Nouns.singularize( adderIteratorName ); rhs.setSourceLoopVarName( rhs.createUniqueVarName( desiredName ) ); - adderType = first( getSourceType().determineTypeArguments( Collection.class ) ); + if ( getSourceType().isCollectionType() ) { + adderType = first( getSourceType().determineTypeArguments( Collection.class ) ); + } + else if ( getSourceType().isArrayType() ) { + adderType = getSourceType().getComponentType(); + } + else { // iterable + adderType = first( getSourceType().determineTypeArguments( Iterable.class ) ); + } } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java index b73d1ecf44..b4d422c797 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java @@ -149,6 +149,12 @@ public Type getSourceTypeForMatching() { else if ( sourceType.isStreamType() ) { return first( sourceType.determineTypeArguments( Stream.class ) ); } + else if ( sourceType.isArrayType() ) { + return sourceType.getComponentType(); + } + else if ( sourceType.isIterableType() ) { + return first( sourceType.determineTypeArguments( Iterable.class ) ); + } } return sourceType; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3165/Issue3165Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3165/Issue3165Mapper.java new file mode 100644 index 0000000000..e065871ea3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3165/Issue3165Mapper.java @@ -0,0 +1,65 @@ +/* + * 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._3165; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.ArrayList; +import java.util.List; + +@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface Issue3165Mapper { + + Issue3165Mapper INSTANCE = Mappers.getMapper( Issue3165Mapper.class ); + + Target toTarget(Source source); + + class Source { + private String[] pets; + private Iterable cats; + + public Source(String[] pets, Iterable cats) { + this.pets = pets; + this.cats = cats; + } + + public String[] getPets() { + return pets; + } + + public Iterable getCats() { + return cats; + } + } + + class Target { + private List pets; + private List cats; + + Target() { + this.pets = new ArrayList<>(); + this.cats = new ArrayList<>(); + } + + public List getPets() { + return pets; + } + + public void addPet(String pet) { + pets.add( pet ); + } + + public List getCats() { + return cats; + } + + public void addCat(String cat) { + cats.add( cat ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3165/Issue3165MapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3165/Issue3165MapperTest.java new file mode 100644 index 0000000000..4fb9e0ab3a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3165/Issue3165MapperTest.java @@ -0,0 +1,32 @@ +/* + * 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._3165; + +import java.util.Arrays; + +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; + +@WithClasses({ + Issue3165Mapper.class +}) +@IssueKey("3165") +class Issue3165MapperTest { + + @ProcessorTest + void supportsAdderWhenMappingArrayAndIterableToCollection() { + Issue3165Mapper.Source src = new Issue3165Mapper.Source( + new String[] { "cat", "dog", "mouse" }, + Arrays.asList( "ivy", "flu", "freya" ) + ); + Issue3165Mapper.Target target = Issue3165Mapper.INSTANCE.toTarget( src ); + assertThat( target.getPets() ).containsExactly( "cat", "dog", "mouse" ); + assertThat( target.getCats() ).containsExactly( "ivy", "flu", "freya" ); + } +} From f3dac94701b316e5a62df3a34e52b3d75c483fe7 Mon Sep 17 00:00:00 2001 From: MengxingYuan Date: Mon, 1 May 2023 16:28:51 +0800 Subject: [PATCH 353/597] #2781 Remove unmapped source properties when source parameter is directly mapped --- .../ap/internal/model/BeanMappingMethod.java | 36 ++++++++----- .../ap/test/bugs/_2781/Issue2781Mapper.java | 53 +++++++++++++++++++ .../ap/test/bugs/_2781/Issue2781Test.java | 24 +++++++++ 3 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2781/Issue2781Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2781/Issue2781Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 58b4640c95..84cfd18285 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1295,8 +1295,16 @@ else if ( mapping.getJavaExpression() != null ) { .options( mapping ) .build(); handledTargets.add( targetPropertyName ); - unprocessedSourceParameters.remove( sourceRef.getParameter() ); - unprocessedSourceProperties.remove( sourceRef.getShallowestPropertyName() ); + Parameter sourceParameter = sourceRef.getParameter(); + unprocessedSourceParameters.remove( sourceParameter ); + // If the source parameter was directly mapped + if ( sourceRef.getPropertyEntries().isEmpty() ) { + // Ignore all of its source properties completely + ignoreSourceProperties( sourceParameter ); + } + else { + unprocessedSourceProperties.remove( sourceRef.getShallowestPropertyName() ); + } } else { errorOccured = true; @@ -1471,23 +1479,25 @@ private void applyParameterNameBasedMapping() { sourceParameters.remove(); unprocessedDefinedTargets.remove( targetProperty.getKey() ); unprocessedSourceProperties.remove( targetProperty.getKey() ); - - // The source parameter was directly mapped so ignore all of its source properties completely - if ( !sourceParameter.getType().isPrimitive() && !sourceParameter.getType().isArrayType() ) { - // We explicitly ignore source properties from primitives or array types - Map readAccessors = sourceParameter.getType() - .getPropertyReadAccessors(); - for ( String sourceProperty : readAccessors.keySet() ) { - unprocessedSourceProperties.remove( sourceProperty ); - } - } - unprocessedConstructorProperties.remove( targetProperty.getKey() ); + ignoreSourceProperties( sourceParameter ); } } } } + private void ignoreSourceProperties(Parameter sourceParameter) { + // The source parameter was directly mapped so ignore all of its source properties completely + if ( !sourceParameter.getType().isPrimitive() && !sourceParameter.getType().isArrayType() ) { + // We explicitly ignore source properties from primitives or array types + Map readAccessors = sourceParameter.getType() + .getPropertyReadAccessors(); + for ( String sourceProperty : readAccessors.keySet() ) { + unprocessedSourceProperties.remove( sourceProperty ); + } + } + } + private SourceReference getSourceRefByTargetName(Parameter sourceParameter, String targetPropertyName) { SourceReference sourceRef = null; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2781/Issue2781Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2781/Issue2781Mapper.java new file mode 100644 index 0000000000..9abdf560c4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2781/Issue2781Mapper.java @@ -0,0 +1,53 @@ +/* + * 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._2781; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Mengxing Yuan + */ +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface Issue2781Mapper { + + Issue2781Mapper INSTANCE = Mappers.getMapper( Issue2781Mapper.class ); + + @Mapping(target = "nested", source = "source") + Target map(Source source); + + class Target { + private Source nested; + + public Source getNested() { + return nested; + } + + public void setNested(Source nested) { + this.nested = nested; + } + } + + class Source { + private String field1; + private String field2; + + public Source(String field1, String field2) { + this.field1 = field1; + this.field2 = field2; + } + + public String getField1() { + return field1; + } + + public String getField2() { + return field2; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2781/Issue2781Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2781/Issue2781Test.java new file mode 100644 index 0000000000..80ac4f5ce3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2781/Issue2781Test.java @@ -0,0 +1,24 @@ +/* + * 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._2781; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Mengxing Yuan + */ +@IssueKey("2781") +@WithClasses({ + Issue2781Mapper.class +}) +class Issue2781Test { + + @ProcessorTest + void shouldCompileWithoutErrors() { + } +} From be94569791e8b587c8ef3a33c1c7e55db4a90ead Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 23:28:54 +0000 Subject: [PATCH 354/597] Bump protobuf-java from 3.21.2 to 3.21.7 in /parent Bumps [protobuf-java](https://github.com/protocolbuffers/protobuf) from 3.21.2 to 3.21.7. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/generate_changelog.py) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.21.2...v3.21.7) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index b306446b65..6e9c2836b6 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -41,7 +41,7 @@ The processor module needs at least Java 11. --> 1.8 - 3.21.2 + 3.21.7 2.3.2 From bc5a8771217365b32782f160f132d1dd2a1c4fa1 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Mon, 1 May 2023 11:54:24 +0200 Subject: [PATCH 355/597] #3054: Allow abstract return type when all directly sealed subtypes are covered by subclass mappings Co-authored-by: Ben Zegveld --- .../itest/tests/MavenIntegrationTest.java | 5 + .../test/resources/sealedSubclassTest/pom.xml | 102 ++++++++++++++++++ .../mapstruct/itest/sealedsubclass/Bike.java | 18 ++++ .../itest/sealedsubclass/BikeDto.java | 18 ++++ .../mapstruct/itest/sealedsubclass/Car.java | 19 ++++ .../itest/sealedsubclass/CarDto.java | 18 ++++ .../itest/sealedsubclass/Davidson.java | 18 ++++ .../itest/sealedsubclass/DavidsonDto.java | 18 ++++ .../itest/sealedsubclass/Harley.java | 18 ++++ .../itest/sealedsubclass/HarleyDto.java | 18 ++++ .../mapstruct/itest/sealedsubclass/Motor.java | 18 ++++ .../itest/sealedsubclass/MotorDto.java | 18 ++++ .../sealedsubclass/SealedSubclassMapper.java | 31 ++++++ .../itest/sealedsubclass/Vehicle.java | 27 +++++ .../sealedsubclass/VehicleCollection.java | 17 +++ .../sealedsubclass/VehicleCollectionDto.java | 17 +++ .../itest/sealedsubclass/VehicleDto.java | 27 +++++ .../sealedsubclass/SealedSubclassTest.java | 59 ++++++++++ .../ap/internal/model/BeanMappingMethod.java | 35 +++++- .../ap/internal/model/common/Type.java | 38 +++++++ 20 files changed, 537 insertions(+), 2 deletions(-) create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/pom.xml create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java create mode 100644 integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 75513dd6c6..7e9175dd92 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -112,6 +112,11 @@ void namingStrategyTest() { void protobufBuilderTest() { } + @ProcessorTest(baseDir = "sealedSubclassTest") + @EnabledForJreRange(min = JRE.JAVA_17) + void sealedSubclassTest() { + } + @ProcessorTest(baseDir = "recordsTest", processorTypes = { ProcessorTest.ProcessorType.JAVAC }) diff --git a/integrationtest/src/test/resources/sealedSubclassTest/pom.xml b/integrationtest/src/test/resources/sealedSubclassTest/pom.xml new file mode 100644 index 0000000000..0706425e01 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/pom.xml @@ -0,0 +1,102 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + sealedSubclassTest + jar + + + + generate-via-compiler-plugin + + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + \${compiler-id} + --enable-preview + + + + org.eclipse.tycho + tycho-compiler-jdt + ${org.eclipse.tycho.compiler-jdt.version} + + + + + + + + ${project.groupId} + mapstruct-processor + ${mapstruct.version} + provided + + + + + debug-forked-javac + + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + + true + + --enable-preview + -J-Xdebug + -J-Xnoagent + -J-Djava.compiler=NONE + -J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 + + + + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 1 + --enable-preview + + + + + + diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java new file mode 100644 index 0000000000..5b68f52e64 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public final class Bike extends Vehicle { + private int numberOfGears; + + public int getNumberOfGears() { + return numberOfGears; + } + + public void setNumberOfGears(int numberOfGears) { + this.numberOfGears = numberOfGears; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java new file mode 100644 index 0000000000..d51e95633b --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public final class BikeDto extends VehicleDto { + private int numberOfGears; + + public int getNumberOfGears() { + return numberOfGears; + } + + public void setNumberOfGears(int numberOfGears) { + this.numberOfGears = numberOfGears; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java new file mode 100644 index 0000000000..0ed238e2a5 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java @@ -0,0 +1,19 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public final class Car extends Vehicle { + private boolean manual; + + public boolean isManual() { + return manual; + } + + public void setManual(boolean manual) { + this.manual = manual; + } + +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java new file mode 100644 index 0000000000..800bd23d39 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public final class CarDto extends VehicleDto { + private boolean manual; + + public boolean isManual() { + return manual; + } + + public void setManual(boolean manual) { + this.manual = manual; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java new file mode 100644 index 0000000000..e883c14be3 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public final class Davidson extends Motor { + private int numberOfExhausts; + + public int getNumberOfExhausts() { + return numberOfExhausts; + } + + public void setNumberOfExhausts(int numberOfExhausts) { + this.numberOfExhausts = numberOfExhausts; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java new file mode 100644 index 0000000000..e975226e3e --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public final class DavidsonDto extends MotorDto { + private int numberOfExhausts; + + public int getNumberOfExhausts() { + return numberOfExhausts; + } + + public void setNumberOfExhausts(int numberOfExhausts) { + this.numberOfExhausts = numberOfExhausts; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java new file mode 100644 index 0000000000..87a48034c6 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public final class Harley extends Motor { + private int engineDb; + + public int getEngineDb() { + return engineDb; + } + + public void setEngineDb(int engineDb) { + this.engineDb = engineDb; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java new file mode 100644 index 0000000000..2090ee7450 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public final class HarleyDto extends MotorDto { + private int engineDb; + + public int getEngineDb() { + return engineDb; + } + + public void setEngineDb(int engineDb) { + this.engineDb = engineDb; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java new file mode 100644 index 0000000000..fcd5f4e4dc --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public sealed abstract class Motor extends Vehicle permits Harley, Davidson { + private int cc; + + public int getCc() { + return cc; + } + + public void setCc(int cc) { + this.cc = cc; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java new file mode 100644 index 0000000000..bd74eb9296 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +public sealed abstract class MotorDto extends VehicleDto permits HarleyDto, DavidsonDto { + private int cc; + + public int getCc() { + return cc; + } + + public void setCc(int cc) { + this.cc = cc; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java new file mode 100644 index 0000000000..b37f623686 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SealedSubclassMapper { + SealedSubclassMapper INSTANCE = Mappers.getMapper( SealedSubclassMapper.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + @SubclassMapping( source = Car.class, target = CarDto.class ) + @SubclassMapping( source = Bike.class, target = BikeDto.class ) + @SubclassMapping( source = Harley.class, target = HarleyDto.class ) + @SubclassMapping( source = Davidson.class, target = DavidsonDto.class ) + @Mapping( source = "vehicleManufacturingCompany", target = "maker") + VehicleDto map(Vehicle vehicle); + + VehicleCollection mapInverse(VehicleCollectionDto vehicles); + + @InheritInverseConfiguration + Vehicle mapInverse(VehicleDto dto); +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java new file mode 100644 index 0000000000..2a4e7560f6 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java @@ -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.itest.sealedsubclass; + +public abstract sealed class Vehicle permits Bike, Car, Motor { + private String name; + private String vehicleManufacturingCompany; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVehicleManufacturingCompany() { + return vehicleManufacturingCompany; + } + + public void setVehicleManufacturingCompany(String vehicleManufacturingCompany) { + this.vehicleManufacturingCompany = vehicleManufacturingCompany; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java new file mode 100644 index 0000000000..1ada92a298 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java @@ -0,0 +1,17 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +import java.util.ArrayList; +import java.util.Collection; + +public class VehicleCollection { + private Collection vehicles = new ArrayList<>(); + + public Collection getVehicles() { + return vehicles; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java new file mode 100644 index 0000000000..0cae412177 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java @@ -0,0 +1,17 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.sealedsubclass; + +import java.util.ArrayList; +import java.util.Collection; + +public class VehicleCollectionDto { + private Collection vehicles = new ArrayList<>(); + + public Collection getVehicles() { + return vehicles; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java new file mode 100644 index 0000000000..8c50bdcad9 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java @@ -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.itest.sealedsubclass; + +public abstract sealed class VehicleDto permits CarDto, BikeDto, MotorDto { + private String name; + private String maker; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMaker() { + return maker; + } + + public void setMaker(String maker) { + this.maker = maker; + } +} diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java b/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java new file mode 100644 index 0000000000..379341ff66 --- /dev/null +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.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.itest.sealedsubclass; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +public class SealedSubclassTest { + + @Test + public void mappingIsDoneUsingSubclassMapping() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Car() ); + vehicles.getVehicles().add( new Bike() ); + vehicles.getVehicles().add( new Harley() ); + vehicles.getVehicles().add( new Davidson() ); + + VehicleCollectionDto result = SealedSubclassMapper.INSTANCE.map( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( CarDto.class, BikeDto.class, HarleyDto.class, DavidsonDto.class ); + } + + @Test + public void inverseMappingIsDoneUsingSubclassMapping() { + VehicleCollectionDto vehicles = new VehicleCollectionDto(); + vehicles.getVehicles().add( new CarDto() ); + vehicles.getVehicles().add( new BikeDto() ); + vehicles.getVehicles().add( new HarleyDto() ); + vehicles.getVehicles().add( new DavidsonDto() ); + + VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( Car.class, Bike.class, Harley.class, Davidson.class ); + } + + @Test + public void subclassMappingInheritsInverseMapping() { + VehicleCollectionDto vehiclesDto = new VehicleCollectionDto(); + CarDto carDto = new CarDto(); + carDto.setMaker( "BenZ" ); + vehiclesDto.getVehicles().add( carDto ); + + VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehiclesDto ); + + assertThat( result.getVehicles() ) + .extracting( Vehicle::getVehicleManufacturingCompany ) + .containsExactly( "BenZ" ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 84cfd18285..cf3e23db9e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -446,8 +446,39 @@ private SubclassMapping createSubclassMapping(SubclassMappingOptions subclassMap } private boolean isAbstractReturnTypeAllowed() { - return method.getOptions().getBeanMapping().getSubclassExhaustiveStrategy().isAbstractReturnTypeAllowed() - && !method.getOptions().getSubclassMappings().isEmpty(); + return !method.getOptions().getSubclassMappings().isEmpty() + && ( method.getOptions().getBeanMapping().getSubclassExhaustiveStrategy().isAbstractReturnTypeAllowed() + || isCorrectlySealed() ); + } + + private boolean isCorrectlySealed() { + Type mappingSourceType = method.getMappingSourceType(); + return isCorrectlySealed( mappingSourceType ); + } + + private boolean isCorrectlySealed(Type mappingSourceType) { + if ( mappingSourceType.isSealed() ) { + List unusedPermittedSubclasses = + new ArrayList<>( mappingSourceType.getPermittedSubclasses() ); + method.getOptions().getSubclassMappings().forEach( subClassOption -> { + for (Iterator iterator = unusedPermittedSubclasses.iterator(); + iterator.hasNext(); ) { + if ( ctx.getTypeUtils().isSameType( iterator.next(), subClassOption.getSource() ) ) { + iterator.remove(); + } + } + } ); + for ( Iterator iterator = unusedPermittedSubclasses.iterator(); + iterator.hasNext(); ) { + TypeMirror typeMirror = iterator.next(); + Type type = ctx.getTypeFactory().getType( typeMirror ); + if ( type.isAbstract() && isCorrectlySealed( type ) ) { + iterator.remove(); + } + } + return unusedPermittedSubclasses.isEmpty(); + } + return false; } private void initializeMappingReferencesIfNeeded(Type resultTypeToMap) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 6894906266..4254913d83 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -5,6 +5,8 @@ */ package org.mapstruct.ap.internal.model.common; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; @@ -53,6 +55,7 @@ import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; import org.mapstruct.ap.internal.util.accessor.ReadAccessor; +import static java.util.Collections.emptyList; import static org.mapstruct.ap.internal.util.Collections.first; /** @@ -67,6 +70,18 @@ * @author Filip Hrisafov */ public class Type extends ModelElement implements Comparable { + private static final Method SEALED_PERMITTED_SUBCLASSES_METHOD; + + static { + Method permittedSubclassesMethod; + try { + permittedSubclassesMethod = TypeElement.class.getMethod( "getPermittedSubclasses" ); + } + catch ( NoSuchMethodException e ) { + permittedSubclassesMethod = null; + } + SEALED_PERMITTED_SUBCLASSES_METHOD = permittedSubclassesMethod; + } private final TypeUtils typeUtils; private final ElementUtils elementUtils; @@ -1661,4 +1676,27 @@ public boolean isEnumSet() { return "java.util.EnumSet".equals( getFullyQualifiedName() ); } + /** + * return true if this type is a java 17+ sealed class + */ + public boolean isSealed() { + return typeElement.getModifiers().stream().map( Modifier::name ).anyMatch( "SEALED"::equals ); + } + + /** + * return the list of permitted TypeMirrors for the java 17+ sealed class + */ + @SuppressWarnings( "unchecked" ) + public List getPermittedSubclasses() { + if (SEALED_PERMITTED_SUBCLASSES_METHOD == null) { + return emptyList(); + } + try { + return (List) SEALED_PERMITTED_SUBCLASSES_METHOD.invoke( typeElement ); + } + catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + return emptyList(); + } + } + } From d0e4c48228dc03722d34deb5506ec159ac38514d Mon Sep 17 00:00:00 2001 From: Jason Bodnar Date: Mon, 8 May 2023 15:23:03 -0500 Subject: [PATCH 356/597] #3172 Add mapping between Locale and String --- .../chapter-5-data-type-conversions.asciidoc | 3 ++ .../ap/internal/conversion/Conversions.java | 2 + .../conversion/LocaleToStringConversion.java | 37 +++++++++++++++ .../locale/LocaleConversionTest.java | 46 +++++++++++++++++++ .../test/conversion/locale/LocaleMapper.java | 19 ++++++++ .../test/conversion/locale/LocaleSource.java | 23 ++++++++++ .../test/conversion/locale/LocaleTarget.java | 21 +++++++++ 7 files changed, 151 insertions(+) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/LocaleToStringConversion.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleTarget.java diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 258c7085f2..30430fe97e 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -130,6 +130,9 @@ public interface CarMapper { * Between `java.net.URL` and `String`. ** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/URL[URL] otherwise a `MalformedURLException` is thrown. +* Between `java.util.Locale` and `String`. +** When converting from a `Locale`, the resulting `String` will be a well-formed IETF BCP 47 language tag representing the locale. When converting from a `String`, the locale that best represents the language tag will be returned. See https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#forLanguageTag-java.lang.String-[Locale.forLanguageTag()] and https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#toLanguageTag--[Locale.toLanguageTag()] for more information. + [[mapping-object-references]] === Mapping object references diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 9a4085df83..6acb69492a 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -21,6 +21,7 @@ import java.util.Currency; import java.util.Date; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.UUID; @@ -198,6 +199,7 @@ public Conversions(TypeFactory typeFactory) { register( Currency.class, String.class, new CurrencyToStringConversion() ); register( UUID.class, String.class, new UUIDToStringConversion() ); + register( Locale.class, String.class, new LocaleToStringConversion() ); registerURLConversion(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/LocaleToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/LocaleToStringConversion.java new file mode 100644 index 0000000000..05a6da0b19 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/LocaleToStringConversion.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.internal.conversion; + +import java.util.Locale; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Collections; + +import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale; + +/** + * Conversion between {@link java.util.Locale} and {@link String}. + * + * @author Jason Bodnar + */ +public class LocaleToStringConversion extends SimpleConversion { + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toLanguageTag()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return locale( conversionContext ) + ".forLanguageTag( )"; + } + + @Override + protected Set getFromConversionImportTypes(final ConversionContext conversionContext) { + return Collections.asSet( conversionContext.getTypeFactory().getType( Locale.class ) ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleConversionTest.java new file mode 100644 index 0000000000..cba7785512 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleConversionTest.java @@ -0,0 +1,46 @@ +/* + * 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.conversion.locale; + +import java.util.Locale; + +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 conversions between {@link Locale} and String. + * + * @author Jason Bodnar + */ +@IssueKey("3172") +@WithClasses({ LocaleSource.class, LocaleTarget.class, LocaleMapper.class }) +public class LocaleConversionTest { + + @ProcessorTest + public void shouldApplyLocaleConversion() { + LocaleSource source = new LocaleSource(); + source.setLocaleA( Locale.getDefault() ); + + LocaleTarget target = LocaleMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getLocaleA() ).isEqualTo( source.getLocaleA().toLanguageTag() ); + } + + @ProcessorTest + public void shouldApplyReverseLocaleConversion() { + LocaleTarget target = new LocaleTarget(); + target.setLocaleA( Locale.getDefault().toLanguageTag() ); + + LocaleSource source = LocaleMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getLocaleA() ).isEqualTo( Locale.forLanguageTag( target.getLocaleA() ) ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleMapper.java new file mode 100644 index 0000000000..3da3fc4ae4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleMapper.java @@ -0,0 +1,19 @@ +/* + * 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.conversion.locale; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface LocaleMapper { + + LocaleMapper INSTANCE = Mappers.getMapper( LocaleMapper.class ); + + LocaleTarget sourceToTarget(LocaleSource source); + + LocaleSource targetToSource(LocaleTarget target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleSource.java new file mode 100644 index 0000000000..69ea5bda2d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleSource.java @@ -0,0 +1,23 @@ +/* + * 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.conversion.locale; + +import java.util.Locale; + +/** + * @author Jason Bodnar + */ +public class LocaleSource { + private Locale localeA; + + public Locale getLocaleA() { + return localeA; + } + + public void setLocaleA(Locale localeA) { + this.localeA = localeA; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleTarget.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleTarget.java new file mode 100644 index 0000000000..000ff86b05 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/locale/LocaleTarget.java @@ -0,0 +1,21 @@ +/* + * 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.conversion.locale; + +/** + * @author Jason Bodnar + */ +public class LocaleTarget { + private String localeA; + + public String getLocaleA() { + return localeA; + } + + public void setLocaleA(String localeA) { + this.localeA = localeA; + } +} From a89c34f00c58e4b74bb81a102f7e615405c38692 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 1 May 2023 09:44:05 +0200 Subject: [PATCH 357/597] #3238 Compile error instead of null pointer exception for invalid ignore with target this --- .../internal/model/source/MappingOptions.java | 3 ++ .../mapstruct/ap/internal/util/Message.java | 1 + .../bugs/_3238/ErroneousIssue3238Mapper.java | 42 +++++++++++++++++++ .../ap/test/bugs/_3238/Issue3238Test.java | 37 ++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3238/ErroneousIssue3238Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3238/Issue3238Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 5046ce8b29..2af1c95f71 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -254,6 +254,9 @@ else if ( gem.nullValuePropertyMappingStrategy().hasValue() && gem.ignore().hasValue() && gem.ignore().getValue() ) { message = Message.PROPERTYMAPPING_IGNORE_AND_NVPMS; } + else if ( ".".equals( gem.target().get() ) && gem.ignore().hasValue() && gem.ignore().getValue() ) { + message = Message.PROPERTYMAPPING_TARGET_THIS_AND_IGNORE; + } if ( message == null ) { return true; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 6f72f720f1..249825ec50 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -68,6 +68,7 @@ public enum Message { PROPERTYMAPPING_CONSTANT_VALUE_AND_NVPMS( "Constant and nullValuePropertyMappingStrategy are both defined in @Mapping, either define a constant or an nullValuePropertyMappingStrategy." ), PROPERTYMAPPING_DEFAULT_EXPERSSION_AND_NVPMS( "DefaultExpression and nullValuePropertyMappingStrategy are both defined in @Mapping, either define a defaultExpression or an nullValuePropertyMappingStrategy." ), PROPERTYMAPPING_IGNORE_AND_NVPMS( "Ignore and nullValuePropertyMappingStrategy are both defined in @Mapping, either define ignore or an nullValuePropertyMappingStrategy." ), + PROPERTYMAPPING_TARGET_THIS_AND_IGNORE( "Using @Mapping( target = \".\", ignore = true ) is not allowed. You need to use @BeanMapping( ignoreByDefault = true ) if you would like to ignore all non explicitly mapped target properties." ), PROPERTYMAPPING_EXPRESSION_AND_QUALIFIER_BOTH_DEFINED("Expression and a qualifier both defined in @Mapping, either define an expression or a qualifier."), PROPERTYMAPPING_INVALID_EXPRESSION( "Value for expression must be given in the form \"java()\"." ), PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION( "Value for default expression must be given in the form \"java()\"." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3238/ErroneousIssue3238Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3238/ErroneousIssue3238Mapper.java new file mode 100644 index 0000000000..fd0d4672a6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3238/ErroneousIssue3238Mapper.java @@ -0,0 +1,42 @@ +/* + * 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._3238; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper +public interface ErroneousIssue3238Mapper { + + @Mapping(target = ".", ignore = true) + Target map(Source source); + + class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Source { + + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3238/Issue3238Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3238/Issue3238Test.java new file mode 100644 index 0000000000..6648495a70 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3238/Issue3238Test.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.bugs._3238; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +/** + * @author Filip Hrisafov + */ +@WithClasses(ErroneousIssue3238Mapper.class) +@IssueKey("3238") +class Issue3238Test { + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = @Diagnostic( type = ErroneousIssue3238Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 14, + message = "Using @Mapping( target = \".\", ignore = true ) is not allowed." + + " You need to use @BeanMapping( ignoreByDefault = true ) if you would like to ignore" + + " all non explicitly mapped target properties." + ) + ) + void shouldGenerateValidCompileError() { + + } + +} From efaa67aadf6f24c8f12804a1f18c1218d929d56f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 1 May 2023 10:18:23 +0200 Subject: [PATCH 358/597] #3104 Update methods with NullValuePropertyMappingStrategy.IGNORE should use SetterWrapperForCollectionsAndMapsWithNullCheck --- .../model/CollectionAssignmentBuilder.java | 11 +++ .../ap/test/bugs/_3104/Issue3104Mapper.java | 77 +++++++++++++++++++ .../ap/test/bugs/_3104/Issue3104Test.java | 38 +++++++++ 3 files changed, 126 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3104/Issue3104Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3104/Issue3104Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index e3bc85342a..9a0a025845 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -31,6 +31,7 @@ import org.mapstruct.ap.internal.util.accessor.AccessorType; import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_NULL; @@ -177,6 +178,16 @@ else if ( method.isUpdateMethod() && !targetImmutable ) { targetAccessorType.isFieldAssignment() ); } + else if ( method.isUpdateMethod() && nvpms == IGNORE ) { + + result = new SetterWrapperForCollectionsAndMapsWithNullCheck( + result, + method.getThrownTypes(), + targetType, + ctx.getTypeFactory(), + targetAccessorType.isFieldAssignment() + ); + } else if ( setterWrapperNeedsSourceNullCheck( result ) && canBeMappedOrDirectlyAssigned( result ) ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3104/Issue3104Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3104/Issue3104Mapper.java new file mode 100644 index 0000000000..e561f40bab --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3104/Issue3104Mapper.java @@ -0,0 +1,77 @@ +/* + * 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._3104; + +import java.util.Collections; +import java.util.List; + +import org.mapstruct.BeanMapping; +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +@Mapper(collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE) +public interface Issue3104Mapper { + + Issue3104Mapper INSTANCE = Mappers.getMapper( Issue3104Mapper.class ); + + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + void update(@MappingTarget Target target, Source source); + + class Target { + private List children = Collections.emptyList(); + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + if ( children == null ) { + throw new IllegalArgumentException( "children is null" ); + } + this.children = Collections.unmodifiableList( children ); + } + } + + class Child { + private String myField; + + public String getMyField() { + return myField; + } + + public void setMyField(String myField) { + this.myField = myField; + } + } + + class Source { + private final List children; + + public Source(List children) { + this.children = children; + } + + public List getChildren() { + return children; + } + + } + + class ChildSource { + private final String myField; + + public ChildSource(String myField) { + this.myField = myField; + } + + public String getMyField() { + return myField; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3104/Issue3104Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3104/Issue3104Test.java new file mode 100644 index 0000000000..5f3a9e160b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3104/Issue3104Test.java @@ -0,0 +1,38 @@ +/* + * 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._3104; + +import java.util.Collections; + +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 Filip Hrisafov + */ +@IssueKey("3104") +@WithClasses(Issue3104Mapper.class) +class Issue3104Test { + + @ProcessorTest + void shouldCorrectlyMapUpdateMappingWithTargetImmutableCollectionStrategy() { + Issue3104Mapper.Target target = new Issue3104Mapper.Target(); + Issue3104Mapper.INSTANCE.update( target, new Issue3104Mapper.Source( null ) ); + + assertThat( target.getChildren() ).isEmpty(); + + Issue3104Mapper.INSTANCE.update( + target, + new Issue3104Mapper.Source( Collections.singletonList( new Issue3104Mapper.ChildSource( "tester" ) ) ) + ); + assertThat( target.getChildren() ) + .extracting( Issue3104Mapper.Child::getMyField ) + .containsExactly( "tester" ); + } +} From 7c90592d051c828cc20af0818f899c6fc5adc0e4 Mon Sep 17 00:00:00 2001 From: paparadva Date: Sun, 30 Oct 2022 13:11:26 +0300 Subject: [PATCH 359/597] #2863 Add validation of String type to @TargetPropertyName --- .../processor/MethodRetrievalProcessor.java | 15 ++++++++++-- .../mapstruct/ap/internal/util/Message.java | 1 + ...sNonStringTargetPropertyNameParameter.java | 21 +++++++++++++++++ .../TargetPropertyNameTest.java | 23 +++++++++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 4ad531d334..6f1febae9d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -28,6 +28,7 @@ import org.mapstruct.ap.internal.gem.ObjectFactoryGem; import org.mapstruct.ap.internal.gem.SubclassMappingGem; import org.mapstruct.ap.internal.gem.SubclassMappingsGem; +import org.mapstruct.ap.internal.gem.TargetPropertyNameGem; import org.mapstruct.ap.internal.gem.ValueMappingGem; import org.mapstruct.ap.internal.gem.ValueMappingsGem; import org.mapstruct.ap.internal.model.common.Parameter; @@ -230,7 +231,7 @@ private SourceMethod getMethod(TypeElement usedMapper, // otherwise add reference to existing mapper method else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( method, parameters, returnType ) || isValidLifecycleCallbackMethod( method ) - || isValidPresenceCheckMethod( method, returnType ) ) { + || isValidPresenceCheckMethod( method, parameters, returnType ) ) { return getReferencedMethod( usedMapper, methodType, method, mapperToImplement, parameters ); } else { @@ -407,7 +408,17 @@ private boolean hasFactoryAnnotation(ExecutableElement method) { return ObjectFactoryGem.instanceOn( method ) != null; } - private boolean isValidPresenceCheckMethod(ExecutableElement method, Type returnType) { + private boolean isValidPresenceCheckMethod(ExecutableElement method, List parameters, Type returnType) { + for ( Parameter param : parameters ) { + if ( param.isTargetPropertyName() && !param.getType().isString() ) { + messager.printMessage( + param.getElement(), + TargetPropertyNameGem.instanceOn( param.getElement() ).mirror(), + Message.RETRIEVAL_TARGET_PROPERTY_NAME_WRONG_TYPE + ); + return false; + } + } return isBoolean( returnType ) && hasConditionAnnotation( method ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 249825ec50..a24a43036c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -176,6 +176,7 @@ public enum Message { RETRIEVAL_MAPPER_USES_CYCLE( "The mapper %s is referenced itself in Mapper#uses.", Diagnostic.Kind.WARNING ), RETRIEVAL_AFTER_METHOD_NOT_IMPLEMENTED( "@AfterMapping can only be applied to an implemented method." ), RETRIEVAL_BEFORE_METHOD_NOT_IMPLEMENTED( "@BeforeMapping can only be applied to an implemented method." ), + RETRIEVAL_TARGET_PROPERTY_NAME_WRONG_TYPE( "@TargetPropertyName can only by applied to a String parameter." ), INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ), INHERITINVERSECONFIGURATION_INVALID_NAME( "None of the candidates %s() matches given name: \"%s\"." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java new file mode 100644 index 0000000000..ec545a058b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java @@ -0,0 +1,21 @@ +/* + * 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.targetpropertyname; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.TargetPropertyName; + +@Mapper +public interface ErroneousNonStringTargetPropertyNameParameter { + + Employee map(EmployeeDto employee); + + @Condition + default boolean isNotBlank(String value, @TargetPropertyName int propName) { + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java index 3d96e66664..91e4b77de2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java @@ -9,6 +9,9 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.runner.GeneratedSource; import java.util.Collections; @@ -278,4 +281,24 @@ public void conditionalMethodWithTargetPropertyNameInUsesContextMapper() { "addresses.street" ); } + + @IssueKey("2863") + @ProcessorTest + @WithClasses({ + ErroneousNonStringTargetPropertyNameParameter.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousNonStringTargetPropertyNameParameter.class, + line = 18, + message = "@TargetPropertyName can only by applied to a String parameter." + ) + } + ) + public void nonStringTargetPropertyNameParameter() { + + } } From 6d205e5bc46365444774609e860f60819818ef4b Mon Sep 17 00:00:00 2001 From: Oliver Erhart <8238759+thunderhook@users.noreply.github.com> Date: Sun, 21 May 2023 22:49:41 +0200 Subject: [PATCH 360/597] #1454 Support for lifecycle methods on type being built with builders Add missing support for lifecycle methods with builders: * `@BeforeMapping` with `@TargetType` the type being build * `@AftereMapping` with `@TargetType` the type being build * `@AfterMapping` with `@MappingTarget` the type being build --- .../resources/build-config/checkstyle.xml | 4 +- .../chapter-12-customizing-mapping.asciidoc | 14 ++-- .../chapter-13-using-mapstruct-spi.asciidoc | 2 +- .../ap/internal/model/BeanMappingMethod.java | 72 ++++++++++++++++++- .../ap/internal/model/MappingMethod.java | 4 +- .../ap/internal/model/BeanMappingMethod.ftl | 21 +++++- .../BuilderLifecycleCallbacksTest.java | 6 +- .../builder/lifecycle/MappingContext.java | 10 ++- 8 files changed, 119 insertions(+), 14 deletions(-) diff --git a/build-config/src/main/resources/build-config/checkstyle.xml b/build-config/src/main/resources/build-config/checkstyle.xml index a4591c39de..a1ff4af23a 100644 --- a/build-config/src/main/resources/build-config/checkstyle.xml +++ b/build-config/src/main/resources/build-config/checkstyle.xml @@ -29,7 +29,9 @@ - + + + diff --git a/documentation/src/main/asciidoc/chapter-12-customizing-mapping.asciidoc b/documentation/src/main/asciidoc/chapter-12-customizing-mapping.asciidoc index 0c873eac09..dc07b30e62 100644 --- a/documentation/src/main/asciidoc/chapter-12-customizing-mapping.asciidoc +++ b/documentation/src/main/asciidoc/chapter-12-customizing-mapping.asciidoc @@ -248,9 +248,8 @@ All before/after-mapping methods that *can* be applied to a mapping method *will The order of the method invocation is determined primarily by their variant: -1. `@BeforeMapping` methods without an `@MappingTarget` parameter are called before any null-checks on source -parameters and constructing a new target bean. -2. `@BeforeMapping` methods with an `@MappingTarget` parameter are called after constructing a new target bean. +1. `@BeforeMapping` methods without parameters, a `@MappingTarget` parameter or a `@TargetType` parameter are called before any null-checks on source parameters and constructing a new target bean. +2. `@BeforeMapping` methods with a `@MappingTarget` parameter are called after constructing a new target bean. 3. `@AfterMapping` methods are called at the end of the mapping method before the last `return` statement. Within those groups, the method invocations are ordered by their location of definition: @@ -262,4 +261,11 @@ Within those groups, the method invocations are ordered by their location of def *Important:* the order of methods declared within one type can not be guaranteed, as it depends on the compiler and the processing environment implementation. -*Important:* when using a builder, the `@AfterMapping` annotated method must have the builder as `@MappingTarget` annotated parameter so that the method is able to modify the object going to be build. The `build` method is called when the `@AfterMapping` annotated method scope finishes. MapStruct will not call the `@AfterMapping` annotated method if the real target is used as `@MappingTarget` annotated parameter. \ No newline at end of file +[NOTE] +==== +Before/After-mapping methods can also be used with builders: + +* `@BeforeMapping` methods with a `@MappingTarget` parameter of the real target will not be invoked because it is only available after the mapping was already performed. +* To be able to modify the object that is going to be built, the `@AfterMapping` annotated method must have the builder as `@MappingTarget` annotated parameter. The `build` method is called when the `@AfterMapping` annotated method scope finishes. +* The `@AfterMapping` annotated method can also have the real target as `@TargetType` or `@MappingTarget`. It will be invoked after the real target was built (first the methods annotated with `@TargetType`, then the methods annotated with `@MappingTarget`) +==== \ No newline at end of file diff --git a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc index ce9b288428..51b2bbff3c 100644 --- a/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc +++ b/documentation/src/main/asciidoc/chapter-13-using-mapstruct-spi.asciidoc @@ -71,7 +71,7 @@ public class GolfPlayerDto { public GolfPlayerDto withName(String name) { this.name = name; - return this + return this; } } ---- diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index cf3e23db9e..b6afce74d2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -94,6 +94,9 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { private final Type returnTypeToConstruct; private final BuilderType returnTypeBuilder; private final MethodReference finalizerMethod; + private final String finalizedResultName; + private final List beforeMappingReferencesWithFinalizedReturnType; + private final List afterMappingReferencesWithFinalizedReturnType; private final MappingReferences mappingReferences; @@ -368,8 +371,35 @@ else if ( !method.isUpdateMethod() ) { MethodReference finalizeMethod = null; + List beforeMappingReferencesWithFinalizedReturnType = new ArrayList<>(); + List afterMappingReferencesWithFinalizedReturnType = new ArrayList<>(); if ( shouldCallFinalizerMethod( returnTypeToConstruct ) ) { finalizeMethod = getFinalizerMethod(); + + Type actualReturnType = method.getReturnType(); + + beforeMappingReferencesWithFinalizedReturnType.addAll( filterMappingTarget( + LifecycleMethodResolver.beforeMappingMethods( + method, + actualReturnType, + selectionParameters, + ctx, + existingVariableNames + ), + false + ) ); + + afterMappingReferencesWithFinalizedReturnType.addAll( LifecycleMethodResolver.afterMappingMethods( + method, + actualReturnType, + selectionParameters, + ctx, + existingVariableNames + ) ); + + // remove methods without parameters as they are already being invoked + removeMappingReferencesWithoutSourceParameters( beforeMappingReferencesWithFinalizedReturnType ); + removeMappingReferencesWithoutSourceParameters( afterMappingReferencesWithFinalizedReturnType ); } return new BeanMappingMethod( @@ -383,12 +413,18 @@ else if ( !method.isUpdateMethod() ) { returnTypeBuilder, beforeMappingMethods, afterMappingMethods, + beforeMappingReferencesWithFinalizedReturnType, + afterMappingReferencesWithFinalizedReturnType, finalizeMethod, mappingReferences, subclasses ); } + private void removeMappingReferencesWithoutSourceParameters(List references) { + references.removeIf( r -> r.getSourceParameters().isEmpty() && r.getReturnType().isVoid() ); + } + private boolean doesNotAllowAbstractReturnTypeAndCanBeConstructed(Type returnTypeImpl) { return !isAbstractReturnTypeAllowed() && canReturnTypeBeConstructed( returnTypeImpl ); @@ -706,7 +742,6 @@ private boolean isReturnTypeAbstractOrCanBeConstructed(Type returnType) { * Find a factory method for a return type or for a builder. * @param returnTypeImpl the return type implementation to construct * @param @selectionParameters - * @return */ private void initializeFactoryMethod(Type returnTypeImpl, SelectionParameters selectionParameters) { List> matchingFactoryMethods = @@ -1380,7 +1415,7 @@ else if ( mapping.getJavaExpression() != null ) { *

      * When a target property matches its name with the (nested) source property, it is added to the list if and * only if it is an unprocessed target property. - * + *

      * duplicates will be handled by {@link #applyPropertyNameBasedMapping(List)} */ private void applyTargetThisMapping() { @@ -1766,6 +1801,8 @@ private BeanMappingMethod(Method method, BuilderType returnTypeBuilder, List beforeMappingReferences, List afterMappingReferences, + List beforeMappingReferencesWithFinalizedReturnType, + List afterMappingReferencesWithFinalizedReturnType, MethodReference finalizerMethod, MappingReferences mappingReferences, List subclassMappings) { @@ -1783,9 +1820,20 @@ private BeanMappingMethod(Method method, this.propertyMappings = propertyMappings; this.returnTypeBuilder = returnTypeBuilder; this.finalizerMethod = finalizerMethod; + if ( this.finalizerMethod != null ) { + this.finalizedResultName = + Strings.getSafeVariableName( getResultName() + "Result", existingVariableNames ); + existingVariableNames.add( this.finalizedResultName ); + } + else { + this.finalizedResultName = null; + } this.mappingReferences = mappingReferences; - // intialize constant mappings as all mappings, but take out the ones that can be contributed to a + this.beforeMappingReferencesWithFinalizedReturnType = beforeMappingReferencesWithFinalizedReturnType; + this.afterMappingReferencesWithFinalizedReturnType = afterMappingReferencesWithFinalizedReturnType; + + // initialize constant mappings as all mappings, but take out the ones that can be contributed to a // parameter mapping. this.mappingsByParameter = new HashMap<>(); this.constantMappings = new ArrayList<>( propertyMappings.size() ); @@ -1830,6 +1878,18 @@ public List getSubclassMappings() { return subclassMappings; } + public String getFinalizedResultName() { + return finalizedResultName; + } + + public List getBeforeMappingReferencesWithFinalizedReturnType() { + return beforeMappingReferencesWithFinalizedReturnType; + } + + public List getAfterMappingReferencesWithFinalizedReturnType() { + return afterMappingReferencesWithFinalizedReturnType; + } + public List propertyMappingsByParameter(Parameter parameter) { // issues: #909 and #1244. FreeMarker has problem getting values from a map when the search key is size or value return mappingsByParameter.getOrDefault( parameter.getName(), Collections.emptyList() ); @@ -1882,6 +1942,12 @@ public Set getImportTypes() { if ( returnTypeBuilder != null ) { types.add( returnTypeBuilder.getOwningType() ); } + for ( LifecycleCallbackMethodReference reference : beforeMappingReferencesWithFinalizedReturnType ) { + types.addAll( reference.getImportTypes() ); + } + for ( LifecycleCallbackMethodReference reference : afterMappingReferencesWithFinalizedReturnType ) { + types.addAll( reference.getImportTypes() ); + } return types; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java index 5b9eda644b..4c59216e09 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java @@ -186,8 +186,8 @@ public String toString() { return returnType + " " + getName() + "(" + join( parameters, ", " ) + ")"; } - private List filterMappingTarget(List methods, - boolean mustHaveMappingTargetParameter) { + protected static List filterMappingTarget( + List methods, boolean mustHaveMappingTargetParameter) { if ( methods == null ) { return Collections.emptyList(); } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 1b402a57f0..a0fbe24b3e 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -21,6 +21,12 @@ + <#list beforeMappingReferencesWithFinalizedReturnType as callback> + <@includeModel object=callback targetBeanName=finalizedResultName targetType=returnType/> + <#if !callback_has_next> + + + <#if !mapNullToDefault> if ( <#list sourceParametersExcludingPrimitives as sourceParam>${sourceParam.name} == null<#if sourceParam_has_next> && ) { return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#if finalizerMethod??>.<@includeModel object=finalizerMethod /><#else>null; @@ -129,7 +135,20 @@ <#if returnType.name != "void"> <#if finalizerMethod??> - return ${resultName}.<@includeModel object=finalizerMethod />; + <#if (afterMappingReferencesWithFinalizedReturnType?size > 0)> + ${returnType.name} ${finalizedResultName} = ${resultName}.<@includeModel object=finalizerMethod />; + + <#list afterMappingReferencesWithFinalizedReturnType as callback> + <#if callback_index = 0> + + + <@includeModel object=callback targetBeanName=finalizedResultName targetType=returnType/> + + + return ${finalizedResultName}; + <#else> + return ${resultName}.<@includeModel object=finalizerMethod />; + <#else> return ${resultName}; diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/BuilderLifecycleCallbacksTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/BuilderLifecycleCallbacksTest.java index e3892f2154..3d5fb7e533 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/BuilderLifecycleCallbacksTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/BuilderLifecycleCallbacksTest.java @@ -43,12 +43,16 @@ public void lifecycleMethodsShouldBeInvoked() { assertThat( context.getInvokedMethods() ) .contains( "beforeWithoutParameters", + "beforeWithTargetType", "beforeWithBuilderTargetType", "beforeWithBuilderTarget", "afterWithoutParameters", "afterWithBuilderTargetType", "afterWithBuilderTarget", - "afterWithBuilderTargetReturningTarget" + "afterWithBuilderTargetReturningTarget", + "afterWithTargetType", + "afterWithTarget", + "afterWithTargetReturningTarget" ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/MappingContext.java b/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/MappingContext.java index 96b9b30db6..079be90a81 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/MappingContext.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/MappingContext.java @@ -74,7 +74,15 @@ public void afterWithBuilderTarget(OrderDto source, @MappingTarget Order.Builder public Order afterWithBuilderTargetReturningTarget(@MappingTarget Order.Builder orderBuilder) { invokedMethods.add( "afterWithBuilderTargetReturningTarget" ); - return orderBuilder.create(); + // return null, so that @AfterMapping methods on the finalized object will be called in the tests + return null; + } + + @AfterMapping + public Order afterWithTargetReturningTarget(@MappingTarget Order order) { + invokedMethods.add( "afterWithTargetReturningTarget" ); + + return order; } public List getInvokedMethods() { From 84c443df9c141d9947253a19facbb952cbda816e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Campanero=20Ortiz?= Date: Wed, 24 May 2023 05:44:36 +0200 Subject: [PATCH 361/597] #3245 Remove redundant null checks in nested properties --- .../model/NestedPropertyMappingMethod.ftl | 9 ++-- .../test/bugs/_1561/Issue1561MapperImpl.java | 9 +--- .../ap/test/bugs/_1685/UserMapperImpl.java | 45 +++---------------- .../ap/test/bugs/_2245/TestMapperImpl.java | 9 +--- .../nestedsource/ArtistToChartEntryImpl.java | 27 ++--------- .../nestedtarget/ChartEntryToArtistImpl.java | 45 +++---------------- .../nestedbeans/mixed/FishTankMapperImpl.java | 27 ++--------- .../ArtistToChartEntryImpl.java | 27 ++--------- .../ChartEntryToArtistImpl.java | 45 +++---------------- 9 files changed, 30 insertions(+), 213 deletions(-) diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl index ce6dab0714..8eabe23a16 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl @@ -7,15 +7,16 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.NestedPropertyMappingMethod" --> <#lt>private <@includeModel object=returnType.typeBound/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { - if ( ${sourceParameter.name} == null ) { - return ${returnType.null}; - } <#list propertyEntries as entry> <#if entry.presenceChecker?? > if ( <#if entry_index != 0>${entry.previousPropertyName} == null || !<@includeModel object=entry.presenceChecker /> ) { return ${returnType.null}; } + <#if !entry_has_next> + return ${entry.previousPropertyName}.${entry.accessorName}; + + <#if entry_has_next> <@includeModel object=entry.type.typeBound/> ${entry.name} = ${entry.previousPropertyName}.${entry.accessorName}; <#if !entry.presenceChecker?? > <#if !entry.type.primitive> @@ -24,8 +25,6 @@ } - <#if !entry_has_next> - return ${entry.name}; } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1561/Issue1561MapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1561/Issue1561MapperImpl.java index dde54d65b7..83cebe2bde 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1561/Issue1561MapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1561/Issue1561MapperImpl.java @@ -61,17 +61,10 @@ protected NestedTarget sourceToNestedTarget(Source source) { } private Stream targetNestedTargetProperties(Target target) { - if ( target == null ) { - return null; - } NestedTarget nestedTarget = target.getNestedTarget(); if ( nestedTarget == null ) { return null; } - Stream properties = nestedTarget.getProperties(); - if ( properties == null ) { - return null; - } - return properties; + return nestedTarget.getProperties(); } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1685/UserMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1685/UserMapperImpl.java index b8d3e9db4b..3c019a0dd8 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1685/UserMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_1685/UserMapperImpl.java @@ -168,77 +168,42 @@ protected ContactDataDTO userToContactDataDTO(User user) { } 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; + return contactDataDTO.getEmail(); } 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; + return contactDataDTO.getPhone(); } 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; + return contactDataDTO.getAddress(); } private List userDTOContactDataDTOPreferences(UserDTO userDTO) { - if ( userDTO == null ) { - return null; - } ContactDataDTO contactDataDTO = userDTO.getContactDataDTO(); if ( contactDataDTO == null ) { return null; } - List preferences = contactDataDTO.getPreferences(); - if ( preferences == null ) { - return null; - } - return preferences; + return contactDataDTO.getPreferences(); } 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; + return contactDataDTO.getSettings(); } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2245/TestMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2245/TestMapperImpl.java index c3ecc02911..b7a18048a0 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2245/TestMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2245/TestMapperImpl.java @@ -34,17 +34,10 @@ public Tenant map(TenantDTO tenant) { } private String tenantInnerId(TenantDTO tenantDTO) { - if ( tenantDTO == null ) { - return null; - } Inner inner = tenantDTO.getInner(); if ( inner == null ) { return null; } - String id = inner.getId(); - if ( id == null ) { - return null; - } - return id; + return inner.getId(); } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java index 09f96b4b3a..40c16c6086 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedsource/ArtistToChartEntryImpl.java @@ -96,24 +96,14 @@ public ChartEntry map(Chart name) { } private String songArtistName(Song song) { - if ( song == null ) { - return null; - } Artist artist = song.getArtist(); if ( artist == null ) { return null; } - String name = artist.getName(); - if ( name == null ) { - return null; - } - return name; + return artist.getName(); } private String songArtistLabelStudioName(Song song) { - if ( song == null ) { - return null; - } Artist artist = song.getArtist(); if ( artist == null ) { return null; @@ -126,17 +116,10 @@ private String songArtistLabelStudioName(Song song) { if ( studio == null ) { return null; } - String name = studio.getName(); - if ( name == null ) { - return null; - } - return name; + return studio.getName(); } private String songArtistLabelStudioCity(Song song) { - if ( song == null ) { - return null; - } Artist artist = song.getArtist(); if ( artist == null ) { return null; @@ -149,10 +132,6 @@ private String songArtistLabelStudioCity(Song song) { if ( studio == null ) { return null; } - String city = studio.getCity(); - if ( city == null ) { - return null; - } - return city; + return studio.getCity(); } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java index 0413ef5b79..cbd125366e 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/constructor/nestedtarget/ChartEntryToArtistImpl.java @@ -132,24 +132,14 @@ protected Song chartEntryToSong(ChartEntry chartEntry) { } private String chartSongTitle(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; } - String title = song.getTitle(); - if ( title == null ) { - return null; - } - return title; + return song.getTitle(); } private String chartSongArtistName(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; @@ -158,17 +148,10 @@ private String chartSongArtistName(Chart chart) { if ( artist == null ) { return null; } - String name = artist.getName(); - if ( name == null ) { - return null; - } - return name; + return artist.getName(); } private String chartSongArtistLabelStudioName(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; @@ -185,17 +168,10 @@ private String chartSongArtistLabelStudioName(Chart chart) { if ( studio == null ) { return null; } - String name = studio.getName(); - if ( name == null ) { - return null; - } - return name; + return studio.getName(); } private String chartSongArtistLabelStudioCity(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; @@ -212,25 +188,14 @@ private String chartSongArtistLabelStudioCity(Chart chart) { if ( studio == null ) { return null; } - String city = studio.getCity(); - if ( city == null ) { - return null; - } - return city; + return studio.getCity(); } private List chartSongPositions(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; } - List positions = song.getPositions(); - if ( positions == null ) { - return null; - } - return positions; + return song.getPositions(); } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java index 3642d15340..dc70a28ce8 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java @@ -159,18 +159,11 @@ protected WaterQualityDto waterQualityToWaterQualityDto(WaterQuality waterQualit } private Ornament sourceInteriorOrnament(FishTank fishTank) { - if ( fishTank == null ) { - return null; - } Interior interior = fishTank.getInterior(); if ( interior == null ) { return null; } - Ornament ornament = interior.getOrnament(); - if ( ornament == null ) { - return null; - } - return ornament; + return interior.getOrnament(); } protected OrnamentDto ornamentToOrnamentDto(Ornament ornament) { @@ -295,18 +288,11 @@ protected Interior fishTankDtoToInterior(FishTankDto fishTankDto) { } private String waterQualityReportDtoOrganisationName(WaterQualityReportDto waterQualityReportDto) { - if ( waterQualityReportDto == null ) { - return null; - } WaterQualityOrganisationDto organisation = waterQualityReportDto.getOrganisation(); if ( organisation == null ) { return null; } - String name = organisation.getName(); - if ( name == null ) { - return null; - } - return name; + return organisation.getName(); } protected WaterQualityReport waterQualityReportDtoToWaterQualityReport(WaterQualityReportDto waterQualityReportDto) { @@ -335,18 +321,11 @@ protected WaterQuality waterQualityDtoToWaterQuality(WaterQualityDto waterQualit } private MaterialTypeDto sourceMaterialMaterialType(FishTankDto fishTankDto) { - if ( fishTankDto == null ) { - return null; - } MaterialDto material = fishTankDto.getMaterial(); if ( material == null ) { return null; } - MaterialTypeDto materialType = material.getMaterialType(); - if ( materialType == null ) { - return null; - } - return materialType; + return material.getMaterialType(); } protected MaterialType materialTypeDtoToMaterialType(MaterialTypeDto materialTypeDto) { diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryImpl.java index 131566ad4f..e43534dc84 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryImpl.java @@ -74,24 +74,14 @@ public ChartEntry map(Chart name) { } private String songArtistName(Song song) { - if ( song == null ) { - return null; - } Artist artist = song.getArtist(); if ( artist == null ) { return null; } - String name = artist.getName(); - if ( name == null ) { - return null; - } - return name; + return artist.getName(); } private String songArtistLabelStudioName(Song song) { - if ( song == null ) { - return null; - } Artist artist = song.getArtist(); if ( artist == null ) { return null; @@ -104,17 +94,10 @@ private String songArtistLabelStudioName(Song song) { if ( studio == null ) { return null; } - String name = studio.getName(); - if ( name == null ) { - return null; - } - return name; + return studio.getName(); } private String songArtistLabelStudioCity(Song song) { - if ( song == null ) { - return null; - } Artist artist = song.getArtist(); if ( artist == null ) { return null; @@ -127,10 +110,6 @@ private String songArtistLabelStudioCity(Song song) { if ( studio == null ) { return null; } - String city = studio.getCity(); - if ( city == null ) { - return null; - } - return city; + return studio.getCity(); } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedtargetproperties/ChartEntryToArtistImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedtargetproperties/ChartEntryToArtistImpl.java index 8877bd421f..725f5b8efe 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedtargetproperties/ChartEntryToArtistImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedtargetproperties/ChartEntryToArtistImpl.java @@ -198,24 +198,14 @@ protected void chartEntryToSong2(ChartEntry chartEntry, Song mappingTarget) { } private String chartSongTitle(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; } - String title = song.getTitle(); - if ( title == null ) { - return null; - } - return title; + return song.getTitle(); } private String chartSongArtistName(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; @@ -224,17 +214,10 @@ private String chartSongArtistName(Chart chart) { if ( artist == null ) { return null; } - String name = artist.getName(); - if ( name == null ) { - return null; - } - return name; + return artist.getName(); } private String chartSongArtistLabelStudioName(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; @@ -251,17 +234,10 @@ private String chartSongArtistLabelStudioName(Chart chart) { if ( studio == null ) { return null; } - String name = studio.getName(); - if ( name == null ) { - return null; - } - return name; + return studio.getName(); } private String chartSongArtistLabelStudioCity(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; @@ -278,25 +254,14 @@ private String chartSongArtistLabelStudioCity(Chart chart) { if ( studio == null ) { return null; } - String city = studio.getCity(); - if ( city == null ) { - return null; - } - return city; + return studio.getCity(); } private List chartSongPositions(Chart chart) { - if ( chart == null ) { - return null; - } Song song = chart.getSong(); if ( song == null ) { return null; } - List positions = song.getPositions(); - if ( positions == null ) { - return null; - } - return positions; + return song.getPositions(); } } From 51f4e7eba9c65d9078491711d616c7b5b38ecc1a Mon Sep 17 00:00:00 2001 From: Oliver Erhart <8238759+thunderhook@users.noreply.github.com> Date: Wed, 24 May 2023 06:04:13 +0200 Subject: [PATCH 362/597] #3231 Prefer record constructor annotated with `@Default` --- .../org/mapstruct/itest/records/Default.java | 21 ++++++++++++++++++ .../org/mapstruct/itest/records/Task.java | 15 +++++++++++++ .../org/mapstruct/itest/records/TaskDto.java | 20 +++++++++++++++++ .../mapstruct/itest/records/TaskMapper.java | 22 +++++++++++++++++++ .../mapstruct/itest/records/RecordsTest.java | 12 ++++++++++ .../ap/internal/model/BeanMappingMethod.java | 18 ++++++++++++++- 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Default.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Task.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskDto.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskMapper.java diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Default.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Default.java new file mode 100644 index 0000000000..b845bdd730 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Default.java @@ -0,0 +1,21 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Filip Hrisafov + */ +@Documented +@Target(ElementType.CONSTRUCTOR) +@Retention(RetentionPolicy.SOURCE) +public @interface Default { +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Task.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Task.java new file mode 100644 index 0000000000..3f990fc89a --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/Task.java @@ -0,0 +1,15 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +import java.util.List; + +/** + * @author Oliver Erhart + */ +public record Task( String id, Long number ) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskDto.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskDto.java new file mode 100644 index 0000000000..1ba6eb2be9 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskDto.java @@ -0,0 +1,20 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +import java.util.List; + +/** + * @author Oliver Erhart + */ +public record TaskDto(String id, Long number) { + + @Default + TaskDto(String id) { + this( id, 1L ); + } + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskMapper.java new file mode 100644 index 0000000000..8d9da767cd --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/TaskMapper.java @@ -0,0 +1,22 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Oliver Erhart + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface TaskMapper { + + TaskMapper INSTANCE = Mappers.getMapper( TaskMapper.class ); + + TaskDto toRecord(Task source); + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java index e98df8797e..2f77e8d49f 100644 --- a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java @@ -83,4 +83,16 @@ public void shouldMapIntoMemberRecord() { assertThat( value.isActive() ).isEqualTo( false ); assertThat( value.premium() ).isEqualTo( true ); } + + @Test + public void shouldUseDefaultConstructor() { + Task entity = new Task( "some-id", 1000L ); + + TaskDto value = TaskMapper.INSTANCE.toRecord( entity ); + + assertThat( value ).isNotNull(); + assertThat( value.id() ).isEqualTo( "some-id" ); + assertThat( value.number() ).isEqualTo( 1L ); + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index b6afce74d2..9d0acea91b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -791,7 +791,23 @@ private ConstructorAccessor getConstructorAccessor(Type type) { } if ( type.isRecord() ) { - // If the type is a record then just get the record components and use then + + List constructors = ElementFilter.constructorsIn( type.getTypeElement() + .getEnclosedElements() ); + + for ( ExecutableElement constructor : constructors ) { + if ( constructor.getModifiers().contains( Modifier.PRIVATE ) ) { + continue; + } + + // prefer constructor annotated with @Default + if ( hasDefaultAnnotationFromAnyPackage( constructor ) ) { + return getConstructorAccessor( type, constructor ); + } + } + + + // Other than that, just get the record components and use them List recordComponents = type.getRecordComponents(); List parameterBindings = new ArrayList<>( recordComponents.size() ); Map constructorAccessors = new LinkedHashMap<>(); From c2eed45df12802cacaec158420656431c061471c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 21 May 2023 23:01:58 +0200 Subject: [PATCH 363/597] #3126 Apply target this references in the BeanMappingMethod --- .../ap/internal/model/BeanMappingMethod.java | 14 ++- .../model/beanmapping/MappingReferences.java | 30 +----- .../ap/test/bugs/_3126/Issue3126Mapper.java | 92 +++++++++++++++++++ .../ap/test/bugs/_3126/Issue3126Test.java | 28 ++++++ .../nestedbeans/mixed/FishTankMapperImpl.java | 41 +-------- 5 files changed, 137 insertions(+), 68 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 9d0acea91b..361d04b390 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -117,6 +117,7 @@ public static class Builder extends AbstractMappingMethodBuilder> unprocessedDefinedTargets = new LinkedHashMap<>(); private MappingReferences mappingReferences; + private List targetThisReferences; private MethodReference factoryMethod; private boolean hasFactoryMethod; @@ -1068,6 +1069,14 @@ private boolean handleDefinedMappings(Type resultTypeToMap) { for ( MappingReference mapping : mappingReferences.getMappingReferences() ) { if ( mapping.isValid() ) { String target = mapping.getTargetReference().getShallowestPropertyName(); + if ( target == null ) { + // When the shallowest property name is null then it is for @Mapping(target = ".") + if ( this.targetThisReferences == null ) { + this.targetThisReferences = new ArrayList<>(); + } + this.targetThisReferences.add( mapping ); + continue; + } if ( !handledTargets.contains( target ) ) { if ( handleDefinedMapping( mapping, resultTypeToMap, handledTargets ) ) { errorOccurred = true; @@ -1435,8 +1444,11 @@ else if ( mapping.getJavaExpression() != null ) { * duplicates will be handled by {@link #applyPropertyNameBasedMapping(List)} */ private void applyTargetThisMapping() { + if ( this.targetThisReferences == null ) { + return; + } Set handledTargetProperties = new HashSet<>(); - for ( MappingReference targetThis : mappingReferences.getTargetThisReferences() ) { + for ( MappingReference targetThis : this.targetThisReferences ) { // handle all prior unprocessed target properties, but let duplicates fall through List sourceRefs = targetThis diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java index 569c75e0eb..ced945bb90 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java @@ -5,10 +5,8 @@ */ package org.mapstruct.ap.internal.model.beanmapping; -import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; -import java.util.List; import java.util.Objects; import java.util.Set; @@ -23,7 +21,6 @@ public class MappingReferences { private static final MappingReferences EMPTY = new MappingReferences( Collections.emptySet(), false ); private final Set mappingReferences; - private final List targetThisReferences; private final boolean restrictToDefinedMappings; private final boolean forForgedMethods; @@ -38,7 +35,6 @@ public static MappingReferences forSourceMethod(SourceMethod sourceMethod, TypeFactory typeFactory) { Set references = new LinkedHashSet<>(); - List targetThisReferences = new ArrayList<>( ); for ( MappingOptions mapping : sourceMethod.getOptions().getMappings() ) { @@ -61,30 +57,16 @@ public static MappingReferences forSourceMethod(SourceMethod sourceMethod, // add when inverse is also valid MappingReference mappingReference = new MappingReference( mapping, targetReference, sourceReference ); if ( isValidWhenInversed( mappingReference ) ) { - if ( ".".equals( mapping.getTargetName() ) ) { - targetThisReferences.add( mappingReference ); - } - else { - references.add( mappingReference ); - } + references.add( mappingReference ); } } - return new MappingReferences( references, targetThisReferences, false ); - } - - public MappingReferences(Set mappingReferences, List targetThisReferences, - boolean restrictToDefinedMappings) { - this.mappingReferences = mappingReferences; - this.restrictToDefinedMappings = restrictToDefinedMappings; - this.forForgedMethods = restrictToDefinedMappings; - this.targetThisReferences = targetThisReferences; + return new MappingReferences( references, false ); } public MappingReferences(Set mappingReferences, boolean restrictToDefinedMappings) { this.mappingReferences = mappingReferences; this.restrictToDefinedMappings = restrictToDefinedMappings; this.forForgedMethods = restrictToDefinedMappings; - this.targetThisReferences = Collections.emptyList(); } public MappingReferences(Set mappingReferences, boolean restrictToDefinedMappings, @@ -92,7 +74,6 @@ public MappingReferences(Set mappingReferences, boolean restri this.mappingReferences = mappingReferences; this.restrictToDefinedMappings = restrictToDefinedMappings; this.forForgedMethods = forForgedMethods; - this.targetThisReferences = Collections.emptyList(); } public Set getMappingReferences() { @@ -136,10 +117,6 @@ public boolean hasNestedTargetReferences() { return false; } - public List getTargetThisReferences() { - return targetThisReferences; - } - @Override public boolean equals(Object o) { if ( this == o ) { @@ -160,9 +137,6 @@ public boolean equals(Object o) { if ( !Objects.equals( mappingReferences, that.mappingReferences ) ) { return false; } - if ( Objects.equals( targetThisReferences, that.targetThisReferences ) ) { - return false; - } return true; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Mapper.java new file mode 100644 index 0000000000..e680db9317 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Mapper.java @@ -0,0 +1,92 @@ +/* + * 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._3126; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION) +public interface Issue3126Mapper { + + Issue3126Mapper INSTANCE = Mappers.getMapper( Issue3126Mapper.class ); + + @SubclassMapping(target = HomeAddressDto.class, source = HomeAddress.class) + @SubclassMapping(target = OfficeAddressDto.class, source = OfficeAddress.class) + @Mapping(target = ".", source = "auditable") + AddressDto map(Address address); + + interface AddressDto { + + } + + class HomeAddressDto implements AddressDto { + private String createdBy; + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + } + + class OfficeAddressDto implements AddressDto { + private String createdBy; + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + } + + class HomeAddress extends Address { + + public HomeAddress(Auditable auditable) { + super( auditable ); + } + } + + class OfficeAddress extends Address { + + public OfficeAddress(Auditable auditable) { + super( auditable ); + } + } + + abstract class Address { + + private final Auditable auditable; + + protected Address(Auditable auditable) { + this.auditable = auditable; + } + + public Auditable getAuditable() { + return auditable; + } + } + + class Auditable { + + private final String createdBy; + + public Auditable(String createdBy) { + this.createdBy = createdBy; + } + + public String getCreatedBy() { + return createdBy; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Test.java new file mode 100644 index 0000000000..35e3eb873c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Test.java @@ -0,0 +1,28 @@ +/* + * 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._3126; + +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; + +@IssueKey("3126") +@WithClasses(Issue3126Mapper.class) +class Issue3126Test { + + @ProcessorTest + void shouldCompile() { + Issue3126Mapper.Auditable auditable = new Issue3126Mapper.Auditable( "home-user" ); + Issue3126Mapper.Address address = new Issue3126Mapper.HomeAddress( auditable ); + Issue3126Mapper.AddressDto addressDto = Issue3126Mapper.INSTANCE.map( address ); + + assertThat( addressDto ).isInstanceOfSatisfying( Issue3126Mapper.HomeAddressDto.class, homeAddress -> { + assertThat( homeAddress.getCreatedBy() ).isEqualTo( "home-user" ); + } ); + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java index dc70a28ce8..b98223fa6f 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java @@ -57,9 +57,9 @@ public FishTankDto mapAsWell(FishTank source) { FishTankDto fishTankDto = new FishTankDto(); - fishTankDto.setFish( fishToFishDto1( source.getFish() ) ); + fishTankDto.setFish( fishToFishDto( source.getFish() ) ); fishTankDto.setMaterial( fishTankToMaterialDto1( source ) ); - fishTankDto.setQuality( waterQualityToWaterQualityDto1( source.getQuality() ) ); + fishTankDto.setQuality( waterQualityToWaterQualityDto( source.getQuality() ) ); fishTankDto.setOrnament( ornamentToOrnamentDto( sourceInteriorOrnament( source ) ) ); fishTankDto.setPlant( waterPlantToWaterPlantDto( source.getPlant() ) ); fishTankDto.setName( source.getName() ); @@ -190,18 +190,6 @@ protected WaterPlantDto waterPlantToWaterPlantDto(WaterPlant waterPlant) { return waterPlantDto; } - protected FishDto fishToFishDto1(Fish fish) { - if ( fish == null ) { - return null; - } - - FishDto fishDto = new FishDto(); - - fishDto.setKind( fish.getType() ); - - return fishDto; - } - protected MaterialDto fishTankToMaterialDto1(FishTank fishTank) { if ( fishTank == null ) { return null; @@ -226,31 +214,6 @@ protected WaterQualityOrganisationDto waterQualityReportToWaterQualityOrganisati return waterQualityOrganisationDto; } - protected WaterQualityReportDto waterQualityReportToWaterQualityReportDto1(WaterQualityReport waterQualityReport) { - if ( waterQualityReport == null ) { - return null; - } - - WaterQualityReportDto waterQualityReportDto = new WaterQualityReportDto(); - - waterQualityReportDto.setOrganisation( waterQualityReportToWaterQualityOrganisationDto1( waterQualityReport ) ); - waterQualityReportDto.setVerdict( waterQualityReport.getVerdict() ); - - return waterQualityReportDto; - } - - protected WaterQualityDto waterQualityToWaterQualityDto1(WaterQuality waterQuality) { - if ( waterQuality == null ) { - return null; - } - - WaterQualityDto waterQualityDto = new WaterQualityDto(); - - waterQualityDto.setReport( waterQualityReportToWaterQualityReportDto1( waterQuality.getReport() ) ); - - return waterQualityDto; - } - protected Fish fishDtoToFish(FishDto fishDto) { if ( fishDto == null ) { return null; From 62d1bd3490974468494853a46ec79f6bd5904bdd Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 27 May 2023 15:04:34 +0200 Subject: [PATCH 364/597] #3280 Refactor method selection and use a context to be able to more easily access information --- .../model/LifecycleMethodResolver.java | 12 +- .../model/ObjectFactoryMethodResolver.java | 10 +- .../model/PresenceCheckMethodResolver.java | 12 +- .../selector/CreateOrUpdateSelector.java | 11 +- .../selector/FactoryParameterSelector.java | 9 +- .../source/selector/InheritanceSelector.java | 14 +- .../source/selector/MethodFamilySelector.java | 10 +- .../model/source/selector/MethodSelector.java | 15 +- .../source/selector/MethodSelectors.java | 28 +- .../MostSpecificResultTypeSelector.java | 8 +- .../source/selector/QualifierSelector.java | 9 +- .../source/selector/SelectionContext.java | 240 ++++++++++++++++++ .../source/selector/SourceRhsSelector.java | 8 +- .../source/selector/TargetTypeSelector.java | 10 +- .../model/source/selector/TypeSelector.java | 112 +------- .../selector/XmlElementDeclSelector.java | 12 +- .../creation/MappingResolverImpl.java | 10 +- 17 files changed, 302 insertions(+), 228 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java index 8b44dee254..b713fa5f0c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java @@ -6,7 +6,6 @@ package org.mapstruct.ap.internal.model; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -19,7 +18,7 @@ import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; -import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; +import org.mapstruct.ap.internal.model.source.selector.SelectionContext; /** * Factory for creating lists of appropriate {@link LifecycleCallbackMethodReference}s @@ -134,15 +133,12 @@ private static List collectLifecycleCallbackMe MappingBuilderContext ctx, Set existingVariableNames) { MethodSelectors selectors = - new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() ); + new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager() ); List> matchingMethods = selectors.getMatchingMethods( - method, callbackMethods, - Collections.emptyList(), - targetType, - method.getResultType(), - SelectionCriteria.forLifecycleMethods( selectionParameters ) ); + SelectionContext.forLifecycleMethods( method, targetType, selectionParameters, ctx.getTypeFactory() ) + ); return toLifecycleCallbackMethodRefs( method, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index aded4cbe5f..4cf653b46b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -23,7 +23,7 @@ import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; -import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; +import org.mapstruct.ap.internal.model.source.selector.SelectionContext; import org.mapstruct.ap.internal.util.Message; /** @@ -126,15 +126,11 @@ public static List> getMatchingFactoryMethods( Meth MappingBuilderContext ctx) { MethodSelectors selectors = - new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() ); + new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager() ); return selectors.getMatchingMethods( - method, getAllAvailableMethods( method, ctx.getSourceModel() ), - java.util.Collections.emptyList(), - alternativeTarget, - alternativeTarget, - SelectionCriteria.forFactoryMethods( selectionParameters ) + SelectionContext.forFactoryMethods( method, alternativeTarget, selectionParameters, ctx.getTypeFactory() ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java index 9d4910bdce..a5c873743d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java @@ -6,20 +6,18 @@ package org.mapstruct.ap.internal.model; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.PresenceCheck; -import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; -import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; +import org.mapstruct.ap.internal.model.source.selector.SelectionContext; import org.mapstruct.ap.internal.util.Message; /** @@ -60,18 +58,12 @@ private static SelectedMethod findMatchingPresenceCheckMethod( MethodSelectors selectors = new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), - ctx.getTypeFactory(), ctx.getMessager() ); - Type booleanType = ctx.getTypeFactory().getType( Boolean.class ); List> matchingMethods = selectors.getMatchingMethods( - method, getAllAvailableMethods( method, ctx.getSourceModel() ), - Collections.emptyList(), - booleanType, - booleanType, - SelectionCriteria.forPresenceCheckMethods( selectionParameters ) + SelectionContext.forPresenceCheckMethods( method, selectionParameters, ctx.getTypeFactory() ) ); if ( matchingMethods.isEmpty() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java index ed1c72ab2a..03a671de57 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.List; -import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; /** @@ -29,13 +28,9 @@ public class CreateOrUpdateSelector implements MethodSelector { @Override - public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type mappingTargetType, - Type returnType, - SelectionCriteria criteria) { - + public List> getMatchingMethods(List> methods, + SelectionContext context) { + SelectionCriteria criteria = context.getSelectionCriteria(); if ( criteria.isLifecycleCallbackRequired() || criteria.isObjectFactoryRequired() || criteria.isPresenceCheckRequired() ) { return methods; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/FactoryParameterSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/FactoryParameterSelector.java index 41d37e8b69..50896195f1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/FactoryParameterSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/FactoryParameterSelector.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.List; -import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; /** @@ -21,11 +20,9 @@ public class FactoryParameterSelector implements MethodSelector { @Override - public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type mappingTargetType, Type returnType, - SelectionCriteria criteria) { + public List> getMatchingMethods(List> methods, + SelectionContext context) { + SelectionCriteria criteria = context.getSelectionCriteria(); if ( !criteria.isObjectFactoryRequired() || methods.size() <= 1 ) { return methods; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java index a624c1accb..297dcaa4a3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java @@ -22,18 +22,14 @@ public class InheritanceSelector implements MethodSelector { @Override - public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type mappingTargetType, Type returnType, - SelectionCriteria criteria) { + public List> getMatchingMethods(List> methods, + SelectionContext context) { - if ( sourceTypes.size() != 1 ) { + Type sourceType = context.getSourceType(); + if ( sourceType == null ) { return methods; } - Type singleSourceType = first( sourceTypes ); - List> candidatesWithBestMatchingSourceType = new ArrayList<>(); int bestMatchingSourceTypeDistance = Integer.MAX_VALUE; @@ -41,7 +37,7 @@ public List> getMatchingMethods(Method mapp for ( SelectedMethod method : methods ) { Parameter singleSourceParam = first( method.getMethod().getSourceParameters() ); - int sourceTypeDistance = singleSourceType.distanceTo( singleSourceParam.getType() ); + int sourceTypeDistance = sourceType.distanceTo( singleSourceParam.getType() ); bestMatchingSourceTypeDistance = addToCandidateListIfMinimal( candidatesWithBestMatchingSourceType, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java index 37502ec56d..d81269421f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodFamilySelector.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.List; -import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; /** @@ -20,12 +19,9 @@ public class MethodFamilySelector implements MethodSelector { @Override - public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type mappingTargetType, - Type returnType, - SelectionCriteria criteria) { + public List> getMatchingMethods(List> methods, + SelectionContext context) { + SelectionCriteria criteria = context.getSelectionCriteria(); List> result = new ArrayList<>( methods.size() ); for ( SelectedMethod method : methods ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelector.java index 86d35bb3aa..375d5a9bf8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelector.java @@ -7,7 +7,6 @@ import java.util.List; -import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; /** @@ -23,18 +22,10 @@ interface MethodSelector { * Selects those methods which match the given types and other criteria * * @param either SourceMethod or BuiltInMethod - * @param mappingMethod mapping method, defined in Mapper for which this selection is carried out * @param candidates list of available methods - * @param sourceTypes parameter type(s) that should be matched - * @param mappingTargetType mappingTargetType that should be matched - * @param returnType return type that should be matched - * @param criteria criteria used in the selection process + * @param context the context for the matching * @return list of methods that passes the matching process */ - List> getMatchingMethods(Method mappingMethod, - List> candidates, - List sourceTypes, - Type mappingTargetType, - Type returnType, - SelectionCriteria criteria); + List> getMatchingMethods(List> candidates, + SelectionContext context); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java index 74fa1a33a3..c14729a90f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java @@ -9,8 +9,6 @@ import java.util.Arrays; import java.util.List; -import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; @@ -25,11 +23,11 @@ public class MethodSelectors { private final List selectors; - public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils, TypeFactory typeFactory, + public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils, FormattingMessager messager) { selectors = Arrays.asList( new MethodFamilySelector(), - new TypeSelector( typeFactory, messager ), + new TypeSelector( messager ), new QualifierSelector( typeUtils, elementUtils ), new TargetTypeSelector( typeUtils ), new JavaxXmlElementDeclSelector( typeUtils ), @@ -46,20 +44,12 @@ public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils, TypeFacto * Selects those methods which match the given types and other criteria * * @param either SourceMethod or BuiltInMethod - * @param mappingMethod mapping method, defined in Mapper for which this selection is carried out * @param methods list of available methods - * @param sourceTypes parameter type(s) that should be matched - * @param mappingTargetType the mapping target type that should be matched - * @param returnType return type that should be matched - * @param criteria criteria used in the selection process + * @param context the selection context that should be used in the matching process * @return list of methods that passes the matching process */ - public List> getMatchingMethods(Method mappingMethod, - List methods, - List sourceTypes, - Type mappingTargetType, - Type returnType, - SelectionCriteria criteria) { + public List> getMatchingMethods(List methods, + SelectionContext context) { List> candidates = new ArrayList<>( methods.size() ); for ( T method : methods ) { @@ -67,13 +57,7 @@ public List> getMatchingMethods(Method mapp } for ( MethodSelector selector : selectors ) { - candidates = selector.getMatchingMethods( - mappingMethod, - candidates, - sourceTypes, - mappingTargetType, - returnType, - criteria ); + candidates = selector.getMatchingMethods( candidates, context ); } return candidates; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java index 6e86a30bc7..23af0a55f9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java @@ -17,10 +17,10 @@ public class MostSpecificResultTypeSelector implements MethodSelector { @Override - public List> getMatchingMethods(Method mappingMethod, - List> candidates, - List sourceTypes, Type mappingTargetType, - Type returnType, SelectionCriteria criteria) { + public List> getMatchingMethods(List> candidates, + SelectionContext context) { + SelectionCriteria criteria = context.getSelectionCriteria(); + Type mappingTargetType = context.getMappingTargetType(); if ( candidates.size() < 2 || !criteria.isForMapping() || criteria.getQualifyingResultType() != null) { return candidates; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java index 210e462e3e..f2f5b591d6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/QualifierSelector.java @@ -49,12 +49,9 @@ public QualifierSelector(TypeUtils typeUtils, ElementUtils elementUtils ) { } @Override - public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type mappingTargetType, - Type returnType, - SelectionCriteria criteria) { + public List> getMatchingMethods(List> methods, + SelectionContext context) { + SelectionCriteria criteria = context.getSelectionCriteria(); int numberOfQualifiersToMatch = 0; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java new file mode 100644 index 0000000000..9e82dae25d --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java @@ -0,0 +1,240 @@ +/* + * 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.internal.model.source.selector; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.ParameterBinding; +import org.mapstruct.ap.internal.model.common.SourceRHS; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.model.source.Method; +import org.mapstruct.ap.internal.model.source.SelectionParameters; + +/** + * Context passed to the selectors to get the information they need. + * + * @author Filip Hrisafov + */ +public class SelectionContext { + + private final Type sourceType; + private final SelectionCriteria selectionCriteria; + private final Method mappingMethod; + private final Type mappingTargetType; + private final Type returnType; + private final Supplier> parameterBindingsProvider; + private List parameterBindings; + + private SelectionContext(Type sourceType, SelectionCriteria selectionCriteria, Method mappingMethod, + Type mappingTargetType, Type returnType, + Supplier> parameterBindingsProvider) { + this.sourceType = sourceType; + this.selectionCriteria = selectionCriteria; + this.mappingMethod = mappingMethod; + this.mappingTargetType = mappingTargetType; + this.returnType = returnType; + this.parameterBindingsProvider = parameterBindingsProvider; + } + + /** + * @return the source type that should be matched + */ + public Type getSourceType() { + return sourceType; + } + + /** + * @return the criteria used in the selection process + */ + public SelectionCriteria getSelectionCriteria() { + return selectionCriteria; + } + + /** + * @return the mapping target type that should be matched + */ + public Type getMappingTargetType() { + return mappingTargetType; + } + + /** + * @return the return type that should be matched + */ + public Type getReturnType() { + return returnType; + } + + /** + * @return the available parameter bindings for the matching + */ + public List getAvailableParameterBindings() { + if ( this.parameterBindings == null ) { + this.parameterBindings = this.parameterBindingsProvider.get(); + } + return parameterBindings; + } + + /** + * @return the mapping method, defined in Mapper for which this selection is carried out + */ + public Method getMappingMethod() { + return mappingMethod; + } + + public static SelectionContext forMappingMethods(Method mappingMethod, Type source, Type target, + SelectionCriteria criteria, TypeFactory typeFactory) { + return new SelectionContext( + source, + criteria, + mappingMethod, + target, + target, + () -> getAvailableParameterBindingsFromSourceType( + source, + target, + mappingMethod, + typeFactory + ) + ); + } + + public static SelectionContext forLifecycleMethods(Method mappingMethod, Type targetType, + SelectionParameters selectionParameters, + TypeFactory typeFactory) { + SelectionCriteria criteria = SelectionCriteria.forLifecycleMethods( selectionParameters ); + return new SelectionContext( + null, + criteria, + mappingMethod, + targetType, + mappingMethod.getResultType(), + () -> getAvailableParameterBindingsFromMethod( + mappingMethod, + targetType, + criteria.getSourceRHS(), + typeFactory + ) + ); + } + + public static SelectionContext forFactoryMethods(Method mappingMethod, Type alternativeTarget, + SelectionParameters selectionParameters, + TypeFactory typeFactory) { + SelectionCriteria criteria = SelectionCriteria.forFactoryMethods( selectionParameters ); + return new SelectionContext( + null, + criteria, + mappingMethod, + alternativeTarget, + alternativeTarget, + () -> getAvailableParameterBindingsFromMethod( + mappingMethod, + alternativeTarget, + criteria.getSourceRHS(), + typeFactory + ) + ); + } + + public static SelectionContext forPresenceCheckMethods(Method mappingMethod, + SelectionParameters selectionParameters, + TypeFactory typeFactory) { + SelectionCriteria criteria = SelectionCriteria.forPresenceCheckMethods( selectionParameters ); + Type booleanType = typeFactory.getType( Boolean.class ); + return new SelectionContext( + null, + criteria, + mappingMethod, + booleanType, + booleanType, + () -> getAvailableParameterBindingsFromMethod( + mappingMethod, + booleanType, + criteria.getSourceRHS(), + typeFactory + ) + ); + } + + private static List getAvailableParameterBindingsFromMethod(Method method, Type targetType, + SourceRHS sourceRHS, + TypeFactory typeFactory) { + List availableParams = new ArrayList<>( method.getParameters().size() + 3 ); + + if ( sourceRHS != null ) { + availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); + availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) ); + } + else { + availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); + } + + addTargetRelevantBindings( availableParams, targetType, typeFactory ); + + return availableParams; + } + + private static List getAvailableParameterBindingsFromSourceType(Type sourceType, + Type targetType, + Method mappingMethod, + TypeFactory typeFactory) { + + List availableParams = new ArrayList<>(); + + availableParams.add( ParameterBinding.forSourceTypeBinding( sourceType ) ); + + for ( Parameter param : mappingMethod.getParameters() ) { + if ( param.isMappingContext() ) { + availableParams.add( ParameterBinding.fromParameter( param ) ); + } + } + + addTargetRelevantBindings( availableParams, targetType, typeFactory ); + + return availableParams; + } + + /** + * Adds default parameter bindings for the mapping-target and target-type if not already available. + * + * @param availableParams Already available params, new entries will be added to this list + * @param targetType Target type + */ + private static void addTargetRelevantBindings(List availableParams, Type targetType, + TypeFactory typeFactory) { + boolean mappingTargetAvailable = false; + boolean targetTypeAvailable = false; + boolean targetPropertyNameAvailable = false; + + // search available parameter bindings if mapping-target and/or target-type is available + for ( ParameterBinding pb : availableParams ) { + if ( pb.isMappingTarget() ) { + mappingTargetAvailable = true; + } + else if ( pb.isTargetType() ) { + targetTypeAvailable = true; + } + else if ( pb.isTargetPropertyName() ) { + targetPropertyNameAvailable = true; + } + } + + if ( !mappingTargetAvailable ) { + availableParams.add( ParameterBinding.forMappingTargetBinding( targetType ) ); + } + if ( !targetTypeAvailable ) { + availableParams.add( ParameterBinding.forTargetTypeBinding( typeFactory.classTypeOf( targetType ) ) ); + } + if ( !targetPropertyNameAvailable ) { + availableParams.add( ParameterBinding.forTargetPropertyNameBinding( typeFactory.getType( String.class ) ) ); + } + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java index 930ce2d403..fb797f5808 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java @@ -9,7 +9,6 @@ import java.util.List; import org.mapstruct.ap.internal.model.common.ParameterBinding; -import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; /** @@ -20,10 +19,9 @@ public class SourceRhsSelector implements MethodSelector { @Override - public List> getMatchingMethods(Method mappingMethod, - List> candidates, - List sourceTypes, Type mappingTargetType, - Type returnType, SelectionCriteria criteria) { + public List> getMatchingMethods(List> candidates, + SelectionContext context) { + SelectionCriteria criteria = context.getSelectionCriteria(); if ( candidates.size() < 2 || criteria.getSourceRHS() == null ) { return candidates; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java index 38a907aedb..0afae641db 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TargetTypeSelector.java @@ -11,7 +11,6 @@ import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.util.TypeUtils; -import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; /** @@ -31,12 +30,9 @@ public TargetTypeSelector( TypeUtils typeUtils ) { } @Override - public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type mappingTargetType, - Type returnType, - SelectionCriteria criteria) { + public List> getMatchingMethods(List> methods, + SelectionContext context) { + SelectionCriteria criteria = context.getSelectionCriteria(); TypeMirror qualifyingTypeMirror = criteria.getQualifyingResultType(); if ( qualifyingTypeMirror != null && !criteria.isLifecycleCallbackRequired() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java index 8acdd327c4..24275f594b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java @@ -11,9 +11,7 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.ParameterBinding; -import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.MethodMatcher; import org.mapstruct.ap.internal.util.FormattingMessager; @@ -29,44 +27,24 @@ */ public class TypeSelector implements MethodSelector { - private TypeFactory typeFactory; private FormattingMessager messager; - public TypeSelector(TypeFactory typeFactory, FormattingMessager messager) { - this.typeFactory = typeFactory; + public TypeSelector(FormattingMessager messager) { this.messager = messager; } @Override - public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type mappingTargetType, - Type returnType, - SelectionCriteria criteria) { - + public List> getMatchingMethods(List> methods, + SelectionContext context) { if ( methods.isEmpty() ) { return methods; } + Type returnType = context.getReturnType(); + List> result = new ArrayList<>(); - List availableBindings; - if ( sourceTypes.isEmpty() ) { - // if no source types are given, we have a factory or lifecycle method - availableBindings = getAvailableParameterBindingsFromMethod( - mappingMethod, - mappingTargetType, - criteria.getSourceRHS() - ); - } - else { - availableBindings = getAvailableParameterBindingsFromSourceTypes( - sourceTypes, - mappingTargetType, - mappingMethod - ); - } + List availableBindings = context.getAvailableParameterBindings(); for ( SelectedMethod method : methods ) { List> parameterBindingPermutations = @@ -74,7 +52,7 @@ public List> getMatchingMethods(Method mapp if ( parameterBindingPermutations != null ) { SelectedMethod matchingMethod = - getMatchingParameterBinding( returnType, mappingMethod, method, parameterBindingPermutations ); + getMatchingParameterBinding( returnType, context, method, parameterBindingPermutations ); if ( matchingMethod != null ) { result.add( matchingMethod ); @@ -84,80 +62,8 @@ public List> getMatchingMethods(Method mapp return result; } - private List getAvailableParameterBindingsFromMethod(Method method, Type targetType, - SourceRHS sourceRHS) { - List availableParams = new ArrayList<>( method.getParameters().size() + 3 ); - - if ( sourceRHS != null ) { - availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); - availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) ); - } - else { - availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); - } - - addTargetRelevantBindings( availableParams, targetType ); - - return availableParams; - } - - private List getAvailableParameterBindingsFromSourceTypes(List sourceTypes, - Type targetType, Method mappingMethod) { - - List availableParams = new ArrayList<>( sourceTypes.size() + 2 ); - - for ( Type sourceType : sourceTypes ) { - availableParams.add( ParameterBinding.forSourceTypeBinding( sourceType ) ); - } - - for ( Parameter param : mappingMethod.getParameters() ) { - if ( param.isMappingContext() ) { - availableParams.add( ParameterBinding.fromParameter( param ) ); - } - } - - addTargetRelevantBindings( availableParams, targetType ); - - return availableParams; - } - - /** - * Adds default parameter bindings for the mapping-target and target-type if not already available. - * - * @param availableParams Already available params, new entries will be added to this list - * @param targetType Target type - */ - private void addTargetRelevantBindings(List availableParams, Type targetType) { - boolean mappingTargetAvailable = false; - boolean targetTypeAvailable = false; - boolean targetPropertyNameAvailable = false; - - // search available parameter bindings if mapping-target and/or target-type is available - for ( ParameterBinding pb : availableParams ) { - if ( pb.isMappingTarget() ) { - mappingTargetAvailable = true; - } - else if ( pb.isTargetType() ) { - targetTypeAvailable = true; - } - else if ( pb.isTargetPropertyName() ) { - targetPropertyNameAvailable = true; - } - } - - if ( !mappingTargetAvailable ) { - availableParams.add( ParameterBinding.forMappingTargetBinding( targetType ) ); - } - if ( !targetTypeAvailable ) { - availableParams.add( ParameterBinding.forTargetTypeBinding( typeFactory.classTypeOf( targetType ) ) ); - } - if ( !targetPropertyNameAvailable ) { - availableParams.add( ParameterBinding.forTargetPropertyNameBinding( typeFactory.getType( String.class ) ) ); - } - } - private SelectedMethod getMatchingParameterBinding(Type returnType, - Method mappingMethod, SelectedMethod selectedMethodInfo, + SelectionContext context, SelectedMethod selectedMethodInfo, List> parameterAssignmentVariants) { List> matchingParameterAssignmentVariants = new ArrayList<>( @@ -200,7 +106,7 @@ else if ( matchingParameterAssignmentVariants.size() == 1 ) { messager.printMessage( selectedMethod.getExecutable(), Message.LIFECYCLEMETHOD_AMBIGUOUS_PARAMETERS, - mappingMethod + context.getMappingMethod() ); return null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java index 91b4b5ca10..a32cb250d5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java @@ -45,18 +45,16 @@ abstract class XmlElementDeclSelector implements MethodSelector { } @Override - public List> getMatchingMethods(Method mappingMethod, - List> methods, - List sourceTypes, - Type mappingTargetType, - Type returnType, - SelectionCriteria criteria) { + public List> getMatchingMethods(List> methods, + SelectionContext context) { + Type resultType = context.getMappingMethod().getResultType(); + String targetPropertyName = context.getSelectionCriteria().getTargetPropertyName(); List> nameMatches = new ArrayList<>(); List> scopeMatches = new ArrayList<>(); List> nameAndScopeMatches = new ArrayList<>(); XmlElementRefInfo xmlElementRefInfo = - findXmlElementRef( mappingMethod.getResultType(), criteria.getTargetPropertyName() ); + findXmlElementRef( resultType, targetPropertyName ); for ( SelectedMethod candidate : methods ) { if ( !( candidate.getMethod() instanceof SourceMethod ) ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 0c8e2cc4c4..586465d5f1 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.internal.processor.creation; -import static java.util.Collections.singletonList; import static org.mapstruct.ap.internal.util.Collections.first; import static org.mapstruct.ap.internal.util.Collections.firstKey; import static org.mapstruct.ap.internal.util.Collections.firstValue; @@ -57,6 +56,7 @@ import org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod; import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; +import org.mapstruct.ap.internal.model.source.selector.SelectionContext; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.internal.util.ElementUtils; @@ -116,7 +116,7 @@ public MappingResolverImpl(FormattingMessager messager, ElementUtils elementUtil this.conversions = new Conversions( typeFactory ); this.builtInMethods = new BuiltInMappingMethods( typeFactory ); - this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, typeFactory, messager ); + this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, messager ); this.verboseLogging = verboseLogging; } @@ -491,12 +491,8 @@ private boolean isUpdateMethodForMapping(Method methodCandidate) { private List> getBestMatch(List methods, Type source, Type target) { return methodSelectors.getMatchingMethods( - mappingMethod, methods, - singletonList( source ), - target, - target, - selectionCriteria + SelectionContext.forMappingMethods( mappingMethod, source, target, selectionCriteria, typeFactory ) ); } From d075d9a5b69a2377069dc0a9de86613dff8c2619 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 24 May 2023 06:09:57 +0200 Subject: [PATCH 365/597] Upgrade Freemarker to 2.3.32 --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 6e9c2836b6..f9531590f6 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -109,7 +109,7 @@ org.freemarker freemarker - 2.3.31 + 2.3.32 org.assertj From 86919c637f228ec3a668f8140b9b68e2092f5189 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 28 May 2023 09:55:40 +0200 Subject: [PATCH 366/597] #3144 Map to Bean should only be possible for single source mappings and if explicitly used in multi source mappings --- .../ap/internal/model/BeanMappingMethod.java | 16 +++-- .../NestedTargetPropertyMappingHolder.java | 5 +- .../model/beanmapping/SourceReference.java | 23 +++++-- .../ap/internal/model/common/Type.java | 4 +- .../ap/test/bugs/_3144/Issue3144Mapper.java | 66 +++++++++++++++++++ .../ap/test/bugs/_3144/Issue3144Test.java | 63 ++++++++++++++++++ .../MapToBeanUsingMappingMethodMapper.java | 2 +- 7 files changed, 165 insertions(+), 14 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3144/Issue3144Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3144/Issue3144Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 361d04b390..903dfa5cc5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -586,7 +586,7 @@ private void handleUnprocessedDefinedTargets() { .build(); ReadAccessor targetPropertyReadAccessor = - method.getResultType().getReadAccessor( propertyName ); + method.getResultType().getReadAccessor( propertyName, forceUpdateMethod ); MappingReferences mappingRefs = extractMappingReferences( propertyName, true ); PropertyMapping propertyMapping = new PropertyMappingBuilder() .mappingContext( ctx ) @@ -1160,7 +1160,10 @@ private boolean handleDefinedMapping(MappingReference mappingRef, Type resultTyp } Accessor targetWriteAccessor = unprocessedTargetProperties.get( targetPropertyName ); - ReadAccessor targetReadAccessor = resultTypeToMap.getReadAccessor( targetPropertyName ); + ReadAccessor targetReadAccessor = resultTypeToMap.getReadAccessor( + targetPropertyName, + method.getSourceParameters().size() == 1 + ); if ( targetWriteAccessor == null ) { if ( targetReadAccessor == null ) { @@ -1513,7 +1516,8 @@ private void applyPropertyNameBasedMapping(List sourceReference } ReadAccessor targetPropertyReadAccessor = - method.getResultType().getReadAccessor( targetPropertyName ); + method.getResultType() + .getReadAccessor( targetPropertyName, method.getSourceParameters().size() == 1 ); MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false ); PropertyMapping propertyMapping = new PropertyMappingBuilder().mappingContext( ctx ) .sourceMethod( method ) @@ -1556,7 +1560,8 @@ private void applyParameterNameBasedMapping() { .build(); ReadAccessor targetPropertyReadAccessor = - method.getResultType().getReadAccessor( targetProperty.getKey() ); + method.getResultType() + .getReadAccessor( targetProperty.getKey(), method.getSourceParameters().size() == 1 ); MappingReferences mappingRefs = extractMappingReferences( targetProperty.getKey(), false ); PropertyMapping propertyMapping = new PropertyMappingBuilder() .mappingContext( ctx ) @@ -1600,7 +1605,8 @@ private SourceReference getSourceRefByTargetName(Parameter sourceParameter, Stri return sourceRef; } - ReadAccessor sourceReadAccessor = sourceParameter.getType().getReadAccessor( targetPropertyName ); + ReadAccessor sourceReadAccessor = sourceParameter.getType() + .getReadAccessor( targetPropertyName, method.getSourceParameters().size() == 1 ); if ( sourceReadAccessor != null ) { // property mapping PresenceCheckAccessor sourcePresenceChecker = diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index 61e3926047..b7bfc69780 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -643,7 +643,10 @@ private PropertyMapping createPropertyMappingForNestedTarget(MappingReferences m boolean forceUpdateMethod) { Accessor targetWriteAccessor = targetPropertiesWriteAccessors.get( targetPropertyName ); - ReadAccessor targetReadAccessor = targetType.getReadAccessor( targetPropertyName ); + ReadAccessor targetReadAccessor = targetType.getReadAccessor( + targetPropertyName, + method.getSourceParameters().size() == 1 + ); if ( targetWriteAccessor == null ) { Set readAccessors = targetType.getPropertyReadAccessors().keySet(); String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index 4e7dad220c..73ec84b6b7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -152,15 +152,27 @@ public SourceReference build() { private SourceReference buildFromSingleSourceParameters(String[] segments, Parameter parameter) { boolean foundEntryMatch; + boolean allowedMapToBean = false; + if ( segments.length > 0 ) { + if ( parameter.getType().isMapType() ) { + // When the parameter type is a map and the parameter name matches the first segment + // then the first segment should not be treated as a property of the map + allowedMapToBean = !segments[0].equals( parameter.getName() ); + } + } String[] propertyNames = segments; - List entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames ); + List entries = matchWithSourceAccessorTypes( + parameter.getType(), + propertyNames, + allowedMapToBean + ); foundEntryMatch = ( entries.size() == propertyNames.length ); if ( !foundEntryMatch ) { //Lets see if the expression contains the parameterName, so parameterName.propName1.propName2 if ( getSourceParameterFromMethodOrTemplate( segments[0] ) != null ) { propertyNames = Arrays.copyOfRange( segments, 1, segments.length ); - entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames ); + entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames, true ); foundEntryMatch = ( entries.size() == propertyNames.length ); } else { @@ -193,7 +205,7 @@ private SourceReference buildFromMultipleSourceParameters(String[] segments, Par if ( segments.length > 1 && parameter != null ) { propertyNames = Arrays.copyOfRange( segments, 1, segments.length ); - entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames ); + entries = matchWithSourceAccessorTypes( parameter.getType(), propertyNames, true ); foundEntryMatch = ( entries.size() == propertyNames.length ); } else { @@ -306,13 +318,14 @@ private void reportErrorOnNoMatch( Parameter parameter, String[] propertyNames, } } - private List matchWithSourceAccessorTypes(Type type, String[] entryNames) { + private List matchWithSourceAccessorTypes(Type type, String[] entryNames, + boolean allowedMapToBean) { List sourceEntries = new ArrayList<>(); Type newType = type; for ( int i = 0; i < entryNames.length; i++ ) { boolean matchFound = false; Type noBoundsType = newType.withoutBounds(); - ReadAccessor readAccessor = noBoundsType.getReadAccessor( entryNames[i] ); + ReadAccessor readAccessor = noBoundsType.getReadAccessor( entryNames[i], i > 0 || allowedMapToBean ); if ( readAccessor != null ) { PresenceCheckAccessor presenceChecker = noBoundsType.getPresenceChecker( entryNames[i] ); newType = typeFactory.getReturnType( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 4254913d83..93358c2a27 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -652,8 +652,8 @@ public Type asRawType() { } } - public ReadAccessor getReadAccessor(String propertyName) { - if ( hasStringMapSignature() ) { + public ReadAccessor getReadAccessor(String propertyName, boolean allowedMapToBean) { + if ( allowedMapToBean && hasStringMapSignature() ) { ExecutableElement getMethod = getAllMethods() .stream() .filter( m -> m.getSimpleName().contentEquals( "get" ) ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3144/Issue3144Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3144/Issue3144Mapper.java new file mode 100644 index 0000000000..df5b767876 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3144/Issue3144Mapper.java @@ -0,0 +1,66 @@ +/* + * 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._3144; + +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface Issue3144Mapper { + + Issue3144Mapper INSTANCE = Mappers.getMapper( Issue3144Mapper.class ); + + @Mapping(target = "map", source = "sourceMap") + Target mapExplicitDefined(Map sourceMap); + + @Mapping(target = "map", ignore = true) + Target map(Map sourceMap); + + Target mapMultiParameters(Source source, Map map); + + @Mapping(target = "value", source = "map.testValue") + Target mapMultiParametersDefinedMapping(Source source, Map map); + + class Source { + private final String sourceValue; + + public Source(String sourceValue) { + this.sourceValue = sourceValue; + } + + public String getSourceValue() { + return sourceValue; + } + } + + class Target { + private String value; + private Map map; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3144/Issue3144Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3144/Issue3144Test.java new file mode 100644 index 0000000000..13f349414a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3144/Issue3144Test.java @@ -0,0 +1,63 @@ +/* + * 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._3144; + +import java.util.HashMap; +import java.util.Map; + +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; +import static org.assertj.core.api.Assertions.entry; + +/** + * @author Filip Hrisafov + */ +@WithClasses(Issue3144Mapper.class) +@IssueKey("3144") +class Issue3144Test { + + @ProcessorTest + void shouldCorrectlyHandleMapBeanMapping() { + Map map = new HashMap<>(); + map.put( "value", "Map Value" ); + map.put( "testValue", "Map Test Value" ); + + Issue3144Mapper.Target target = Issue3144Mapper.INSTANCE.mapExplicitDefined( map ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "Map Value" ); + assertThat( target.getMap() ) + .containsOnly( + entry( "value", "Map Value" ), + entry( "testValue", "Map Test Value" ) + ); + + target = Issue3144Mapper.INSTANCE.map( map ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "Map Value" ); + assertThat( target.getMap() ).isNull(); + + target = Issue3144Mapper.INSTANCE.mapMultiParameters( null, map ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isNull(); + assertThat( target.getMap() ) + .containsOnly( + entry( "value", "Map Value" ), + entry( "testValue", "Map Test Value" ) + ); + + target = Issue3144Mapper.INSTANCE.mapMultiParametersDefinedMapping( null, map ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "Map Test Value" ); + assertThat( target.getMap() ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUsingMappingMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUsingMappingMethodMapper.java index 9a6344ff15..225ca00adc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUsingMappingMethodMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/frommap/MapToBeanUsingMappingMethodMapper.java @@ -19,7 +19,7 @@ public interface MapToBeanUsingMappingMethodMapper { MapToBeanUsingMappingMethodMapper INSTANCE = Mappers.getMapper( MapToBeanUsingMappingMethodMapper.class ); - @Mapping(target = "normalInt", source = "number") + @Mapping(target = "normalInt", source = "source.number") Target toTarget(Map source); default String mapIntegerToString( Integer input ) { From 1b1325da6d27ecc995aa659551c98dcf4ee764a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 22:28:38 +0000 Subject: [PATCH 367/597] Bump guava from 29.0-jre to 32.0.0-jre in /parent Bumps [guava](https://github.com/google/guava) from 29.0-jre to 32.0.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index f9531590f6..32f7e2897a 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -119,7 +119,7 @@ com.google.guava guava - 29.0-jre + 32.0.0-jre org.mapstruct.tools.gem From 04434af17a1195268af14b9775c93d48936f7e6e Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sat, 8 Jul 2023 18:03:56 +0200 Subject: [PATCH 368/597] #3296: Skip default and static methods when determining prototype methods --- .../processor/MethodRetrievalProcessor.java | 5 +++ .../mapstruct/ap/test/bugs/_3296/Entity.java | 18 ++++++++ .../ap/test/bugs/_3296/Issue3296Test.java | 43 +++++++++++++++++++ .../MapperConfigWithPayloadArgument.java | 24 +++++++++++ .../MapperConfigWithoutPayloadArgument.java | 23 ++++++++++ .../bugs/_3296/MapperExtendingConfig.java | 13 ++++++ .../bugs/_3296/MapperNotExtendingConfig.java | 13 ++++++ .../mapstruct/ap/test/bugs/_3296/Payload.java | 19 ++++++++ 8 files changed, 158 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Entity.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Issue3296Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperConfigWithPayloadArgument.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperConfigWithoutPayloadArgument.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperExtendingConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperNotExtendingConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Payload.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 6f1febae9d..faf7a786fd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -124,6 +124,11 @@ private List retrievePrototypeMethods(TypeElement mapperTypeElemen List methods = new ArrayList<>(); for ( ExecutableElement executable : elementUtils.getAllEnclosedExecutableElements( typeElement ) ) { + if ( executable.isDefault() || executable.getModifiers().contains( Modifier.STATIC ) ) { + // skip the default and static methods because these are not prototypes. + continue; + } + ExecutableType methodType = typeFactory.getMethodType( mapperAnnotation.mapperConfigType(), executable ); List parameters = typeFactory.getParameters( methodType, executable ); boolean containsTargetTypeParameter = SourceMethod.containsTargetTypeParameter( parameters ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Entity.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Entity.java new file mode 100644 index 0000000000..cfd038b03c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Entity.java @@ -0,0 +1,18 @@ +/* + * 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._3296; + +public class Entity { + String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Issue3296Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Issue3296Test.java new file mode 100644 index 0000000000..941eee9a70 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Issue3296Test.java @@ -0,0 +1,43 @@ +/* + * 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._3296; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.factory.Mappers; + +/** + * @author Ben Zegveld + */ +@IssueKey( "3296" ) +@WithClasses( { Entity.class, Payload.class } ) +public class Issue3296Test { + + @ProcessorTest + @WithClasses( { MapperExtendingConfig.class, MapperConfigWithPayloadArgument.class } ) + public void shouldNotRaiseErrorForDefaultAfterMappingMethodImplementation() { + Payload payload = new Payload(); + payload.setName( "original" ); + + Entity entity = Mappers.getMapper( MapperExtendingConfig.class ).toEntity( payload ); + + assertThat( entity.getName() ).isEqualTo( "AfterMapping called" ); + } + + @ProcessorTest + @WithClasses( { MapperNotExtendingConfig.class, MapperConfigWithoutPayloadArgument.class } ) + public void shouldNotRaiseErrorRequiringArgumentsForDefaultMethods() { + Payload payload = new Payload(); + payload.setName( "original" ); + + Entity entity = Mappers.getMapper( MapperNotExtendingConfig.class ).toEntity( payload ); + + assertThat( entity.getName() ).isEqualTo( "original" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperConfigWithPayloadArgument.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperConfigWithPayloadArgument.java new file mode 100644 index 0000000000..69826aa10e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperConfigWithPayloadArgument.java @@ -0,0 +1,24 @@ +/* + * 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._3296; + +import org.mapstruct.AfterMapping; +import org.mapstruct.MapperConfig; +import org.mapstruct.MappingTarget; + +@MapperConfig +public interface MapperConfigWithPayloadArgument { + + @AfterMapping + default void afterMapping(@MappingTarget Entity entity, Payload unused) { + staticMethod( entity ); + } + + static void staticMethod(Entity entity) { + entity.setName( "AfterMapping called" ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperConfigWithoutPayloadArgument.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperConfigWithoutPayloadArgument.java new file mode 100644 index 0000000000..36cc046d12 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperConfigWithoutPayloadArgument.java @@ -0,0 +1,23 @@ +/* + * 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._3296; + +import org.mapstruct.AfterMapping; +import org.mapstruct.MapperConfig; +import org.mapstruct.MappingTarget; + +@MapperConfig +public interface MapperConfigWithoutPayloadArgument { + + @AfterMapping + default void afterMapping(@MappingTarget Entity entity) { + staticMethod( entity ); + } + + static void staticMethod(Entity entity) { + entity.setName( "AfterMapping called" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperExtendingConfig.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperExtendingConfig.java new file mode 100644 index 0000000000..157bd051cb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperExtendingConfig.java @@ -0,0 +1,13 @@ +/* + * 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._3296; + +import org.mapstruct.Mapper; + +@Mapper( config = MapperConfigWithPayloadArgument.class ) +public interface MapperExtendingConfig extends MapperConfigWithPayloadArgument { + Entity toEntity(Payload payload); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperNotExtendingConfig.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperNotExtendingConfig.java new file mode 100644 index 0000000000..2ceb18c253 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/MapperNotExtendingConfig.java @@ -0,0 +1,13 @@ +/* + * 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._3296; + +import org.mapstruct.Mapper; + +@Mapper( config = MapperConfigWithoutPayloadArgument.class ) +public interface MapperNotExtendingConfig { + Entity toEntity(Payload payload); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Payload.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Payload.java new file mode 100644 index 0000000000..2502e5e4a2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3296/Payload.java @@ -0,0 +1,19 @@ +/* + * 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._3296; + +public class Payload { + + String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} From 53c73324ff85e0b718d2a8f2f64bcdbb9256632b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 8 Jul 2023 17:45:00 +0200 Subject: [PATCH 369/597] #3310 Make sure that adders work properly when they are in a generic class --- .../ap/internal/model/common/Type.java | 5 +- .../ap/test/bugs/_3310/Issue3310Mapper.java | 61 +++++++++++++++++++ .../ap/test/bugs/_3310/Issue3310Test.java | 31 ++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3310/Issue3310Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3310/Issue3310Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 93358c2a27..933f1f6ce0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -958,9 +958,8 @@ private List getAccessorCandidates(Type property, Class superclass) List adderList = getAdders(); List candidateList = new ArrayList<>(); for ( Accessor adder : adderList ) { - ExecutableElement executable = (ExecutableElement) adder.getElement(); - VariableElement arg = executable.getParameters().get( 0 ); - if ( typeUtils.isSameType( boxed( arg.asType() ), boxed( typeArg ) ) ) { + TypeMirror adderParameterType = determineTargetType( adder ).getTypeMirror(); + if ( typeUtils.isSameType( boxed( adderParameterType ), boxed( typeArg ) ) ) { candidateList.add( adder ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3310/Issue3310Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3310/Issue3310Mapper.java new file mode 100644 index 0000000000..2975a5de8a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3310/Issue3310Mapper.java @@ -0,0 +1,61 @@ +/* + * 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._3310; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface Issue3310Mapper { + + Issue3310Mapper INSTANCE = Mappers.getMapper( Issue3310Mapper.class ); + + Target map(Source source); + + abstract class BaseClass { + + private List items; + + public List getItems() { + return items; + } + + public void setItems(List items) { + throw new UnsupportedOperationException( "adder should be used instead" ); + } + + public void addItem(T item) { + if ( items == null ) { + items = new ArrayList<>(); + } + items.add( item ); + } + } + + class Target extends BaseClass { + + } + + class Source { + + private final List items; + + public Source(List items) { + this.items = items; + } + + public List getItems() { + return items; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3310/Issue3310Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3310/Issue3310Test.java new file mode 100644 index 0000000000..a00526985f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3310/Issue3310Test.java @@ -0,0 +1,31 @@ +/* + * 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._3310; + +import java.util.Collections; + +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 Filip Hrisafov + */ +@IssueKey("3310") +@WithClasses(Issue3310Mapper.class) +class Issue3310Test { + + @ProcessorTest + void shouldUseAdderWithGenericBaseClass() { + Issue3310Mapper.Source source = new Issue3310Mapper.Source( Collections.singletonList( "test" ) ); + Issue3310Mapper.Target target = Issue3310Mapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getItems() ).containsExactly( "test" ); + } +} From 4abf2d42029e8b5e7b767a6f4ac74050e188170a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 8 Jul 2023 18:32:10 +0200 Subject: [PATCH 370/597] #3317 Do not generate source parameter if check for only primitives --- .../ap/internal/model/BeanMappingMethod.ftl | 2 +- .../ap/test/bugs/_3317/Issue3317Mapper.java | 39 +++++++++++++++++++ .../ap/test/bugs/_3317/Issue3317Test.java | 28 +++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3317/Issue3317Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3317/Issue3317Test.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index a0fbe24b3e..0f9b96bb66 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -27,7 +27,7 @@ - <#if !mapNullToDefault> + <#if !mapNullToDefault && !sourceParametersExcludingPrimitives.empty> if ( <#list sourceParametersExcludingPrimitives as sourceParam>${sourceParam.name} == null<#if sourceParam_has_next> && ) { return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#if finalizerMethod??>.<@includeModel object=finalizerMethod /><#else>null; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3317/Issue3317Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3317/Issue3317Mapper.java new file mode 100644 index 0000000000..33a40b1dfb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3317/Issue3317Mapper.java @@ -0,0 +1,39 @@ +/* + * 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._3317; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3317Mapper { + + Issue3317Mapper INSTANCE = Mappers.getMapper( Issue3317Mapper.class ); + + Target map(int id, long value); + + class Target { + + private final int id; + private final long value; + + public Target(int id, long value) { + this.id = id; + this.value = value; + } + + public int getId() { + return id; + } + + public long getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3317/Issue3317Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3317/Issue3317Test.java new file mode 100644 index 0000000000..218a1f8979 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3317/Issue3317Test.java @@ -0,0 +1,28 @@ +/* + * 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._3317; + +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 Filip Hrisafov + */ +@IssueKey("3317") +@WithClasses(Issue3317Mapper.class) +class Issue3317Test { + + @ProcessorTest + void shouldGenerateValidCode() { + Issue3317Mapper.Target target = Issue3317Mapper.INSTANCE.map( 10, 42L ); + assertThat( target ).isNotNull(); + assertThat( target.getId() ).isEqualTo( 10 ); + assertThat( target.getValue() ).isEqualTo( 42L ); + } +} From 230e84efd1dde2f06e3032200051f69932402424 Mon Sep 17 00:00:00 2001 From: Roberto Oliveira Date: Tue, 25 Jul 2023 18:10:23 +0200 Subject: [PATCH 371/597] #3340 Update tarLongFileMode to use POSIX --- distribution/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/pom.xml b/distribution/pom.xml index bcfa1f3db0..afa8e31002 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -162,7 +162,7 @@ ${basedir}/src/main/assembly/dist.xml mapstruct-${project.version} - gnu + posix From 0460c373c06d771e7766dd17aaaad7d1a28928a1 Mon Sep 17 00:00:00 2001 From: Lucas Resch Date: Sun, 30 Jul 2023 10:38:24 +0200 Subject: [PATCH 372/597] #3229: Implement InjectionStrategy.SETTER --- .../java/org/mapstruct/InjectionStrategy.java | 6 +- .../chapter-4-retrieving-a-mapper.asciidoc | 6 +- .../ap/internal/gem/InjectionStrategyGem.java | 3 +- .../ap/internal/model/AnnotatedSetter.java | 59 +++++++++ .../ap/internal/model/GeneratedType.java | 8 +- .../internal/model/GeneratedTypeMethod.java | 15 +++ .../ap/internal/model/MappingMethod.java | 3 +- ...nnotationBasedComponentModelProcessor.java | 70 +++++++++-- .../ap/internal/model/AnnotatedSetter.ftl | 14 +++ .../decorator/spring/setter/PersonMapper.java | 26 ++++ .../spring/setter/PersonMapperDecorator.java | 29 +++++ .../spring/setter/SpringDecoratorTest.java | 88 +++++++++++++ .../setter/CustomerJakartaSetterMapper.java | 26 ++++ .../setter/GenderJakartaSetterMapper.java | 25 ++++ .../setter/JakartaSetterMapperTest.java | 57 +++++++++ .../jakarta/setter/SetterJakartaConfig.java | 18 +++ .../setter/CustomerJsr330SetterMapper.java | 26 ++++ .../setter/GenderJsr330SetterMapper.java | 25 ++++ .../jsr330/setter/Jsr330SetterMapperTest.java | 98 +++++++++++++++ .../jsr330/setter/SetterJsr330Config.java | 18 +++ .../CustomerRecordSpringSetterMapper.java | 23 ++++ .../setter/CustomerSpringSetterMapper.java | 25 ++++ .../setter/GenderSpringSetterMapper.java | 25 ++++ .../spring/setter/SetterSpringConfig.java | 17 +++ .../spring/setter/SpringSetterMapperTest.java | 119 ++++++++++++++++++ 25 files changed, 813 insertions(+), 16 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedSetter.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedTypeMethod.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/AnnotatedSetter.ftl create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/PersonMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/PersonMapperDecorator.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/SpringDecoratorTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/CustomerJakartaSetterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/GenderJakartaSetterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/JakartaSetterMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/SetterJakartaConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/CustomerJsr330SetterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/GenderJsr330SetterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/Jsr330SetterMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/SetterJsr330Config.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/CustomerRecordSpringSetterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/CustomerSpringSetterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/GenderSpringSetterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/SetterSpringConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/SpringSetterMapperTest.java diff --git a/core/src/main/java/org/mapstruct/InjectionStrategy.java b/core/src/main/java/org/mapstruct/InjectionStrategy.java index b841667671..f5029e2246 100644 --- a/core/src/main/java/org/mapstruct/InjectionStrategy.java +++ b/core/src/main/java/org/mapstruct/InjectionStrategy.java @@ -10,6 +10,7 @@ * JSR330 / Jakarta. * * @author Kevin Grüneberg + * @author Lucas Resch */ public enum InjectionStrategy { @@ -17,5 +18,8 @@ public enum InjectionStrategy { FIELD, /** Annotations are written on the constructor **/ - CONSTRUCTOR + CONSTRUCTOR, + + /** A dedicated setter method is created */ + SETTER } diff --git a/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc index 67db28c7c0..35afc93a51 100644 --- a/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-4-retrieving-a-mapper.asciidoc @@ -102,7 +102,7 @@ A mapper which uses other mapper classes (see <>) will o [[injection-strategy]] === Injection strategy -When using <>, you can choose between field and constructor injection. +When using <>, you can choose between constructor, field, or setter injection. This can be done by either providing the injection strategy via `@Mapper` or `@MapperConfig` annotation. .Using constructor injection @@ -120,9 +120,13 @@ public interface CarMapper { The generated mapper will inject classes defined in the **uses** attribute if MapStruct has detected that it needs to use an instance of it for a mapping. When `InjectionStrategy#CONSTRUCTOR` is used, the constructor will have the appropriate annotation and the fields won't. When `InjectionStrategy#FIELD` is used, the annotation is on the field itself. +When `InjectionStrategy#SETTER` is used the annotation is on a generated setter method. For now, the default injection strategy is field injection, but it can be configured with <>. It is recommended to use constructor injection to simplify testing. +When you define mappers in Spring with circular dependencies compilation may fail. +In that case utilize the `InjectionStrategy#SETTER` strategy. + [TIP] ==== For abstract classes or decorators setter injection should be used. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/InjectionStrategyGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/InjectionStrategyGem.java index f8ed93eeb4..621d07fd09 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/InjectionStrategyGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/InjectionStrategyGem.java @@ -13,5 +13,6 @@ public enum InjectionStrategyGem { FIELD, - CONSTRUCTOR; + CONSTRUCTOR, + SETTER; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedSetter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedSetter.java new file mode 100644 index 0000000000..5614376ea4 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedSetter.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.internal.model; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Lucas Resch + */ +public class AnnotatedSetter extends GeneratedTypeMethod { + + private final Field field; + private final Collection methodAnnotations; + private final Collection parameterAnnotations; + + public AnnotatedSetter(Field field, Collection methodAnnotations, + Collection parameterAnnotations) { + this.field = field; + this.methodAnnotations = methodAnnotations; + this.parameterAnnotations = parameterAnnotations; + } + + @Override + public Set getImportTypes() { + Set importTypes = new HashSet<>( field.getImportTypes() ); + for ( Annotation annotation : methodAnnotations ) { + importTypes.addAll( annotation.getImportTypes() ); + } + + for ( Annotation annotation : parameterAnnotations ) { + importTypes.addAll( annotation.getImportTypes() ); + } + + return importTypes; + } + + public Type getType() { + return field.getType(); + } + + public String getFieldName() { + return field.getVariableName(); + } + + public Collection getMethodAnnotations() { + return methodAnnotations; + } + + public Collection getParameterAnnotations() { + return parameterAnnotations; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java index 8348ecd84b..0bc3ec7bf0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java @@ -82,7 +82,7 @@ public T methods(List methods) { private final Type mapperDefinitionType; private final List annotations; - private final List methods; + private final List methods; private final SortedSet extraImportedTypes; private final boolean suppressGeneratorTimestamp; @@ -110,7 +110,7 @@ protected GeneratedType(TypeFactory typeFactory, String packageName, String name this.extraImportedTypes = extraImportedTypes; this.annotations = new ArrayList<>(); - this.methods = methods; + this.methods = new ArrayList<>(methods); this.fields = fields; this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; @@ -161,7 +161,7 @@ public void addAnnotation(Annotation annotation) { annotations.add( annotation ); } - public List getMethods() { + public List getMethods() { return methods; } @@ -204,7 +204,7 @@ public SortedSet getImportTypes() { addIfImportRequired( importedTypes, mapperDefinitionType ); - for ( MappingMethod mappingMethod : methods ) { + for ( GeneratedTypeMethod mappingMethod : methods ) { for ( Type type : mappingMethod.getImportTypes() ) { addIfImportRequired( importedTypes, type ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedTypeMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedTypeMethod.java new file mode 100644 index 0000000000..f02cc16b2c --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedTypeMethod.java @@ -0,0 +1,15 @@ +/* + * 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.internal.model; + +import org.mapstruct.ap.internal.model.common.ModelElement; + +/** + * @author Filip Hrisafov + */ +public abstract class GeneratedTypeMethod extends ModelElement { + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java index 4c59216e09..599ddf1d35 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java @@ -14,7 +14,6 @@ import java.util.Set; import org.mapstruct.ap.internal.model.common.Accessibility; -import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; @@ -27,7 +26,7 @@ * * @author Gunnar Morling */ -public abstract class MappingMethod extends ModelElement { +public abstract class MappingMethod extends GeneratedTypeMethod { private final String name; private final List parameters; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java index d68936f545..014ceda58e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java @@ -11,10 +11,11 @@ import java.util.List; import java.util.ListIterator; import java.util.Set; - +import java.util.stream.Collectors; import javax.lang.model.element.TypeElement; import org.mapstruct.ap.internal.model.AnnotatedConstructor; +import org.mapstruct.ap.internal.model.AnnotatedSetter; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.AnnotationMapperReference; import org.mapstruct.ap.internal.model.Decorator; @@ -77,6 +78,9 @@ else if ( mapper.getDecorator() != null ) { if ( injectionStrategy == InjectionStrategyGem.CONSTRUCTOR ) { buildConstructors( mapper ); } + else if ( injectionStrategy == InjectionStrategyGem.SETTER ) { + buildSetters( mapper ); + } return mapper; } @@ -110,6 +114,42 @@ private List toMapperReferences(List fields) { return mapperReferences; } + private void buildSetters(Mapper mapper) { + List mapperReferences = toMapperReferences( mapper.getFields() ); + for ( MapperReference mapperReference : mapperReferences ) { + if ( mapperReference.isUsed() ) { + AnnotatedSetter setter = new AnnotatedSetter( + mapperReference, + getMapperReferenceAnnotations(), + Collections.emptyList() + ); + mapper.getMethods().add( setter ); + } + } + + Decorator decorator = mapper.getDecorator(); + if ( decorator != null ) { + List mapperReferenceAnnotations = getMapperReferenceAnnotations(); + Set mapperReferenceAnnotationsTypes = mapperReferenceAnnotations + .stream() + .map( Annotation::getType ) + .collect( Collectors.toSet() ); + for ( Field field : decorator.getFields() ) { + if ( field instanceof AnnotationMapperReference ) { + + List fieldAnnotations = ( (AnnotationMapperReference) field ).getAnnotations(); + + List qualifiers = extractMissingAnnotations( + fieldAnnotations, + mapperReferenceAnnotationsTypes + ); + + decorator.getMethods().add( new AnnotatedSetter( field, mapperReferenceAnnotations, qualifiers ) ); + } + } + } + } + private void buildConstructors(Mapper mapper) { if ( !toMapperReferences( mapper.getFields() ).isEmpty() ) { AnnotatedConstructor annotatedConstructor = buildAnnotatedConstructorForMapper( mapper ); @@ -197,17 +237,33 @@ private void removeDuplicateAnnotations(List annotati AnnotationMapperReference annotationMapperReference = mapperReferenceIterator.next(); mapperReferenceIterator.remove(); - List qualifiers = new ArrayList<>(); - for ( Annotation annotation : annotationMapperReference.getAnnotations() ) { - if ( !mapperReferenceAnnotationsTypes.contains( annotation.getType() ) ) { - qualifiers.add( annotation ); - } - } + List qualifiers = extractMissingAnnotations( + annotationMapperReference.getAnnotations(), + mapperReferenceAnnotationsTypes + ); mapperReferenceIterator.add( annotationMapperReference.withNewAnnotations( qualifiers ) ); } } + /** + * Extract all annotations from {@code annotations} that do not have a type in {@code annotationTypes}. + * + * @param annotations the annotations from which we need to extract information + * @param annotationTypes the annotation types to ignore + * @return the annotations that are not in the {@code annotationTypes} + */ + private List extractMissingAnnotations(List annotations, + Set annotationTypes) { + List qualifiers = new ArrayList<>(); + for ( Annotation annotation : annotations ) { + if ( !annotationTypes.contains( annotation.getType() ) ) { + qualifiers.add( annotation ); + } + } + return qualifiers; + } + protected boolean additionalPublicEmptyConstructor() { return false; } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/AnnotatedSetter.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/AnnotatedSetter.ftl new file mode 100644 index 0000000000..74d4241fa5 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/AnnotatedSetter.ftl @@ -0,0 +1,14 @@ + <#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> + <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.AnnotatedSetter" --> + <#list methodAnnotations as annotation> + <#nt><@includeModel object=annotation/> + +public void set${fieldName?cap_first}(<#list parameterAnnotations as annotation><#nt><@includeModel object=annotation/> <@includeModel object=type/> ${fieldName}) { + this.${fieldName} = ${fieldName}; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/PersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/PersonMapper.java new file mode 100644 index 0000000000..20ac9d874c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/PersonMapper.java @@ -0,0 +1,26 @@ +/* + * 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.decorator.spring.setter; + +import org.mapstruct.DecoratedWith; +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; + +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, injectionStrategy = InjectionStrategy.SETTER) +@DecoratedWith(PersonMapperDecorator.class) +public interface PersonMapper { + + @Mapping( target = "name", ignore = true ) + PersonDto personToPersonDto(Person person); + + AddressDto addressToAddressDto(Address address); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/PersonMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/PersonMapperDecorator.java new file mode 100644 index 0000000000..8f306284a1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/PersonMapperDecorator.java @@ -0,0 +1,29 @@ +/* + * 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.decorator.spring.setter; + +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +public abstract class PersonMapperDecorator implements PersonMapper { + + private PersonMapper decoratorDelegate; + + @Override + public PersonDto personToPersonDto(Person person) { + PersonDto dto = decoratorDelegate.personToPersonDto( person ); + dto.setName( person.getFirstName() + " " + person.getLastName() ); + + return dto; + } + + @Autowired + public void setDecoratorDelegate(@Qualifier("delegate") PersonMapper decoratorDelegate) { + this.decoratorDelegate = decoratorDelegate; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/SpringDecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/SpringDecoratorTest.java new file mode 100644 index 0000000000..c03b05395a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/setter/SpringDecoratorTest.java @@ -0,0 +1,88 @@ +/* + * 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.decorator.spring.setter; + +import java.util.Calendar; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +@WithClasses({ + Person.class, + PersonDto.class, + Address.class, + AddressDto.class, + PersonMapper.class, + PersonMapperDecorator.class +}) +@WithSpring +@IssueKey("3229") +@ComponentScan(basePackageClasses = SpringDecoratorTest.class) +@Configuration +public class SpringDecoratorTest { + + @Autowired + private PersonMapper personMapper; + private ConfigurableApplicationContext context; + + @BeforeEach + public void springUp() { + context = new AnnotationConfigApplicationContext( getClass() ); + context.getAutowireCapableBeanFactory().autowireBean( this ); + } + + @AfterEach + public void springDown() { + if ( context != null ) { + context.close(); + } + } + + @ProcessorTest + public void shouldInvokeDecoratorMethods() { + //given + Calendar birthday = Calendar.getInstance(); + birthday.set( 1928, Calendar.MAY, 23 ); + Person person = new Person( "Gary", "Crant", birthday.getTime(), new Address( "42 Ocean View Drive" ) ); + + //when + PersonDto personDto = personMapper.personToPersonDto( person ); + + //then + assertThat( personDto ).isNotNull(); + assertThat( personDto.getName() ).isEqualTo( "Gary Crant" ); + assertThat( personDto.getAddress() ).isNotNull(); + assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); + } + + @ProcessorTest + public void shouldDelegateNonDecoratedMethodsToDefaultImplementation() { + //given + Address address = new Address( "42 Ocean View Drive" ); + + //when + AddressDto addressDto = personMapper.addressToAddressDto( address ); + + //then + assertThat( addressDto ).isNotNull(); + assertThat( addressDto.getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/CustomerJakartaSetterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/CustomerJakartaSetterMapper.java new file mode 100644 index 0000000000..d422442791 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/CustomerJakartaSetterMapper.java @@ -0,0 +1,26 @@ +/* + * 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.injectionstrategy.jakarta.setter; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Filip Hrisafov + */ +@Mapper( componentModel = MappingConstants.ComponentModel.JAKARTA, + uses = GenderJakartaSetterMapper.class, + injectionStrategy = InjectionStrategy.SETTER ) +public interface CustomerJakartaSetterMapper { + + @Mapping(target = "gender", source = "gender") + CustomerDto asTarget(CustomerEntity customerEntity); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/GenderJakartaSetterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/GenderJakartaSetterMapper.java new file mode 100644 index 0000000000..f0d9b851e9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/GenderJakartaSetterMapper.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.ap.test.injectionstrategy.jakarta.setter; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Filip Hrisafov + */ +@Mapper(config = SetterJakartaConfig.class) +public interface GenderJakartaSetterMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/JakartaSetterMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/JakartaSetterMapperTest.java new file mode 100644 index 0000000000..13eba2e866 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/JakartaSetterMapperTest.java @@ -0,0 +1,57 @@ +/* + * 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.injectionstrategy.jakarta.setter; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; +import org.springframework.context.annotation.Configuration; + +import static java.lang.System.lineSeparator; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJakartaSetterMapper.class, + GenderJakartaSetterMapper.class, + SetterJakartaConfig.class +}) +@IssueKey("3229") +@Configuration +@WithJakartaInject +public class JakartaSetterMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void shouldHaveSetterInjection() { + String method = "@Inject" + lineSeparator() + + " public void setGenderJakartaSetterMapper(GenderJakartaSetterMapper genderJakartaSetterMapper) {" + + lineSeparator() + " this.genderJakartaSetterMapper = genderJakartaSetterMapper;" + + lineSeparator() + " }"; + generatedSource.forMapper( CustomerJakartaSetterMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "private GenderJakartaSetterMapper genderJakartaSetterMapper;" ) + .doesNotContain( "@Inject" + lineSeparator() + " private GenderJakartaSetterMapper" ) + .contains( method ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/SetterJakartaConfig.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/SetterJakartaConfig.java new file mode 100644 index 0000000000..a189244936 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jakarta/setter/SetterJakartaConfig.java @@ -0,0 +1,18 @@ +/* + * 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.injectionstrategy.jakarta.setter; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.MapperConfig; +import org.mapstruct.MappingConstants; + +/** + * @author Filip Hrisafov + */ +@MapperConfig(componentModel = MappingConstants.ComponentModel.JAKARTA, + injectionStrategy = InjectionStrategy.SETTER) +public interface SetterJakartaConfig { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/CustomerJsr330SetterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/CustomerJsr330SetterMapper.java new file mode 100644 index 0000000000..ab269f83f8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/CustomerJsr330SetterMapper.java @@ -0,0 +1,26 @@ +/* + * 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.injectionstrategy.jsr330.setter; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Filip Hrisafov + */ +@Mapper( componentModel = MappingConstants.ComponentModel.JSR330, + uses = GenderJsr330SetterMapper.class, + injectionStrategy = InjectionStrategy.SETTER ) +public interface CustomerJsr330SetterMapper { + + @Mapping(target = "gender", source = "gender") + CustomerDto asTarget(CustomerEntity customerEntity); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/GenderJsr330SetterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/GenderJsr330SetterMapper.java new file mode 100644 index 0000000000..d9b2f8ea9c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/GenderJsr330SetterMapper.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.ap.test.injectionstrategy.jsr330.setter; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Filip Hrisafov + */ +@Mapper(config = SetterJsr330Config.class) +public interface GenderJsr330SetterMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/Jsr330SetterMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/Jsr330SetterMapperTest.java new file mode 100644 index 0000000000..558206e51c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/Jsr330SetterMapperTest.java @@ -0,0 +1,98 @@ +/* + * 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.injectionstrategy.jsr330.setter; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import static java.lang.System.lineSeparator; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerJsr330SetterMapper.class, + GenderJsr330SetterMapper.class, + SetterJsr330Config.class +}) +@IssueKey("3229") +@ComponentScan(basePackageClasses = CustomerJsr330SetterMapper.class) +@Configuration +@WithJavaxInject +public class Jsr330SetterMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @Autowired + private CustomerJsr330SetterMapper customerMapper; + private ConfigurableApplicationContext context; + + @BeforeEach + public void springUp() { + context = new AnnotationConfigApplicationContext( getClass() ); + context.getAutowireCapableBeanFactory().autowireBean( this ); + } + + @AfterEach + public void springDown() { + if ( context != null ) { + context.close(); + } + } + + @ProcessorTest + public void shouldConvertToTarget() { + // given + CustomerEntity customerEntity = new CustomerEntity(); + customerEntity.setName( "Samuel" ); + customerEntity.setGender( Gender.MALE ); + + // when + CustomerDto customerDto = customerMapper.asTarget( customerEntity ); + + // then + assertThat( customerDto ).isNotNull(); + assertThat( customerDto.getName() ).isEqualTo( "Samuel" ); + assertThat( customerDto.getGender() ).isEqualTo( GenderDto.M ); + } + + @ProcessorTest + public void shouldHaveSetterInjection() { + String method = "@Inject" + lineSeparator() + + " public void setGenderJsr330SetterMapper(GenderJsr330SetterMapper genderJsr330SetterMapper) {" + + lineSeparator() + " this.genderJsr330SetterMapper = genderJsr330SetterMapper;" + + lineSeparator() + " }"; + generatedSource.forMapper( CustomerJsr330SetterMapper.class ) + .content() + .contains( "import javax.inject.Inject;" ) + .contains( "import javax.inject.Named;" ) + .contains( "import javax.inject.Singleton;" ) + .contains( "private GenderJsr330SetterMapper genderJsr330SetterMapper;" ) + .doesNotContain( "@Inject" + lineSeparator() + " private GenderJsr330SetterMapper" ) + .contains( method ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/SetterJsr330Config.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/SetterJsr330Config.java new file mode 100644 index 0000000000..2b13a56feb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/SetterJsr330Config.java @@ -0,0 +1,18 @@ +/* + * 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.injectionstrategy.jsr330.setter; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.MapperConfig; +import org.mapstruct.MappingConstants; + +/** + * @author Filip Hrisafov + */ +@MapperConfig(componentModel = MappingConstants.ComponentModel.JSR330, + injectionStrategy = InjectionStrategy.SETTER) +public interface SetterJsr330Config { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/CustomerRecordSpringSetterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/CustomerRecordSpringSetterMapper.java new file mode 100644 index 0000000000..1d09bdda03 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/CustomerRecordSpringSetterMapper.java @@ -0,0 +1,23 @@ +/* + * 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.injectionstrategy.spring.setter; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordEntity; + +/** + * @author Lucas Resch + */ +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, + uses = { CustomerSpringSetterMapper.class, GenderSpringSetterMapper.class }, + injectionStrategy = InjectionStrategy.SETTER) +public interface CustomerRecordSpringSetterMapper { + + CustomerRecordDto asTarget(CustomerRecordEntity customerRecordEntity); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/CustomerSpringSetterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/CustomerSpringSetterMapper.java new file mode 100644 index 0000000000..67dbab5e9c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/CustomerSpringSetterMapper.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.ap.test.injectionstrategy.spring.setter; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; + +/** + * @author Lucas Resch + */ +@Mapper( componentModel = MappingConstants.ComponentModel.SPRING, + uses = GenderSpringSetterMapper.class, + injectionStrategy = InjectionStrategy.SETTER ) +public interface CustomerSpringSetterMapper { + + @Mapping(target = "gender", source = "gender") + CustomerDto asTarget(CustomerEntity customerEntity); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/GenderSpringSetterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/GenderSpringSetterMapper.java new file mode 100644 index 0000000000..6243465ab5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/GenderSpringSetterMapper.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.ap.test.injectionstrategy.spring.setter; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; + +/** + * @author Lucas Resch + */ +@Mapper(config = SetterSpringConfig.class) +public interface GenderSpringSetterMapper { + + @ValueMappings({ + @ValueMapping(source = "MALE", target = "M"), + @ValueMapping(source = "FEMALE", target = "F") + }) + GenderDto mapToDto(Gender gender); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/SetterSpringConfig.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/SetterSpringConfig.java new file mode 100644 index 0000000000..18227f315a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/SetterSpringConfig.java @@ -0,0 +1,17 @@ +/* + * 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.injectionstrategy.spring.setter; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.MapperConfig; +import org.mapstruct.MappingConstants; + +/** + * @author Lucas Resch + */ +@MapperConfig(componentModel = MappingConstants.ComponentModel.SPRING, injectionStrategy = InjectionStrategy.SETTER) +public interface SetterSpringConfig { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/SpringSetterMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/SpringSetterMapperTest.java new file mode 100644 index 0000000000..4dcf098a9e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/spring/setter/SpringSetterMapperTest.java @@ -0,0 +1,119 @@ +/* + * 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.injectionstrategy.spring.setter; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.DefaultTimeZone; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordDto; +import org.mapstruct.ap.test.injectionstrategy.shared.CustomerRecordEntity; +import org.mapstruct.ap.test.injectionstrategy.shared.Gender; +import org.mapstruct.ap.test.injectionstrategy.shared.GenderDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; +import org.mapstruct.ap.testutil.runner.GeneratedSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import static java.lang.System.lineSeparator; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test setter injection for component model spring. + * + * @author Lucas Resch + */ +@WithClasses( { + CustomerRecordDto.class, + CustomerRecordEntity.class, + CustomerDto.class, + CustomerEntity.class, + Gender.class, + GenderDto.class, + CustomerRecordSpringSetterMapper.class, + CustomerSpringSetterMapper.class, + GenderSpringSetterMapper.class, + SetterSpringConfig.class +} ) +@IssueKey( "3229" ) +@ComponentScan(basePackageClasses = CustomerSpringSetterMapper.class) +@Configuration +@WithSpring +@DefaultTimeZone("Europe/Berlin") +public class SpringSetterMapperTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @Autowired + private CustomerRecordSpringSetterMapper customerRecordMapper; + private ConfigurableApplicationContext context; + + @BeforeEach + public void springUp() { + context = new AnnotationConfigApplicationContext( getClass() ); + context.getAutowireCapableBeanFactory().autowireBean( this ); + } + + @AfterEach + public void springDown() { + if ( context != null ) { + context.close(); + } + } + + @ProcessorTest + public void shouldConvertToTarget() throws Exception { + // given + CustomerEntity customerEntity = new CustomerEntity(); + customerEntity.setName( "Samuel" ); + customerEntity.setGender( Gender.MALE ); + CustomerRecordEntity customerRecordEntity = new CustomerRecordEntity(); + customerRecordEntity.setCustomer( customerEntity ); + customerRecordEntity.setRegistrationDate( createDate( "31-08-1982 10:20:56" ) ); + + // when + CustomerRecordDto customerRecordDto = customerRecordMapper.asTarget( customerRecordEntity ); + + // then + assertThat( customerRecordDto ).isNotNull(); + assertThat( customerRecordDto.getCustomer() ).isNotNull(); + assertThat( customerRecordDto.getCustomer().getName() ).isEqualTo( "Samuel" ); + assertThat( customerRecordDto.getCustomer().getGender() ).isEqualTo( GenderDto.M ); + assertThat( customerRecordDto.getRegistrationDate() ).isNotNull(); + assertThat( customerRecordDto.getRegistrationDate() ).hasToString( "1982-08-31T10:20:56.000+02:00" ); + } + + @ProcessorTest + public void shouldHaveSetterInjection() { + String method = "@Autowired" + lineSeparator() + + " public void setGenderSpringSetterMapper(GenderSpringSetterMapper genderSpringSetterMapper) {" + + lineSeparator() + " this.genderSpringSetterMapper = genderSpringSetterMapper;" + + lineSeparator() + " }"; + generatedSource.forMapper( CustomerSpringSetterMapper.class ) + .content() + .contains( "private GenderSpringSetterMapper genderSpringSetterMapper;" ) + .contains( method ); + } + + private Date createDate(String date) throws ParseException { + SimpleDateFormat sdf = new SimpleDateFormat( "dd-M-yyyy hh:mm:ss" ); + return sdf.parse( date ); + } + +} From 28d827a724bab212c8069903bd1bac3679370ad6 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Jul 2023 14:52:24 +0200 Subject: [PATCH 373/597] Add test case for subclass mapping and bean mapping ignore by default --- .../SubclassIgnoreByDefaultMapper.java | 29 +++++++++++++++++++ .../subclassmapping/SubclassMappingTest.java | 14 +++++++++ 2 files changed, 43 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassIgnoreByDefaultMapper.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassIgnoreByDefaultMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassIgnoreByDefaultMapper.java new file mode 100644 index 0000000000..d138eb4ec3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassIgnoreByDefaultMapper.java @@ -0,0 +1,29 @@ +/* + * 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.subclassmapping; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.ap.test.subclassmapping.mappables.Bike; +import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Car; +import org.mapstruct.ap.test.subclassmapping.mappables.CarDto; +import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle; +import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SubclassIgnoreByDefaultMapper { + SubclassIgnoreByDefaultMapper INSTANCE = Mappers.getMapper( SubclassIgnoreByDefaultMapper.class ); + + @BeanMapping(ignoreByDefault = true) + @SubclassMapping(source = Car.class, target = CarDto.class) + @SubclassMapping(source = Bike.class, target = BikeDto.class) + @Mapping(target = "name") + VehicleDto map(Vehicle vehicle); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java index ba64d2cc7f..60e274ef19 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/SubclassMappingTest.java @@ -277,4 +277,18 @@ void erroneousMethodWithSourceTargetType() { }) void inverseSubclassMappingNotPossible() { } + + @ProcessorTest + @WithClasses(SubclassIgnoreByDefaultMapper.class) + void beanMappingIgnoreByDefaultShouldBePropagated() { + Car car = new Car(); + car.setName( "Test car" ); + car.setManual( true ); + VehicleDto target = SubclassIgnoreByDefaultMapper.INSTANCE.map( car ); + assertThat( target ) + .isInstanceOfSatisfying( CarDto.class, carDto -> { + assertThat( carDto.getName() ).isEqualTo( "Test car" ); + assertThat( carDto.isManual() ).isFalse(); + } ); + } } From 279ab2248291cba77391cae38a4b2ea40fbeae7c Mon Sep 17 00:00:00 2001 From: Venkatesh Prasad Kannan Date: Tue, 1 Aug 2023 08:48:20 +0100 Subject: [PATCH 374/597] #3309 Add `BeanMapping#unmappedSourcePolicy` --- .../main/java/org/mapstruct/BeanMapping.java | 11 +++ .../ap/internal/model/BeanMappingMethod.java | 2 +- .../model/source/BeanMappingOptions.java | 10 +++ .../unmappedsource/UnmappedSourceTest.java | 1 + ...eanMappingSourcePolicyErroneousMapper.java | 18 +++++ .../BeanMappingSourcePolicyMapper.java | 25 +++++++ .../unmappedsource/beanmapping/Source.java | 30 +++++++++ .../unmappedsource/beanmapping/Target.java | 20 ++++++ .../beanmapping/UnmappedSourceTest.java | 67 +++++++++++++++++++ 9 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/BeanMappingSourcePolicyErroneousMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/BeanMappingSourcePolicyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/UnmappedSourceTest.java diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java index be3df0d29d..a03546b07d 100644 --- a/core/src/main/java/org/mapstruct/BeanMapping.java +++ b/core/src/main/java/org/mapstruct/BeanMapping.java @@ -152,6 +152,17 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() */ String[] ignoreUnmappedSourceProperties() default {}; + /** + * How unmapped properties of the source type of a mapping should be reported. + * If no policy is configured, the policy given via {@link MapperConfig#unmappedSourcePolicy()} or + * {@link Mapper#unmappedSourcePolicy()} will be applied, using {@link ReportingPolicy#IGNORE} by default. + * + * @return The reporting policy for unmapped source properties. + * + * @since 1.6 + */ + ReportingPolicy unmappedSourcePolicy() default ReportingPolicy.IGNORE; + /** * How unmapped properties of the target type of a mapping should be reported. * If no policy is configured, the policy given via {@link MapperConfig#unmappedTargetPolicy()} or diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 903dfa5cc5..11245b3376 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1740,7 +1740,7 @@ private ReportingPolicyGem getUnmappedSourcePolicy() { if ( method.getOptions().getBeanMapping().isignoreByDefault() ) { return ReportingPolicyGem.IGNORE; } - return method.getOptions().getMapper().unmappedSourcePolicy(); + return method.getOptions().getBeanMapping().unmappedSourcePolicy(); } private void reportErrorForUnmappedSourcePropertiesIfRequired() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index e8f19f91f6..6f36238a35 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -113,6 +113,7 @@ private static boolean isConsistent(BeanMappingGem gem, ExecutableElement method && !gem.nullValueMappingStrategy().hasValue() && !gem.subclassExhaustiveStrategy().hasValue() && !gem.unmappedTargetPolicy().hasValue() + && !gem.unmappedSourcePolicy().hasValue() && !gem.ignoreByDefault().hasValue() && !gem.builder().hasValue() ) { @@ -179,6 +180,15 @@ public ReportingPolicyGem unmappedTargetPolicy() { .orElse( next().unmappedTargetPolicy() ); } + @Override + public ReportingPolicyGem unmappedSourcePolicy() { + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::unmappedSourcePolicy ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .map( ReportingPolicyGem::valueOf ) + .orElse( next().unmappedSourcePolicy() ); + } + @Override public BuilderGem getBuilder() { return Optional.ofNullable( beanMapping ).map( BeanMappingGem::builder ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java index 9b244592a2..75231f3f53 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/UnmappedSourceTest.java @@ -110,4 +110,5 @@ public void shouldRaiseErrorDueToUnsetSourcePropertyWithPolicySetViaProcessorOpt ) public void shouldLeaveUnmappedSourcePropertyUnsetWithWarnPolicySetViaProcessorOption() { } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/BeanMappingSourcePolicyErroneousMapper.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/BeanMappingSourcePolicyErroneousMapper.java new file mode 100644 index 0000000000..eab3f8f425 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/BeanMappingSourcePolicyErroneousMapper.java @@ -0,0 +1,18 @@ +/* + * 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.unmappedsource.beanmapping; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +@Mapper(unmappedSourcePolicy = ReportingPolicy.WARN) +public interface BeanMappingSourcePolicyErroneousMapper { + + @BeanMapping(unmappedSourcePolicy = ReportingPolicy.ERROR) + Target map(Source source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/BeanMappingSourcePolicyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/BeanMappingSourcePolicyMapper.java new file mode 100644 index 0000000000..11e1f07132 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/BeanMappingSourcePolicyMapper.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.ap.test.unmappedsource.beanmapping; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface BeanMappingSourcePolicyMapper { + + BeanMappingSourcePolicyMapper MAPPER = + Mappers.getMapper( BeanMappingSourcePolicyMapper.class ); + + @BeanMapping(unmappedSourcePolicy = ReportingPolicy.WARN) + Target map(Source source); + + @BeanMapping(unmappedSourcePolicy = ReportingPolicy.IGNORE) + Target map2(Source source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/Source.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/Source.java new file mode 100644 index 0000000000..fc92d1a5c1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/Source.java @@ -0,0 +1,30 @@ +/* + * 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.unmappedsource.beanmapping; + +public class Source { + + private int foo; + + private int bar; + + public int getFoo() { + return foo; + } + + public void setFoo(int foo) { + this.foo = foo; + } + + public int getBar() { + return bar; + } + + public void setBar(int bar) { + this.bar = bar; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/Target.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/Target.java new file mode 100644 index 0000000000..5696455b46 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/Target.java @@ -0,0 +1,20 @@ +/* + * 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.unmappedsource.beanmapping; + +public class Target { + + private int foo; + + public int getFoo() { + return foo; + } + + public void setFoo(int foo) { + this.foo = foo; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/UnmappedSourceTest.java b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/UnmappedSourceTest.java new file mode 100644 index 0000000000..6eff388774 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/unmappedsource/beanmapping/UnmappedSourceTest.java @@ -0,0 +1,67 @@ +/* + * 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.unmappedsource.beanmapping; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.tools.Diagnostic.Kind; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +@IssueKey("3309") +class UnmappedSourceTest { + + @ProcessorTest + @WithClasses({ Source.class, Target.class, BeanMappingSourcePolicyMapper.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = BeanMappingSourcePolicyMapper.class, + kind = Kind.WARNING, + line = 20, + message = "Unmapped source property: \"bar\".") + } + ) + public void shouldCompileWithUnmappedSourcePolicySetToWarnWithBeanMapping() { + Source source = new Source(); + Source source2 = new Source(); + + source.setFoo( 10 ); + source.setBar( 20 ); + + source2.setFoo( 1 ); + source2.setBar( 2 ); + + Target target = BeanMappingSourcePolicyMapper.MAPPER.map( source ); + Target target2 = BeanMappingSourcePolicyMapper.MAPPER.map2( source2 ); + + assertThat( target ).isNotNull(); + assertThat( target.getFoo() ).isEqualTo( 10 ); + + assertThat( target2 ).isNotNull(); + assertThat( target2.getFoo() ).isEqualTo( 1 ); + } + + @ProcessorTest + @WithClasses({ Source.class, Target.class, BeanMappingSourcePolicyErroneousMapper.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = BeanMappingSourcePolicyErroneousMapper.class, + kind = Kind.ERROR, + line = 16, + message = "Unmapped source property: \"bar\".") + } + ) + public void shouldErrorWithUnmappedSourcePolicySetToErrorWithBeanMapping() { + } + +} From b2dc64136d39547dec3bf3386837be8de7130e0c Mon Sep 17 00:00:00 2001 From: Anton Erofeev Date: Tue, 1 Aug 2023 14:17:50 +0200 Subject: [PATCH 375/597] #3292 Simplify expressions, remove redundant expressions --- .../FullFeatureCompilationExclusionCliEnhancer.java | 2 +- .../itest/tests/GradleIncrementalCompilationTest.java | 2 +- .../conversion/BigDecimalToStringConversion.java | 2 +- .../conversion/BigIntegerToStringConversion.java | 2 +- .../ap/internal/model/AbstractMappingMethodBuilder.java | 4 +--- .../mapstruct/ap/internal/model/BeanMappingMethod.java | 6 ++---- .../model/NestedTargetPropertyMappingHolder.java | 2 +- .../org/mapstruct/ap/internal/model/PropertyMapping.java | 2 +- .../model/common/DateFormatValidatorFactory.java | 9 ++------- .../org/mapstruct/ap/internal/model/common/Type.java | 3 +-- .../ap/test/builder/lifecycle/MappingContext.java | 2 +- .../test/builder/parentchild/ParentChildBuilderTest.java | 2 +- .../ap/test/builder/simple/SimpleImmutablePerson.java | 4 ++-- .../java/org/mapstruct/ap/test/callbacks/BaseMapper.java | 2 +- .../mapstruct/ap/test/callbacks/CallbackMethodTest.java | 2 +- .../ap/test/callbacks/ClassContainingCallbacks.java | 2 +- .../returning/CallbacksWithReturnValuesTest.java | 7 +------ .../ap/test/callbacks/returning/NodeMapperContext.java | 6 +++--- .../ap/test/collection/adder/_target/Target2.java | 2 +- .../collection/defaultimplementation/NoSetterTarget.java | 4 ++-- .../java/org/mapstruct/ap/test/java8stream/Target.java | 2 +- .../java/org/mapstruct/ap/test/nestedbeans/HouseDto.java | 6 ++++-- .../ap/test/nestedbeans/maps/AntonymsDictionaryDto.java | 4 ++-- .../selection/resulttype/InheritanceSelectionTest.java | 2 +- .../org/mapstruct/ap/test/source/constants/Target.java | 2 +- .../mapstruct/ap/testutil/assertions/JavaFileAssert.java | 2 +- .../testutil/compilation/model/DiagnosticDescriptor.java | 4 ++-- .../ap/testutil/runner/JdkCompilingExtension.java | 2 +- .../ap/testutil/runner/ModifiableURLClassLoader.java | 2 +- 29 files changed, 40 insertions(+), 53 deletions(-) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java index e64b207990..c0ea96fd5c 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -44,7 +44,7 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces default: } - Collection result = new ArrayList( additionalExcludes.size() ); + Collection result = new ArrayList<>(additionalExcludes.size()); for ( int i = 0; i < additionalExcludes.size(); i++ ) { result.add( "-DadditionalExclude" + i + "=" + additionalExcludes.get( i ) ); } diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java index 0162582468..3d496906c0 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java @@ -62,7 +62,7 @@ private void replaceInFile(File file, CharSequence target, CharSequence replacem } private GradleRunner getRunner(String... additionalArguments) { - List fullArguments = new ArrayList( compileArgs ); + List fullArguments = new ArrayList<>(compileArgs); fullArguments.addAll( Arrays.asList( additionalArguments ) ); return runner.withArguments( fullArguments ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java index d696cf5bb5..92a93e3893 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java @@ -45,7 +45,7 @@ public String getToExpression(ConversionContext conversionContext) { public String getFromExpression(ConversionContext conversionContext) { if ( requiresDecimalFormat( conversionContext ) ) { StringBuilder sb = new StringBuilder(); - sb.append( "(" + bigDecimal( conversionContext ) + ") " ); + sb.append( "(" ).append( bigDecimal( conversionContext ) ).append( ") " ); appendDecimalFormatter( sb, conversionContext ); sb.append( ".parse( )" ); return sb.toString(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java index 9cb1cf41cb..df0a48a673 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java @@ -47,7 +47,7 @@ public String getToExpression(ConversionContext conversionContext) { public String getFromExpression(ConversionContext conversionContext) { if ( requiresDecimalFormat( conversionContext ) ) { StringBuilder sb = new StringBuilder(); - sb.append( "( (" + bigDecimal( conversionContext ) + ") " ); + sb.append( "( (" ).append( bigDecimal( conversionContext ) ).append( ") " ); appendDecimalFormatter( sb, conversionContext ); sb.append( ".parse( )" ); sb.append( " ).toBigInteger()" ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index c23dd50430..7329df5ab7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -130,9 +130,7 @@ public List getMethodAnnotations() { ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() ); - List annotations = new ArrayList<>(); - annotations.addAll( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); - return annotations; + return new ArrayList<>( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 11245b3376..00e1d9f9f9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -266,9 +266,7 @@ else if ( !method.isUpdateMethod() ) { Map readAccessors = sourceParameter.getType().getPropertyReadAccessors(); - for ( Entry entry : readAccessors.entrySet() ) { - unprocessedSourceProperties.put( entry.getKey(), entry.getValue() ); - } + unprocessedSourceProperties.putAll( readAccessors ); } // get bean mapping (when specified as annotation ) @@ -499,7 +497,7 @@ private boolean isCorrectlySealed(Type mappingSourceType) { new ArrayList<>( mappingSourceType.getPermittedSubclasses() ); method.getOptions().getSubclassMappings().forEach( subClassOption -> { for (Iterator iterator = unusedPermittedSubclasses.iterator(); - iterator.hasNext(); ) { + iterator.hasNext(); ) { if ( ctx.getTypeUtils().isSameType( iterator.next(), subClassOption.getSource() ) ) { iterator.remove(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index b7bfc69780..20825c2dd9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -607,7 +607,7 @@ else if ( mappingsKeyedByProperty.isEmpty() && nonNested.isEmpty() ) { * @param hasNoMappings parameter indicating whether there were any extracted mappings for this target property * @param sourceParameter the source parameter for which the grouping is being done * - * @return a list with valid single target references + * @return a set with valid single target references */ private Set extractSingleTargetReferencesToUseAndPopulateSourceParameterMappings( Set singleTargetReferences, Set sourceParameterMappings, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index bedb37cc8f..b81cf55447 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -1115,7 +1115,7 @@ private PropertyMapping(String name, String sourceBeanName, String targetWriteAc this.targetType = targetType; this.assignment = assignment; - this.dependsOn = dependsOn != null ? dependsOn : Collections.emptySet(); + this.dependsOn = dependsOn != null ? dependsOn : Collections.emptySet(); this.defaultValueAssignment = defaultValueAssignment; this.constructorMapping = constructorMapping; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java index 09e4957aad..4d54f3c3e9 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java @@ -61,13 +61,8 @@ else if ( isJodaDateTimeSupposed( sourceType, targetType ) ) { dateFormatValidator = new JodaTimeDateFormatValidator(); } else { - dateFormatValidator = new DateFormatValidator() { - @Override - public DateFormatValidationResult validate(String dateFormat) { - return new DateFormatValidationResult( true, Message.GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK, - sourceType, targetType ); - } - }; + dateFormatValidator = dateFormat -> new DateFormatValidationResult( + true, Message.GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK, sourceType, targetType ); } return dateFormatValidator; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 933f1f6ce0..a6d942748d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -684,10 +684,9 @@ public PresenceCheckAccessor getPresenceChecker(String propertyName) { */ public Map getPropertyReadAccessors() { if ( readAccessors == null ) { - Map modifiableGetters = new LinkedHashMap<>(); Map recordAccessors = filters.recordAccessorsIn( getRecordComponents() ); - modifiableGetters.putAll( recordAccessors ); + Map modifiableGetters = new LinkedHashMap<>(recordAccessors); List getterList = filters.getterMethodsIn( getAllMethods() ); for ( ReadAccessor getter : getterList ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/MappingContext.java b/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/MappingContext.java index 079be90a81..2065bad478 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/MappingContext.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/lifecycle/MappingContext.java @@ -18,7 +18,7 @@ */ public class MappingContext { - private final List invokedMethods = new ArrayList(); + private final List invokedMethods = new ArrayList<>(); @BeforeMapping public void beforeWithoutParameters() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/parentchild/ParentChildBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/parentchild/ParentChildBuilderTest.java index dd45573a59..562770a2bb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/parentchild/ParentChildBuilderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/parentchild/ParentChildBuilderTest.java @@ -26,7 +26,7 @@ public class ParentChildBuilderTest { public void testParentChildBuilderMapper() { final MutableParent parent = new MutableParent(); parent.setCount( 4 ); - parent.setChildren( new ArrayList() ); + parent.setChildren( new ArrayList<>() ); parent.getChildren().add( new MutableChild( "Phineas" ) ); parent.getChildren().add( new MutableChild( "Ferb" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutablePerson.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutablePerson.java index daa3dfad0b..2fe2c8ded2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutablePerson.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutablePerson.java @@ -22,7 +22,7 @@ public class SimpleImmutablePerson { this.job = builder.job; this.city = builder.city; this.address = builder.address; - this.children = new ArrayList( builder.children ); + this.children = new ArrayList<>(builder.children); } public static Builder builder() { @@ -59,7 +59,7 @@ public static class Builder { private String job; private String city; private String address; - private List children = new ArrayList(); + private List children = new ArrayList<>(); public Builder age(int age) { this.age = age; diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/BaseMapper.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/BaseMapper.java index bee462accc..4e02045ed5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/BaseMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/BaseMapper.java @@ -24,7 +24,7 @@ public abstract class BaseMapper { @Qualified public abstract Target sourceToTargetQualified(Source source); - private static final List INVOCATIONS = new ArrayList(); + private static final List INVOCATIONS = new ArrayList<>(); @BeforeMapping public void noArgsBeforeMapping() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/CallbackMethodTest.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/CallbackMethodTest.java index 7026a335d8..825b359152 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/CallbackMethodTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/CallbackMethodTest.java @@ -111,7 +111,7 @@ public void callbackMethodsForEnumMappingCalled() { SourceEnum source = SourceEnum.B; TargetEnum target = SourceTargetMapper.INSTANCE.toTargetEnum( source ); - List invocations = new ArrayList(); + List invocations = new ArrayList<>(); invocations.addAll( allBeforeMappingMethods( source, target, TargetEnum.class ) ); invocations.addAll( allAfterMappingMethods( source, target, TargetEnum.class ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/ClassContainingCallbacks.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/ClassContainingCallbacks.java index 762247045d..2889a4ad30 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/ClassContainingCallbacks.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/ClassContainingCallbacks.java @@ -18,7 +18,7 @@ * @author Andreas Gudian */ public class ClassContainingCallbacks { - private static final List INVOCATIONS = new ArrayList(); + private static final List INVOCATIONS = new ArrayList<>(); @BeforeMapping public void noArgsBeforeMapping() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java index 7d6eaacb79..10d2a29cd7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/CallbacksWithReturnValuesTest.java @@ -50,12 +50,7 @@ void updatingWithDefaultHandlingRaisesStackOverflowError() { @ProcessorTest void mappingWithContextCorrectlyResolvesCycles() { final AtomicReference contextLevel = new AtomicReference<>( null ); - ContextListener contextListener = new ContextListener() { - @Override - public void methodCalled(Integer level, String method, Object source, Object target) { - contextLevel.set( level ); - } - }; + ContextListener contextListener = (level, method, source, target) -> contextLevel.set( level ); NodeMapperContext.addContextListener( contextListener ); try { diff --git a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperContext.java b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperContext.java index 40a1a63937..325778b85e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperContext.java +++ b/processor/src/test/java/org/mapstruct/ap/test/callbacks/returning/NodeMapperContext.java @@ -19,11 +19,11 @@ * @author Pascal Grün */ public class NodeMapperContext { - private static final ThreadLocal LEVEL = new ThreadLocal(); - private static final ThreadLocal> MAPPING = new ThreadLocal>(); + private static final ThreadLocal LEVEL = new ThreadLocal<>(); + private static final ThreadLocal> MAPPING = new ThreadLocal<>(); /** Only for test-inspection */ - private static final List LISTENERS = new CopyOnWriteArrayList(); + private static final List LISTENERS = new CopyOnWriteArrayList<>(); private NodeMapperContext() { // Only allow static access diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/_target/Target2.java b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/_target/Target2.java index 850c6ee94d..bd6e8f9979 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/adder/_target/Target2.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/adder/_target/Target2.java @@ -16,7 +16,7 @@ */ public class Target2 { - private List attributes = new ArrayList(); + private List attributes = new ArrayList<>(); public Foo addAttribute( Foo foo ) { attributes.add( foo ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/NoSetterTarget.java b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/NoSetterTarget.java index 562966dfd1..817879fa01 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/NoSetterTarget.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/NoSetterTarget.java @@ -15,8 +15,8 @@ * */ public class NoSetterTarget { - private List listValues = new ArrayList(); - private Map mapValues = new HashMap(); + private List listValues = new ArrayList<>(); + private Map mapValues = new HashMap<>(); public List getListValues() { return listValues; diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/Target.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/Target.java index 2d63c3f166..49b23530df 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/Target.java @@ -77,7 +77,7 @@ public Set getColours() { public List getStringListNoSetter() { if ( stringListNoSetter == null ) { - stringListNoSetter = new ArrayList(); + stringListNoSetter = new ArrayList<>(); } return stringListNoSetter; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/HouseDto.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/HouseDto.java index 1d92eb7e4f..71beedaa93 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/HouseDto.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/HouseDto.java @@ -5,6 +5,8 @@ */ package org.mapstruct.ap.test.nestedbeans; +import java.util.Objects; + public class HouseDto { private String name; @@ -58,10 +60,10 @@ public boolean equals(Object o) { if ( year != houseDto.year ) { return false; } - if ( name != null ? !name.equals( houseDto.name ) : houseDto.name != null ) { + if ( !Objects.equals( name, houseDto.name ) ) { return false; } - return roof != null ? roof.equals( houseDto.roof ) : houseDto.roof == null; + return Objects.equals( roof, houseDto.roof ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/maps/AntonymsDictionaryDto.java b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/maps/AntonymsDictionaryDto.java index 33ccfbc1cc..9ac7913c9f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/maps/AntonymsDictionaryDto.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedbeans/maps/AntonymsDictionaryDto.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.nestedbeans.maps; import java.util.Map; +import java.util.Objects; public class AntonymsDictionaryDto { private Map antonyms; @@ -36,8 +37,7 @@ public boolean equals(Object o) { AntonymsDictionaryDto antonymsDictionaryDto = (AntonymsDictionaryDto) o; - return antonyms != null ? antonyms.equals( antonymsDictionaryDto.antonyms ) : - antonymsDictionaryDto.antonyms == null; + return Objects.equals( antonyms, antonymsDictionaryDto.antonyms ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java index 05245bafe0..8cd58f0664 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/resulttype/InheritanceSelectionTest.java @@ -192,7 +192,7 @@ public void testShouldSelectResultTypeInCaseOfAmbiguityForIterable() { }) public void testShouldSelectResultTypeInCaseOfAmbiguityForMap() { - Map source = new HashMap(); + Map source = new HashMap<>(); source.put( new AppleDto( "GoldenDelicious" ), new AppleDto( "AppleDto" ) ); Map result = FruitFamilyMapper.INSTANCE.mapToGoldenDeliciousMap( source ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/Target.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/Target.java index 98ca13eb0b..7897fe2bd6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/Target.java @@ -20,7 +20,7 @@ public class Target { private int integerConstant; private Long longWrapperConstant; private Date dateConstant; - private List nameConstants = new ArrayList(); + private List nameConstants = new ArrayList<>(); private CountryEnum country; public String getPropertyThatShouldBeMapped() { diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java b/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java index 2bcfbd94d3..e6eabd9f84 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java @@ -59,7 +59,7 @@ public AbstractCharSequenceAssert content() { return Assertions.assertThat( FileUtils.readFileToString( actual, StandardCharsets.UTF_8 ) ); } catch ( IOException e ) { - failWithMessage( "Unable to read" + actual.toString() + ". Exception: " + e.getMessage() ); + failWithMessage( "Unable to read" + actual + ". Exception: " + e.getMessage() ); } return null; } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java index 94dda74708..ecfec14a16 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java @@ -131,7 +131,7 @@ private static URI getUri(javax.tools.Diagnostic diagn //Make the URI absolute in case it isn't (the case with JDK 6) try { - return new URI( "file:" + uri.toString() ); + return new URI( "file:" + uri ); } catch ( URISyntaxException e ) { throw new RuntimeException( e ); @@ -189,7 +189,7 @@ public boolean equals(Object obj) { if ( kind != other.kind ) { return false; } - if ( line != other.line ) { + if ( !Objects.equals( line, other.line ) ) { return false; } if ( !Objects.equals( message, other.message ) ) { diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java index 7b44a4733b..5185bfb7ed 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java @@ -135,7 +135,7 @@ protected List filterExpectedDiagnostics(List filtered = new ArrayList( expectedDiagnostics.size() ); + List filtered = new ArrayList<>(expectedDiagnostics.size()); // The JDK 8 compiler only reports the first message of kind ERROR that is reported for one source file line, // so we filter out the surplus diagnostics. The input list is already sorted by file name and line number, diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/ModifiableURLClassLoader.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/ModifiableURLClassLoader.java index 9cc41fe134..2dd5d5b8e7 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/ModifiableURLClassLoader.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/ModifiableURLClassLoader.java @@ -31,7 +31,7 @@ final class ModifiableURLClassLoader extends URLClassLoader { tryRegisterAsParallelCapable(); } - private final ConcurrentMap addedURLs = new ConcurrentHashMap(); + private final ConcurrentMap addedURLs = new ConcurrentHashMap<>(); ModifiableURLClassLoader(ClassLoader parent) { super( new URL[] { }, parent ); From 812faeef5100ea28db6081c9f7b5b22b4450bf95 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Tue, 1 Aug 2023 09:53:58 +0200 Subject: [PATCH 376/597] Use presence checks for checking source parameter presence Instead of explicitly doing a null check use a PresenceCheck mechanism for * BeanMappingMethod * ContainerMappingMethod * MapMappingMethod --- .../ap/internal/model/BeanMappingMethod.java | 49 +++++++++++------ .../model/ContainerMappingMethod.java | 26 ++++++++-- .../ap/internal/model/MapMappingMethod.java | 26 ++++++++-- .../model/MethodReferencePresenceCheck.java | 15 ++++++ .../model/common/NegatePresenceCheck.java | 52 +++++++++++++++++++ .../internal/model/common/PresenceCheck.java | 2 + .../AllPresenceChecksPresenceCheck.java | 6 +++ .../presence/JavaExpressionPresenceCheck.java | 6 +++ .../model/presence/NullPresenceCheck.java | 16 ++++++ .../model/presence/SuffixPresenceCheck.java | 16 ++++++ .../ap/internal/model/BeanMappingMethod.ftl | 20 +++---- .../internal/model/IterableMappingMethod.ftl | 2 +- .../ap/internal/model/MapMappingMethod.ftl | 2 +- .../model/MethodReferencePresenceCheck.ftl | 2 +- .../ap/internal/model/StreamMappingMethod.ftl | 2 +- .../model/common/NegatePresenceCheck.ftl | 9 ++++ .../model/presence/NullPresenceCheck.ftl | 2 +- .../model/presence/SuffixPresenceCheck.ftl | 2 +- 18 files changed, 214 insertions(+), 41 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.ftl diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 00e1d9f9f9..ee347ef35e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -46,11 +46,13 @@ import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.ParameterBinding; +import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder; +import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; @@ -88,6 +90,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { private final List propertyMappings; private final Map> mappingsByParameter; private final Map> constructorMappingsByParameter; + private final Map presenceChecksByParameter; private final List constantMappings; private final List constructorConstantMappings; private final List subclassMappings; @@ -1869,11 +1872,19 @@ private BeanMappingMethod(Method method, // parameter mapping. this.mappingsByParameter = new HashMap<>(); this.constantMappings = new ArrayList<>( propertyMappings.size() ); + this.presenceChecksByParameter = new LinkedHashMap<>(); this.constructorMappingsByParameter = new LinkedHashMap<>(); this.constructorConstantMappings = new ArrayList<>(); - Set sourceParameterNames = getSourceParameters().stream() - .map( Parameter::getName ) - .collect( Collectors.toSet() ); + Set sourceParameterNames = new HashSet<>(); + for ( Parameter sourceParameter : getSourceParameters() ) { + sourceParameterNames.add( sourceParameter.getName() ); + if ( !sourceParameter.getType().isPrimitive() ) { + presenceChecksByParameter.put( + sourceParameter.getName(), + new NullPresenceCheck( sourceParameter.getName() ) + ); + } + } for ( PropertyMapping mapping : propertyMappings ) { if ( mapping.isConstructorMapping() ) { if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) { @@ -1984,44 +1995,50 @@ public Set getImportTypes() { return types; } - public List getSourceParametersExcludingPrimitives() { - return getSourceParameters().stream() - .filter( parameter -> !parameter.getType().isPrimitive() ) - .collect( Collectors.toList() ); + public Collection getSourcePresenceChecks() { + return presenceChecksByParameter.values(); + } + + public Map getPresenceChecksByParameter() { + return presenceChecksByParameter; + } + + public PresenceCheck getPresenceCheckByParameter(Parameter parameter) { + return presenceChecksByParameter.get( parameter.getName() ); } - public List getSourceParametersNeedingNullCheck() { + public List getSourceParametersNeedingPresenceCheck() { return getSourceParameters().stream() - .filter( this::needsNullCheck ) + .filter( this::needsPresenceCheck ) .collect( Collectors.toList() ); } - public List getSourceParametersNotNeedingNullCheck() { + public List getSourceParametersNotNeedingPresenceCheck() { return getSourceParameters().stream() - .filter( parameter -> !needsNullCheck( parameter ) ) + .filter( parameter -> !needsPresenceCheck( parameter ) ) .collect( Collectors.toList() ); } - private boolean needsNullCheck(Parameter parameter) { - if ( parameter.getType().isPrimitive() ) { + private boolean needsPresenceCheck(Parameter parameter) { + if ( !presenceChecksByParameter.containsKey( parameter.getName() ) ) { return false; } List mappings = propertyMappingsByParameter( parameter ); - if ( mappings.size() == 1 && doesNotNeedNullCheckForSourceParameter( mappings.get( 0 ) ) ) { + if ( mappings.size() == 1 && doesNotNeedPresenceCheckForSourceParameter( mappings.get( 0 ) ) ) { return false; } mappings = constructorPropertyMappingsByParameter( parameter ); - if ( mappings.size() == 1 && doesNotNeedNullCheckForSourceParameter( mappings.get( 0 ) ) ) { + if ( mappings.size() == 1 && doesNotNeedPresenceCheckForSourceParameter( mappings.get( 0 ) ) ) { return false; } return true; } - private boolean doesNotNeedNullCheckForSourceParameter(PropertyMapping mapping) { + private boolean doesNotNeedPresenceCheckForSourceParameter(PropertyMapping mapping) { if ( mapping.getAssignment().isCallingUpdateMethod() ) { // If the mapping assignment is calling an update method then we should do a null check // in the bean mapping diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java index 21e547eea0..7ecb7b0ed1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java @@ -12,7 +12,9 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.util.Strings; @@ -30,6 +32,8 @@ public abstract class ContainerMappingMethod extends NormalTypeMappingMethod { private final SelectionParameters selectionParameters; private final String index1Name; private final String index2Name; + private final Parameter sourceParameter; + private final PresenceCheck sourceParameterPresenceCheck; private IterableCreation iterableCreation; ContainerMappingMethod(Method method, List annotations, @@ -45,16 +49,30 @@ public abstract class ContainerMappingMethod extends NormalTypeMappingMethod { this.selectionParameters = selectionParameters; this.index1Name = Strings.getSafeVariableName( "i", existingVariables ); this.index2Name = Strings.getSafeVariableName( "j", existingVariables ); - } - public Parameter getSourceParameter() { + Parameter sourceParameter = null; for ( Parameter parameter : getParameters() ) { if ( !parameter.isMappingTarget() && !parameter.isMappingContext() ) { - return parameter; + sourceParameter = parameter; + break; } } - throw new IllegalStateException( "Method " + this + " has no source parameter." ); + if ( sourceParameter == null ) { + throw new IllegalStateException( "Method " + this + " has no source parameter." ); + } + + this.sourceParameter = sourceParameter; + this.sourceParameterPresenceCheck = new NullPresenceCheck( this.sourceParameter.getName() ); + + } + + public Parameter getSourceParameter() { + return sourceParameter; + } + + public PresenceCheck getSourceParameterPresenceCheck() { + return sourceParameterPresenceCheck; } public IterableCreation getIterableCreation() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java index 85766e2eca..42dbf826d0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java @@ -15,8 +15,10 @@ import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; @@ -35,6 +37,8 @@ public class MapMappingMethod extends NormalTypeMappingMethod { private final Assignment keyAssignment; private final Assignment valueAssignment; + private final Parameter sourceParameter; + private final PresenceCheck sourceParameterPresenceCheck; private IterableCreation iterableCreation; public static class Builder extends AbstractMappingMethodBuilder { @@ -235,16 +239,28 @@ private MapMappingMethod(Method method, List annotations, this.keyAssignment = keyAssignment; this.valueAssignment = valueAssignment; - } - - public Parameter getSourceParameter() { + Parameter sourceParameter = null; for ( Parameter parameter : getParameters() ) { if ( !parameter.isMappingTarget() && !parameter.isMappingContext() ) { - return parameter; + sourceParameter = parameter; + break; } } - throw new IllegalStateException( "Method " + this + " has no source parameter." ); + if ( sourceParameter == null ) { + throw new IllegalStateException( "Method " + this + " has no source parameter." ); + } + + this.sourceParameter = sourceParameter; + this.sourceParameterPresenceCheck = new NullPresenceCheck( this.sourceParameter.getName() ); + } + + public Parameter getSourceParameter() { + return sourceParameter; + } + + public PresenceCheck getSourceParameterPresenceCheck() { + return sourceParameterPresenceCheck; } public List getSourceElementTypes() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java index e6adceab03..879a76efbe 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java @@ -18,9 +18,15 @@ public class MethodReferencePresenceCheck extends ModelElement implements PresenceCheck { protected final MethodReference methodReference; + protected final boolean negate; public MethodReferencePresenceCheck(MethodReference methodReference) { + this( methodReference, false ); + } + + public MethodReferencePresenceCheck(MethodReference methodReference, boolean negate) { this.methodReference = methodReference; + this.negate = negate; } @Override @@ -32,6 +38,15 @@ public MethodReference getMethodReference() { return methodReference; } + public boolean isNegate() { + return negate; + } + + @Override + public PresenceCheck negate() { + return new MethodReferencePresenceCheck( methodReference, true ); + } + @Override public boolean equals(Object o) { if ( this == o ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.java new file mode 100644 index 0000000000..f8844edcdd --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.java @@ -0,0 +1,52 @@ +/* + * 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.internal.model.common; + +import java.util.Objects; +import java.util.Set; + +/** + * @author Filip Hrisafov + */ +public class NegatePresenceCheck extends ModelElement implements PresenceCheck { + + private final PresenceCheck presenceCheck; + + public NegatePresenceCheck(PresenceCheck presenceCheck) { + this.presenceCheck = presenceCheck; + } + + public PresenceCheck getPresenceCheck() { + return presenceCheck; + } + + @Override + public Set getImportTypes() { + return presenceCheck.getImportTypes(); + } + + @Override + public PresenceCheck negate() { + return presenceCheck; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + NegatePresenceCheck that = (NegatePresenceCheck) o; + return Objects.equals( presenceCheck, that.presenceCheck ); + } + + @Override + public int hashCode() { + return Objects.hash( presenceCheck ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/PresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/PresenceCheck.java index e77e24f218..ec286480d9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/PresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/PresenceCheck.java @@ -21,4 +21,6 @@ public interface PresenceCheck { */ Set getImportTypes(); + PresenceCheck negate(); + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java index df8b008032..6bb191214f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java @@ -11,6 +11,7 @@ import java.util.Set; import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.NegatePresenceCheck; import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; @@ -29,6 +30,11 @@ public Collection getPresenceChecks() { return presenceChecks; } + @Override + public PresenceCheck negate() { + return new NegatePresenceCheck( this ); + } + @Override public Set getImportTypes() { Set importTypes = new HashSet<>(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java index d4f52c9f27..e100be7e79 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java @@ -10,6 +10,7 @@ import java.util.Set; import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.NegatePresenceCheck; import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; @@ -28,6 +29,11 @@ public String getJavaExpression() { return javaExpression; } + @Override + public PresenceCheck negate() { + return new NegatePresenceCheck( this ); + } + @Override public Set getImportTypes() { return Collections.emptySet(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java index c9cae61b7c..3496486e1d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java @@ -19,20 +19,36 @@ public class NullPresenceCheck extends ModelElement implements PresenceCheck { private final String sourceReference; + private final boolean negate; public NullPresenceCheck(String sourceReference) { this.sourceReference = sourceReference; + this.negate = false; + } + + public NullPresenceCheck(String sourceReference, boolean negate) { + this.sourceReference = sourceReference; + this.negate = negate; } public String getSourceReference() { return sourceReference; } + public boolean isNegate() { + return negate; + } + @Override public Set getImportTypes() { return Collections.emptySet(); } + @Override + public PresenceCheck negate() { + return new NullPresenceCheck( sourceReference, !negate ); + } + @Override public boolean equals(Object o) { if ( this == o ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java index 3710e97173..0f75fbba69 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java @@ -10,6 +10,7 @@ import java.util.Set; import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.NegatePresenceCheck; import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; @@ -20,10 +21,16 @@ public class SuffixPresenceCheck extends ModelElement implements PresenceCheck { private final String sourceReference; private final String suffix; + private final boolean negate; public SuffixPresenceCheck(String sourceReference, String suffix) { + this( sourceReference, suffix, false ); + } + + public SuffixPresenceCheck(String sourceReference, String suffix, boolean negate) { this.sourceReference = sourceReference; this.suffix = suffix; + this.negate = negate; } public String getSourceReference() { @@ -34,6 +41,15 @@ public String getSuffix() { return suffix; } + public boolean isNegate() { + return negate; + } + + @Override + public PresenceCheck negate() { + return new NegatePresenceCheck( this ); + } + @Override public Set getImportTypes() { return Collections.emptySet(); diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 0f9b96bb66..8fb5bfdef0 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -27,8 +27,8 @@ - <#if !mapNullToDefault && !sourceParametersExcludingPrimitives.empty> - if ( <#list sourceParametersExcludingPrimitives as sourceParam>${sourceParam.name} == null<#if sourceParam_has_next> && ) { + <#if !mapNullToDefault && !sourcePresenceChecks.empty> + if ( <#list sourcePresenceChecks as sourcePresenceCheck><@includeModel object=sourcePresenceCheck.negate() /><#if sourcePresenceCheck_has_next> && ) { return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#if finalizerMethod??>.<@includeModel object=finalizerMethod /><#else>null; } @@ -47,19 +47,19 @@ <#if !existingInstanceMapping> <#if hasConstructorMappings()> <#if (sourceParameters?size > 1)> - <#list sourceParametersNeedingNullCheck as sourceParam> + <#list sourceParametersNeedingPresenceCheck as sourceParam> <#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)> <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; - if ( ${sourceParam.name} != null ) { + if ( <@includeModel object=getPresenceCheckByParameter(sourceParam) /> ) { <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> } - <#list sourceParametersNotNeedingNullCheck as sourceParam> + <#list sourceParametersNotNeedingPresenceCheck as sourceParam> <#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)> <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; @@ -71,7 +71,7 @@ <#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping> <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; - <#if mapNullToDefault>if ( ${sourceParameters[0].name} != null ) { + <#if mapNullToDefault>if ( <@includeModel object=getPresenceCheckByParameter(sourceParameters[0]) /> ) { <#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping> <@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> @@ -100,16 +100,16 @@ <#if (sourceParameters?size > 1)> - <#list sourceParametersNeedingNullCheck as sourceParam> + <#list sourceParametersNeedingPresenceCheck as sourceParam> <#if (propertyMappingsByParameter(sourceParam)?size > 0)> - if ( ${sourceParam.name} != null ) { + if ( <@includeModel object=getPresenceCheckByParameter(sourceParam) /> ) { <#list propertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping targetBeanName=resultName existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> } - <#list sourceParametersNotNeedingNullCheck as sourceParam> + <#list sourceParametersNotNeedingPresenceCheck as sourceParam> <#if (propertyMappingsByParameter(sourceParam)?size > 0)> <#list propertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping targetBeanName=resultName existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> @@ -117,7 +117,7 @@ <#else> - <#if mapNullToDefault>if ( ${sourceParameters[0].name} != null ) { + <#if mapNullToDefault>if ( <@includeModel object=getPresenceCheckByParameter(sourceParameters[0]) /> ) { <#list propertyMappingsByParameter(sourceParameters[0]) as propertyMapping> <@includeModel object=propertyMapping targetBeanName=resultName existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> 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 9af6e762bf..4d08c44b38 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 @@ -17,7 +17,7 @@ - if ( ${sourceParameter.name} == null ) { + if ( <@includeModel object=sourceParameterPresenceCheck.negate() /> ) { <#if !mapNullToDefault> return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#else>null; <#else> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl index 0e388da103..1227dbd27a 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl @@ -17,7 +17,7 @@ - if ( ${sourceParameter.name} == null ) { + if ( <@includeModel object=sourceParameterPresenceCheck.negate() /> ) { <#if !mapNullToDefault> return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#else>null; <#else> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl index 9a1c7b24ae..0452e1699a 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl @@ -6,7 +6,7 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MethodReferencePresenceCheck" --> -<@includeModel object=methodReference +<#if isNegate()>!<@includeModel object=methodReference presenceCheck=true targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl index 29977bdd5d..7d0080a1d2 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl @@ -18,7 +18,7 @@ - if ( ${sourceParameter.name} == null ) { + if ( <@includeModel object=sourceParameterPresenceCheck.negate() /> ) { <#if !mapNullToDefault> return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#else>null; <#else> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.ftl new file mode 100644 index 0000000000..6951952487 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.ftl @@ -0,0 +1,9 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.common.NegatePresenceCheck" --> +!( <@includeModel object=presenceCheck /> ) \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.ftl index 6e5494c735..ebd9f12eca 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.ftl @@ -6,4 +6,4 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.NullPresenceCheck" --> -${sourceReference} != null \ No newline at end of file +${sourceReference} <#if isNegate()>==<#else>!= null \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.ftl index 103b4e7f9c..8eddad3485 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.ftl @@ -6,4 +6,4 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.SuffixPresenceCheck" --> -${sourceReference}${suffix} \ No newline at end of file +<#if isNegate()>!${sourceReference}${suffix} \ No newline at end of file From 721288140aa1c1210de222224b44f23e82be0c53 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Fri, 4 Aug 2023 10:14:53 +0200 Subject: [PATCH 377/597] Feature/2663 (#3007) #2663 Fix for 2-step mapping with generics. --------- Co-authored-by: Ben Zegveld --- .../ap/internal/model/common/Type.java | 116 +++++++++++++++++- .../creation/MappingResolverImpl.java | 2 +- .../ap/test/bugs/_2663/Issue2663Mapper.java | 31 +++++ .../ap/test/bugs/_2663/Issue2663Test.java | 47 +++++++ .../ap/test/bugs/_2663/JsonNullable.java | 44 +++++++ .../ap/test/bugs/_2663/Nullable.java | 45 +++++++ .../ap/test/bugs/_2663/NullableHelper.java | 23 ++++ .../mapstruct/ap/test/bugs/_2663/Request.java | 44 +++++++ .../ap/test/bugs/_2663/RequestDto.java | 44 +++++++ 9 files changed, 390 insertions(+), 6 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Issue2663Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Issue2663Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/JsonNullable.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Nullable.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/NullableHelper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Request.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/RequestDto.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index a6d942748d..c18574fcfb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -605,6 +605,21 @@ public Type withoutBounds() { ); } + private Type replaceGeneric(Type oldGenericType, Type newType) { + if ( !typeParameters.contains( oldGenericType ) || newType == null ) { + return this; + } + newType = newType.getBoxedEquivalent(); + TypeMirror[] replacedTypeMirrors = new TypeMirror[typeParameters.size()]; + for ( int i = 0; i < typeParameters.size(); i++ ) { + Type typeParameter = typeParameters.get( i ); + replacedTypeMirrors[i] = + typeParameter.equals( oldGenericType ) ? newType.typeMirror : typeParameter.typeMirror; + } + + return typeFactory.getType( typeUtils.getDeclaredType( typeElement, replacedTypeMirrors ) ); + } + /** * Whether this type is assignable to the given other type, considering the "extends / upper bounds" * as well. @@ -1377,9 +1392,9 @@ public boolean isLiteral() { /** * Steps through the declaredType in order to find a match for this typeVar Type. It aligns with - * the provided parameterized type where this typeVar type is used. - * - * For example: + * the provided parameterized type where this typeVar type is used.
      + *
      + * For example:

            * {@code
            * this: T
            * declaredType: JAXBElement
      @@ -1392,12 +1407,13 @@ public boolean isLiteral() {
            * parameterizedType: Callable
            * return: BigDecimal
            * }
      +     * 
      * * @param declared the type * @param parameterized the parameterized type * - * @return - the same type when this is not a type var in the broadest sense (T, T[], or ? extends T) - * - the matching parameter in the parameterized type when this is a type var when found + * @return - the same type when this is not a type var in the broadest sense (T, T[], or ? extends T)
      + * - the matching parameter in the parameterized type when this is a type var when found
      * - null in all other cases */ public ResolvedPair resolveParameterToType(Type declared, Type parameterized) { @@ -1408,6 +1424,96 @@ public ResolvedPair resolveParameterToType(Type declared, Type parameterized) { return new ResolvedPair( this, this ); } + /** + * Resolves generic types using the declared and parameterized types as input.
      + *
      + * For example: + *
      +     * {@code
      +     * this: T
      +     * declaredType: JAXBElement
      +     * parameterizedType: JAXBElement
      +     * result: Integer
      +     *
      +     * this: List
      +     * declaredType: JAXBElement
      +     * parameterizedType: JAXBElement
      +     * result: List
      +     *
      +     * this: List
      +     * declaredType: JAXBElement
      +     * parameterizedType: JAXBElement
      +     * result: List
      +     *
      +     * this: List>
      +     * declaredType: JAXBElement
      +     * parameterizedType: JAXBElement
      +     * result: List>
      +     * }
      +     * 
      + * It also works for partial matching.
      + *
      + * For example: + *
      +     * {@code
      +     * this: Map
      +     * declaredType: JAXBElement
      +     * parameterizedType: JAXBElement
      +     * result: Map
      +     * }
      +     * 
      + * It also works with multiple parameters at both sides.
      + *
      + * For example when reversing Key/Value for a Map: + *
      +     * {@code
      +     * this: Map
      +     * declaredType: HashMap
      +     * parameterizedType: HashMap
      +     * result: Map
      +     * }
      +     * 
      + * + * Mismatch result examples: + *
      +     * {@code
      +     * this: T
      +     * declaredType: JAXBElement
      +     * parameterizedType: JAXBElement
      +     * result: null
      +     *
      +     * this: List
      +     * declaredType: JAXBElement
      +     * parameterizedType: JAXBElement
      +     * result: List
      +     * }
      +     * 
      + * + * @param declared the type + * @param parameterized the parameterized type + * + * @return - the result of {@link #resolveParameterToType(Type, Type)} when this type itself is a type var.
      + * - the type but then with the matching type parameters replaced.
      + * - the same type when this type does not contain matching type parameters. + */ + public Type resolveGenericTypeParameters(Type declared, Type parameterized) { + if ( isTypeVar() || isArrayTypeVar() || isWildCardBoundByTypeVar() ) { + return resolveParameterToType( declared, parameterized ).getMatch(); + } + Type resultType = this; + for ( Type generic : getTypeParameters() ) { + if ( generic.isTypeVar() || generic.isArrayTypeVar() || generic.isWildCardBoundByTypeVar() ) { + ResolvedPair resolveParameterToType = generic.resolveParameterToType( declared, parameterized ); + resultType = resultType.replaceGeneric( generic, resolveParameterToType.getMatch() ); + } + else { + Type replacementType = generic.resolveParameterToType( declared, parameterized ).getMatch(); + resultType = resultType.replaceGeneric( generic, replacementType ); + } + } + return resultType; + } + public boolean isWildCardBoundByTypeVar() { return ( hasExtendsBound() || hasSuperBound() ) && getTypeBound().isTypeVar(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 586465d5f1..32fa1af931 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -825,7 +825,7 @@ private MethodMethod getBestMatch(Type sourceType, Type targetType, Best for ( T2 yCandidate : yMethods ) { Type ySourceType = yCandidate.getMappingSourceType(); - ySourceType = ySourceType.resolveParameterToType( targetType, yCandidate.getResultType() ).getMatch(); + ySourceType = ySourceType.resolveGenericTypeParameters( targetType, yCandidate.getResultType() ); Type yTargetType = yCandidate.getResultType(); if ( ySourceType == null || !yTargetType.isRawAssignableTo( targetType ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Issue2663Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Issue2663Mapper.java new file mode 100644 index 0000000000..0ac236372a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Issue2663Mapper.java @@ -0,0 +1,31 @@ +/* + * 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._2663; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper( uses = NullableHelper.class ) +public interface Issue2663Mapper { + + Issue2663Mapper INSTANCE = Mappers.getMapper( Issue2663Mapper.class ); + + Request map(RequestDto dto); + + default JsonNullable mapJsonNullableChildren(JsonNullable dtos) { + if ( dtos.isPresent() ) { + return JsonNullable.of( mapChild( dtos.get() ) ); + } + else { + return JsonNullable.undefined(); + } + } + + Request.ChildRequest mapChild(RequestDto.ChildRequestDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Issue2663Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Issue2663Test.java new file mode 100644 index 0000000000..efd14b1d36 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Issue2663Test.java @@ -0,0 +1,47 @@ +/* + * 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._2663; + +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 Filip Hrisafov + */ +@IssueKey( "2663" ) +@WithClasses({ + Issue2663Mapper.class, + JsonNullable.class, + Nullable.class, + NullableHelper.class, + Request.class, + RequestDto.class +}) +public class Issue2663Test { + + @ProcessorTest + public void shouldUnpackGenericsCorrectly() { + RequestDto dto = new RequestDto(); + dto.setName( JsonNullable.of( "Tester" ) ); + + Request request = Issue2663Mapper.INSTANCE.map( dto ); + + assertThat( request.getName() ) + .extracting( Nullable::get ) + .isEqualTo( "Tester" ); + + dto.setName( JsonNullable.undefined() ); + + request = Issue2663Mapper.INSTANCE.map( dto ); + + assertThat( request.getName() ) + .extracting( Nullable::isPresent ) + .isEqualTo( false ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/JsonNullable.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/JsonNullable.java new file mode 100644 index 0000000000..4794aff940 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/JsonNullable.java @@ -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._2663; + +import java.util.NoSuchElementException; + +/** + * @author Filip Hrisafov + */ +public class JsonNullable { + + private static final JsonNullable UNDEFINED = new JsonNullable<>( null, false ); + + private final T value; + private final boolean present; + + private JsonNullable(T value, boolean present) { + this.value = value; + this.present = present; + } + + public T get() { + if (!present) { + throw new NoSuchElementException("Value is undefined"); + } + return value; + } + + public boolean isPresent() { + return present; + } + + @SuppressWarnings("unchecked") + public static JsonNullable undefined() { + return (JsonNullable) UNDEFINED; + } + + public static JsonNullable of(T value) { + return new JsonNullable<>( value, true ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Nullable.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Nullable.java new file mode 100644 index 0000000000..f6309be1c6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Nullable.java @@ -0,0 +1,45 @@ +/* + * 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._2663; + +import java.util.NoSuchElementException; + +/** + * @author Filip Hrisafov + */ +public class Nullable { + + @SuppressWarnings("rawtypes") + private static final Nullable UNDEFINED = new Nullable<>( null, false ); + + private final T value; + private final boolean present; + + private Nullable(T value, boolean present) { + this.value = value; + this.present = present; + } + + public T get() { + if (!present) { + throw new NoSuchElementException("Value is undefined"); + } + return value; + } + + public boolean isPresent() { + return present; + } + + public static Nullable of(T value) { + return new Nullable<>( value, true ); + } + + @SuppressWarnings("unchecked") + public static Nullable undefined() { + return (Nullable) UNDEFINED; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/NullableHelper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/NullableHelper.java new file mode 100644 index 0000000000..ae68300e62 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/NullableHelper.java @@ -0,0 +1,23 @@ +/* + * 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._2663; + +/** + * @author Filip Hrisafov + */ +public class NullableHelper { + + private NullableHelper() { + // Helper class + } + + public static Nullable jsonNullableToNullable(JsonNullable jsonNullable) { + if ( jsonNullable.isPresent() ) { + return Nullable.of( jsonNullable.get() ); + } + return Nullable.undefined(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Request.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Request.java new file mode 100644 index 0000000000..8b58e2e0b5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/Request.java @@ -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._2663; + +/** + * @author Filip Hrisafov + */ +public class Request { + + private Nullable name = Nullable.undefined(); + private Nullable child = Nullable.undefined(); + + public Nullable getName() { + return name; + } + + public void setName(Nullable name) { + this.name = name; + } + + public Nullable getChild() { + return child; + } + + public void setChild(Nullable child) { + this.child = child; + } + + public static class ChildRequest { + + private Nullable name = Nullable.undefined(); + + public Nullable getName() { + return name; + } + + public void setName(Nullable name) { + this.name = name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/RequestDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/RequestDto.java new file mode 100644 index 0000000000..d1a93a9bd4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2663/RequestDto.java @@ -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._2663; + +/** + * @author Filip Hrisafov + */ +public class RequestDto { + + private JsonNullable name = JsonNullable.undefined(); + private JsonNullable child = JsonNullable.undefined(); + + public JsonNullable getName() { + return name; + } + + public void setName(JsonNullable name) { + this.name = name; + } + + public JsonNullable getChild() { + return child; + } + + public void setChild(JsonNullable child) { + this.child = child; + } + + public static class ChildRequestDto { + + private JsonNullable name = JsonNullable.undefined(); + + public JsonNullable getName() { + return name; + } + + public void setName(JsonNullable name) { + this.name = name; + } + } +} From 8cc2bdd0928a11acf2ef656e93a0ebadbb740b90 Mon Sep 17 00:00:00 2001 From: Ben Zegveld Date: Tue, 1 Aug 2023 15:04:15 +0200 Subject: [PATCH 378/597] #3163: Strip wild card when checking for type assignability --- .../ap/internal/model/common/Type.java | 9 +++-- .../ap/test/bugs/_3163/Issue3163Mapper.java | 22 +++++++++++ .../ap/test/bugs/_3163/Issue3163Test.java | 21 ++++++++++ .../mapstruct/ap/test/bugs/_3163/Source.java | 34 +++++++++++++++++ .../mapstruct/ap/test/bugs/_3163/Target.java | 38 +++++++++++++++++++ 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Issue3163Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Issue3163Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Target.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index c18574fcfb..f742e1ba0f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -629,11 +629,14 @@ private Type replaceGeneric(Type oldGenericType, Type newType) { * @return {@code true} if and only if this type is assignable to the given other type. */ public boolean isAssignableTo(Type other) { + TypeMirror otherMirror = other.typeMirror; + if ( otherMirror.getKind() == TypeKind.WILDCARD ) { + otherMirror = typeUtils.erasure( other.typeMirror ); + } if ( TypeKind.WILDCARD == typeMirror.getKind() ) { - return typeUtils.contains( typeMirror, other.typeMirror ); + return typeUtils.contains( typeMirror, otherMirror ); } - - return typeUtils.isAssignable( typeMirror, other.typeMirror ); + return typeUtils.isAssignable( typeMirror, otherMirror ); } /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Issue3163Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Issue3163Mapper.java new file mode 100644 index 0000000000..bd44a9d386 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Issue3163Mapper.java @@ -0,0 +1,22 @@ +/* + * 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._3163; + +import java.util.Optional; + +import org.mapstruct.Mapper; + +@Mapper +public interface Issue3163Mapper { + + Target map(Source value); + + Target.Nested map(Source.Nested value); + + default Optional wrapAsOptional(T value) { + return Optional.ofNullable( value ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Issue3163Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Issue3163Test.java new file mode 100644 index 0000000000..5c16a707f3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Issue3163Test.java @@ -0,0 +1,21 @@ +/* + * 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._3163; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +@WithClasses({ + Issue3163Mapper.class, + Source.class, + Target.class +}) +class Issue3163Test { + + @ProcessorTest + void shouldCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Source.java new file mode 100644 index 0000000000..8f5a57f1bf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Source.java @@ -0,0 +1,34 @@ +/* + * 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._3163; + +/** + * @author Filip Hrisafov + */ +public class Source { + + private final Nested nested; + + public Source(Nested nested) { + this.nested = nested; + } + + public Nested getNested() { + return nested; + } + + public static class Nested { + private final String value; + + public Nested(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Target.java new file mode 100644 index 0000000000..301a281d2d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3163/Target.java @@ -0,0 +1,38 @@ +/* + * 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._3163; + +import java.util.Optional; + +/** + * @author Filip Hrisafov + */ +public class Target { + + private Nested nested; + + public Optional getNested() { + return Optional.ofNullable( nested ); + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + public final void setNested(Optional nested) { + this.nested = nested.orElse( null ); + } + + public static class Nested { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} From ea997f83cefd7d3900f8a6a218af1c20f7a1ef8a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 19 Aug 2023 10:09:36 +0200 Subject: [PATCH 379/597] #2340 Add FUNDING.yml --- .github/FUNDING.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..be6d63e5d2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: mapstruct +open_collective: mapstruct From f61a3acec301de2a781bd46a56cdcf5c83ca159e Mon Sep 17 00:00:00 2001 From: GVladi <47348760+GVladi@users.noreply.github.com> Date: Wed, 30 Aug 2023 22:07:38 +0200 Subject: [PATCH 380/597] #3089 Improve support for Map attributes for Immutables Co-Authored-By: thunderhook <8238759+thunderhook@users.noreply.github.com> --- ...eatureCompilationExclusionCliEnhancer.java | 1 + .../spi/ImmutablesAccessorNamingStrategy.java | 13 +- .../ap/test/bugs/_1801/Issue1801Test.java | 2 +- .../test/bugs/_1801/domain/ImmutableItem.java | 2 +- .../bugs/_3089/Issue3089BuilderProvider.java | 96 +++++ .../ap/test/bugs/_3089/Issue3089Test.java | 61 ++++ .../ap/test/bugs/_3089/ItemMapper.java | 23 ++ .../test/bugs/_3089/domain/ImmutableItem.java | 296 ++++++++++++++++ .../ap/test/bugs/_3089/domain/Item.java | 19 + .../test/bugs/_3089/dto/ImmutableItemDTO.java | 330 ++++++++++++++++++ .../ap/test/bugs/_3089/dto/ItemDTO.java | 18 + 11 files changed, 858 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/Issue3089BuilderProvider.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/Issue3089Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/ItemMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/domain/ImmutableItem.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/domain/Item.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/dto/ImmutableItemDTO.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/dto/ItemDTO.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java index c0ea96fd5c..15d9e42598 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -26,6 +26,7 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces // SPI not working correctly here.. (not picked up) additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1596/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/bugs/_1801/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/bugs/_3089/*.java" ); switch ( currentJreVersion ) { case JAVA_8: diff --git a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java index 100798e063..1df6ebc1b6 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java @@ -21,7 +21,18 @@ public class ImmutablesAccessorNamingStrategy extends DefaultAccessorNamingStrat @Override protected boolean isFluentSetter(ExecutableElement method) { - return super.isFluentSetter( method ) && !method.getSimpleName().toString().equals( "from" ); + return super.isFluentSetter( method ) && + !method.getSimpleName().toString().equals( "from" ) && + !isPutterWithUpperCase4thCharacter( method ); + } + + private boolean isPutterWithUpperCase4thCharacter(ExecutableElement method) { + return isPutterMethod( method ) && Character.isUpperCase( method.getSimpleName().toString().charAt( 3 ) ); + } + + public boolean isPutterMethod(ExecutableElement method) { + String methodName = method.getSimpleName().toString(); + return methodName.startsWith( "put" ) && methodName.length() > 3; } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/Issue1801Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/Issue1801Test.java index ee6076f823..40b9515305 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/Issue1801Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/Issue1801Test.java @@ -38,7 +38,7 @@ public class Issue1801Test { @ProcessorTest - public void shouldIncludeBuildeType() { + public void shouldIncludeBuilderType() { ItemDTO item = ImmutableItemDTO.builder().id( "test" ).build(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java index 68231f12ef..393dc153d6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java @@ -124,7 +124,7 @@ public final Builder from(Item instance) { /** * Initializes the value for the {@link Item#getId() id} attribute. - * @param id The value for id + * @param id The value for id * @return {@code this} builder for use in a chained invocation */ public final Builder id(String id) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/Issue3089BuilderProvider.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/Issue3089BuilderProvider.java new file mode 100644 index 0000000000..c827d32587 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/Issue3089BuilderProvider.java @@ -0,0 +1,96 @@ +/* + * 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._3089; + +import java.util.List; +import java.util.Objects; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.ElementFilter; + +import org.mapstruct.ap.spi.BuilderInfo; +import org.mapstruct.ap.spi.BuilderProvider; +import org.mapstruct.ap.spi.ImmutablesBuilderProvider; + +/** + * @author Oliver Erhart + */ +public class Issue3089BuilderProvider extends ImmutablesBuilderProvider implements BuilderProvider { + + @Override + protected BuilderInfo findBuilderInfo(TypeElement typeElement) { + Name name = typeElement.getQualifiedName(); + if ( name.toString().endsWith( ".Item" ) ) { + BuilderInfo info = findBuilderInfoFromInnerBuilderClass( typeElement ); + if ( info != null ) { + return info; + } + } + return super.findBuilderInfo( typeElement ); + } + + /** + * Looks for inner builder class in the Immutable interface / abstract class. + * + * The inner builder class should be be declared with the following line + * + *
      +     *     public static Builder() extends ImmutableItem.Builder { }
      +     * 
      + * + * The Immutable instance should be created with the following line + * + *
      +     *     new Item.Builder().withId("123").build();
      +     * 
      + * + * @see org.mapstruct.ap.test.bugs._3089.domain.Item + * + * @param typeElement + * @return + */ + private BuilderInfo findBuilderInfoFromInnerBuilderClass(TypeElement typeElement) { + if (shouldIgnore( typeElement )) { + return null; + } + + List innerTypes = ElementFilter.typesIn( typeElement.getEnclosedElements() ); + ExecutableElement defaultConstructor = innerTypes.stream() + .filter( this::isBuilderCandidate ) + .map( this::getEmptyArgPublicConstructor ) + .filter( Objects::nonNull ) + .findAny() + .orElse( null ); + + if ( defaultConstructor != null ) { + return new BuilderInfo.Builder() + .builderCreationMethod( defaultConstructor ) + .buildMethod( findBuildMethods( (TypeElement) defaultConstructor.getEnclosingElement(), typeElement ) ) + .build(); + } + return null; + } + + private boolean isBuilderCandidate(TypeElement innerType ) { + TypeElement outerType = (TypeElement) innerType.getEnclosingElement(); + String packageName = this.elementUtils.getPackageOf( outerType ).getQualifiedName().toString(); + Name outerSimpleName = outerType.getSimpleName(); + String builderClassName = packageName + ".Immutable" + outerSimpleName + ".Builder"; + return innerType.getSimpleName().contentEquals( "Builder" ) + && getTypeElement( innerType.getSuperclass() ).getQualifiedName().contentEquals( builderClassName ) + && innerType.getModifiers().contains( Modifier.PUBLIC ); + } + + private ExecutableElement getEmptyArgPublicConstructor(TypeElement builderType) { + return ElementFilter.constructorsIn( builderType.getEnclosedElements() ).stream() + .filter( c -> c.getParameters().isEmpty() ) + .filter( c -> c.getModifiers().contains( Modifier.PUBLIC ) ) + .findAny() + .orElse( null ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/Issue3089Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/Issue3089Test.java new file mode 100644 index 0000000000..f7c138c146 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/Issue3089Test.java @@ -0,0 +1,61 @@ +/* + * 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._3089; + +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.ap.spi.AccessorNamingStrategy; +import org.mapstruct.ap.spi.BuilderProvider; +import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; +import org.mapstruct.ap.test.bugs._3089.domain.ImmutableItem; +import org.mapstruct.ap.test.bugs._3089.domain.Item; +import org.mapstruct.ap.test.bugs._3089.dto.ImmutableItemDTO; +import org.mapstruct.ap.test.bugs._3089.dto.ItemDTO; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithServiceImplementation; +import org.mapstruct.ap.testutil.WithServiceImplementations; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Oliver Erhart + */ +@WithClasses({ + ItemMapper.class, + Item.class, + ImmutableItem.class, + ItemDTO.class, + ImmutableItemDTO.class +}) +@IssueKey("3089") +@WithServiceImplementations({ + @WithServiceImplementation(provides = BuilderProvider.class, value = Issue3089BuilderProvider.class), + @WithServiceImplementation(provides = AccessorNamingStrategy.class, value = ImmutablesAccessorNamingStrategy.class) +}) +public class Issue3089Test { + + @ProcessorTest + public void shouldIgnorePutterOfMap() { + + Map attributesMap = new HashMap<>(); + attributesMap.put( "a", "b" ); + attributesMap.put( "c", "d" ); + + ItemDTO item = ImmutableItemDTO.builder() + .id( "test" ) + .attributes( attributesMap ) + .build(); + + Item target = ItemMapper.INSTANCE.map( item ); + + assertThat( target ).isNotNull(); + assertThat( target.getId() ).isEqualTo( "test" ); + assertThat( target.getAttributes() ).isEqualTo( attributesMap ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/ItemMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/ItemMapper.java new file mode 100644 index 0000000000..ed06115ae2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/ItemMapper.java @@ -0,0 +1,23 @@ +/* + * 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._3089; + +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.bugs._3089.domain.Item; +import org.mapstruct.ap.test.bugs._3089.dto.ItemDTO; +import org.mapstruct.factory.Mappers; + +/** + * @author Oliver Erhart + */ +@Mapper(builder = @Builder) +public abstract class ItemMapper { + + public static final ItemMapper INSTANCE = Mappers.getMapper( ItemMapper.class ); + + public abstract Item map(ItemDTO itemDTO); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/domain/ImmutableItem.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/domain/ImmutableItem.java new file mode 100644 index 0000000000..ab5a6afdc0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/domain/ImmutableItem.java @@ -0,0 +1,296 @@ +/* + * 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._3089.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Immutable implementation of {@link Item}. + *

      + * Superclass should expose a static subclass of the Builder to create immutable instance + * {@code public static Builder extends ImmutableItem.Builder}. + * + * @author Oliver Erhart + */ +@SuppressWarnings({"all"}) +public final class ImmutableItem extends Item { + private final String id; + private final Map attributes; + + private ImmutableItem(String id, Map attributes) { + this.id = id; + this.attributes = attributes; + } + + /** + * @return The value of the {@code id} attribute + */ + @Override + public String getId() { + return id; + } + + /** + * @return The value of the {@code attributes} attribute + */ + @Override + public Map getAttributes() { + return attributes; + } + + /** + * Copy the current immutable object by setting a value for the {@link Item#getId() id} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for id + * @return A modified copy of the {@code this} object + */ + public final ImmutableItem withId(String value) { + String newValue = Objects.requireNonNull(value, "id"); + if (this.id.equals(newValue)) return this; + return new ImmutableItem(newValue, this.attributes); + } + + /** + * Copy the current immutable object by replacing the {@link Item#getAttributes() attributes} map with the specified map. + * Nulls are not permitted as keys or values. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param entries The entries to be added to the attributes map + * @return A modified copy of {@code this} object + */ + public final ImmutableItem withAttributes(Map entries) { + if (this.attributes == entries) return this; + Map newValue = createUnmodifiableMap(true, false, entries); + return new ImmutableItem(this.id, newValue); + } + + /** + * This instance is equal to all instances of {@code ImmutableItem} that have equal attribute values. + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if (this == another) return true; + return another instanceof ImmutableItem + && equalTo((ImmutableItem) another); + } + + private boolean equalTo(ImmutableItem another) { + return id.equals(another.id) + && attributes.equals(another.attributes); + } + + /** + * Computes a hash code from attributes: {@code id}, {@code attributes}. + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 5381; + h += (h << 5) + id.hashCode(); + h += (h << 5) + attributes.hashCode(); + return h; + } + + /** + * Prints the immutable value {@code Item} with attribute values. + * @return A string representation of the value + */ + @Override + public String toString() { + return "Item{" + + "id=" + id + + ", attributes=" + attributes + + "}"; + } + + /** + * Creates an immutable copy of a {@link Item} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * @param instance The instance to copy + * @return A copied immutable Item instance + */ + public static ImmutableItem copyOf(Item instance) { + if (instance instanceof ImmutableItem) { + return (ImmutableItem) instance; + } + return ImmutableItem.builder() + .from(instance) + .build(); + } + + /** + * Creates a builder for {@link ImmutableItem ImmutableItem}. + *

      +   * ImmutableItem.builder()
      +   *    .id(String) // required {@link Item#getId() id}
      +   *    .putAttributes|putAllAttributes(String => String) // {@link Item#getAttributes() attributes} mappings
      +   *    .build();
      +   * 
      + * @return A new ImmutableItem builder + */ + public static ImmutableItem.Builder builder() { + return new ImmutableItem.Builder(); + } + + /** + * Builds instances of type {@link ImmutableItem ImmutableItem}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

      {@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static class Builder { + private static final long INIT_BIT_ID = 0x1L; + private long initBits = 0x1L; + + private String id; + private Map attributes = new LinkedHashMap(); + + public Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code Item} instance. + * Regular attribute values will be replaced with those from the given instance. + * Absent optional values will not replace present values. + * Collection elements and entries will be added, not replaced. + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(Item instance) { + Objects.requireNonNull(instance, "instance"); + id(instance.getId()); + putAllAttributes(instance.getAttributes()); + return this; + } + + /** + * Initializes the value for the {@link Item#getId() id} attribute. + * @param id The value for id + * @return {@code this} builder for use in a chained invocation + */ + public final Builder id(String id) { + this.id = Objects.requireNonNull(id, "id"); + initBits &= ~INIT_BIT_ID; + return this; + } + + /** + * Put one entry to the {@link Item#getAttributes() attributes} map. + * @param key The key in the attributes map + * @param value The associated value in the attributes map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putAttributes(String key, String value) { + this.attributes.put( + Objects.requireNonNull(key, "attributes key"), + Objects.requireNonNull(value, "attributes value")); + return this; + } + + /** + * Put one entry to the {@link Item#getAttributes() attributes} map. Nulls are not permitted + * @param entry The key and value entry + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putAttributes(Map.Entry entry) { + String k = entry.getKey(); + String v = entry.getValue(); + this.attributes.put( + Objects.requireNonNull(k, "attributes key"), + Objects.requireNonNull(v, "attributes value")); + return this; + } + + /** + * Sets or replaces all mappings from the specified map as entries for the {@link Item#getAttributes() attributes} map. Nulls are not permitted + * @param entries The entries that will be added to the attributes map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder attributes(Map entries) { + this.attributes.clear(); + return putAllAttributes(entries); + } + + /** + * Put all mappings from the specified map as entries to {@link Item#getAttributes() attributes} map. Nulls are not permitted + * @param entries The entries that will be added to the attributes map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putAllAttributes(Map entries) { + for (Map.Entry e : entries.entrySet()) { + String k = e.getKey(); + String v = e.getValue(); + this.attributes.put( + Objects.requireNonNull(k, "attributes key"), + Objects.requireNonNull(v, "attributes value")); + } + return this; + } + + /** + * Builds a new {@link ImmutableItem ImmutableItem}. + * @return An immutable instance of Item + * @throws java.lang.IllegalStateException if any required attributes are missing + */ + public ImmutableItem build() { + if (initBits != 0) { + throw new IllegalStateException(formatRequiredAttributesMessage()); + } + return new ImmutableItem(id, createUnmodifiableMap(false, false, attributes)); + } + + private String formatRequiredAttributesMessage() { + List attributes = new ArrayList<>(); + if ((initBits & INIT_BIT_ID) != 0) attributes.add("id"); + return "Cannot build Item, some of required attributes are not set " + attributes; + } + } + + private static Map createUnmodifiableMap(boolean checkNulls, boolean skipNulls, Map map) { + switch (map.size()) { + case 0: return Collections.emptyMap(); + case 1: { + Map.Entry e = map.entrySet().iterator().next(); + K k = e.getKey(); + V v = e.getValue(); + if (checkNulls) { + Objects.requireNonNull(k, "key"); + Objects.requireNonNull(v, "value"); + } + if (skipNulls && (k == null || v == null)) { + return Collections.emptyMap(); + } + return Collections.singletonMap(k, v); + } + default: { + Map linkedMap = new LinkedHashMap<>(map.size()); + if (skipNulls || checkNulls) { + for (Map.Entry e : map.entrySet()) { + K k = e.getKey(); + V v = e.getValue(); + if (skipNulls) { + if (k == null || v == null) continue; + } else if (checkNulls) { + Objects.requireNonNull(k, "key"); + Objects.requireNonNull(v, "value"); + } + linkedMap.put(k, v); + } + } else { + linkedMap.putAll(map); + } + return Collections.unmodifiableMap(linkedMap); + } + } + } +} \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/domain/Item.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/domain/Item.java new file mode 100644 index 0000000000..e193432367 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/domain/Item.java @@ -0,0 +1,19 @@ +/* + * 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._3089.domain; + +import java.util.Map; + +/** + * @author Oliver Erhart + */ +public abstract class Item { + public abstract String getId(); + + public abstract Map getAttributes(); + + public static class Builder extends ImmutableItem.Builder { } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/dto/ImmutableItemDTO.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/dto/ImmutableItemDTO.java new file mode 100644 index 0000000000..de64727bf1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/dto/ImmutableItemDTO.java @@ -0,0 +1,330 @@ +/* + * 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._3089.dto; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Immutable implementation of {@link ItemDTO}. + *

      + * Use the builder to create immutable instances: + * {@code ImmutableItemDTO.builder()}. + * + * @author Oliver Erhart + */ +public final class ImmutableItemDTO extends ItemDTO { + private final String id; + private final Map attributes; + + private ImmutableItemDTO(String id, Map attributes) { + this.id = id; + this.attributes = attributes; + } + + /** + * @return The value of the {@code id} attribute + */ + @Override + public String getId() { + return id; + } + + /** + * @return The value of the {@code attributes} attribute + */ + @Override + public Map getAttributes() { + return attributes; + } + + /** + * Copy the current immutable object by setting a value for the {@link ItemDTO#getId() id} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for id + * @return A modified copy of the {@code this} object + */ + public ImmutableItemDTO withId(String value) { + String newValue = Objects.requireNonNull( value, "id" ); + if ( this.id.equals( newValue ) ) { + return this; + } + return new ImmutableItemDTO( newValue, this.attributes ); + } + + /** + * Copy the current immutable object by replacing the {@link ItemDTO#getAttributes() attributes} map with + * the specified map. + * Nulls are not permitted as keys or values. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param entries The entries to be added to the attributes map + * @return A modified copy of {@code this} object + */ + public ImmutableItemDTO withAttributes(Map entries) { + if ( this.attributes == entries ) { + return this; + } + Map newValue = createUnmodifiableMap( true, false, entries ); + return new ImmutableItemDTO( this.id, newValue ); + } + + /** + * This instance is equal to all instances of {@code ImmutableItemDTO} that have equal attribute values. + * + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if ( this == another ) { + return true; + } + return another instanceof ImmutableItemDTO + && equalTo( (ImmutableItemDTO) another ); + } + + private boolean equalTo(ImmutableItemDTO another) { + return id.equals( another.id ) + && attributes.equals( another.attributes ); + } + + /** + * Computes a hash code from attributes: {@code id}, {@code attributes}. + * + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 5381; + h += ( h << 5 ) + id.hashCode(); + h += ( h << 5 ) + attributes.hashCode(); + return h; + } + + /** + * Prints the immutable value {@code Item} with attribute values. + * + * @return A string representation of the value + */ + @Override + public String toString() { + return "Item{" + + "id=" + id + + ", attributes=" + attributes + + "}"; + } + + /** + * Creates an immutable copy of a {@link ItemDTO} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * + * @param instance The instance to copy + * @return A copied immutable Item instance + */ + public static ImmutableItemDTO copyOf(ItemDTO instance) { + if ( instance instanceof ImmutableItemDTO ) { + return (ImmutableItemDTO) instance; + } + return ImmutableItemDTO.builder() + .from( instance ) + .build(); + } + + /** + * Creates a builder for {@link ImmutableItemDTO ImmutableItemDTO}. + *

      +     * ImmutableItemDTO.builder()
      +     *    .id(String) // required {@link ItemDTO#getId() id}
      +     *    .putAttributes|putAllAttributes(String => String) // {@link ItemDTO#getAttributes() attributes} mappings
      +     *    .build();
      +     * 
      + * + * @return A new ImmutableItemDTO builder + */ + public static ImmutableItemDTO.Builder builder() { + return new ImmutableItemDTO.Builder(); + } + + /** + * Builds instances of type {@link ImmutableItemDTO ImmutableItemDTO}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

      {@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static class Builder { + private static final long INIT_BIT_ID = 0x1L; + private long initBits = 0x1L; + + private String id; + private Map attributes = new LinkedHashMap(); + + public Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code Item} instance. + * Regular attribute values will be replaced with those from the given instance. + * Absent optional values will not replace present values. + * Collection elements and entries will be added, not replaced. + * + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final ImmutableItemDTO.Builder from(ItemDTO instance) { + Objects.requireNonNull( instance, "instance" ); + id( instance.getId() ); + putAllAttributes( instance.getAttributes() ); + return this; + } + + /** + * Initializes the value for the {@link ItemDTO#getId() id} attribute. + * + * @param id The value for id + * @return {@code this} builder for use in a chained invocation + */ + public final ImmutableItemDTO.Builder id(String id) { + this.id = Objects.requireNonNull( id, "id" ); + initBits &= ~INIT_BIT_ID; + return this; + } + + /** + * Put one entry to the {@link ItemDTO#getAttributes() attributes} map. + * + * @param key The key in the attributes map + * @param value The associated value in the attributes map + * @return {@code this} builder for use in a chained invocation + */ + public final ImmutableItemDTO.Builder putAttributes(String key, String value) { + this.attributes.put( + Objects.requireNonNull( key, "attributes key" ), + Objects.requireNonNull( value, "attributes value" ) + ); + return this; + } + + /** + * Put one entry to the {@link ItemDTO#getAttributes() attributes} map. Nulls are not permitted + * + * @param entry The key and value entry + * @return {@code this} builder for use in a chained invocation + */ + public final ImmutableItemDTO.Builder putAttributes(Map.Entry entry) { + String k = entry.getKey(); + String v = entry.getValue(); + this.attributes.put( + Objects.requireNonNull( k, "attributes key" ), + Objects.requireNonNull( v, "attributes value" ) + ); + return this; + } + + /** + * Sets or replaces all mappings from the specified map as entries for the {@link ItemDTO#getAttributes() + * attributes} map. Nulls are not permitted + * + * @param entries The entries that will be added to the attributes map + * @return {@code this} builder for use in a chained invocation + */ + public final ImmutableItemDTO.Builder attributes(Map entries) { + this.attributes.clear(); + return putAllAttributes( entries ); + } + + /** + * Put all mappings from the specified map as entries to {@link ItemDTO#getAttributes() attributes} map. + * Nulls are not permitted + * + * @param entries The entries that will be added to the attributes map + * @return {@code this} builder for use in a chained invocation + */ + public final ImmutableItemDTO.Builder putAllAttributes(Map entries) { + for ( Map.Entry e : entries.entrySet() ) { + String k = e.getKey(); + String v = e.getValue(); + this.attributes.put( + Objects.requireNonNull( k, "attributes key" ), + Objects.requireNonNull( v, "attributes value" ) + ); + } + return this; + } + + /** + * Builds a new {@link ImmutableItemDTO ImmutableItemDTO}. + * + * @return An immutable instance of Item + * @throws java.lang.IllegalStateException if any required attributes are missing + */ + public ImmutableItemDTO build() { + if ( initBits != 0 ) { + throw new IllegalStateException( formatRequiredAttributesMessage() ); + } + return new ImmutableItemDTO( id, createUnmodifiableMap( false, false, attributes ) ); + } + + private String formatRequiredAttributesMessage() { + List attributes = new ArrayList<>(); + if ( ( initBits & INIT_BIT_ID ) != 0 ) { + attributes.add( "id" ); + } + return "Cannot build Item, some of required attributes are not set " + attributes; + } + } + + @SuppressWarnings( "checkstyle:AvoidNestedBlocks" ) + private static Map createUnmodifiableMap(boolean checkNulls, boolean skipNulls, + Map map) { + switch ( map.size() ) { + case 0: + return Collections.emptyMap(); + case 1: { + Map.Entry e = map.entrySet().iterator().next(); + K k = e.getKey(); + V v = e.getValue(); + if ( checkNulls ) { + Objects.requireNonNull( k, "key" ); + Objects.requireNonNull( v, "value" ); + } + if ( skipNulls && ( k == null || v == null ) ) { + return Collections.emptyMap(); + } + return Collections.singletonMap( k, v ); + } + default: { + Map linkedMap = new LinkedHashMap<>( map.size() ); + if ( skipNulls || checkNulls ) { + for ( Map.Entry e : map.entrySet() ) { + K k = e.getKey(); + V v = e.getValue(); + if ( skipNulls ) { + if ( k == null || v == null ) { + continue; + } + } + else if ( checkNulls ) { + Objects.requireNonNull( k, "key" ); + Objects.requireNonNull( v, "value" ); + } + linkedMap.put( k, v ); + } + } + else { + linkedMap.putAll( map ); + } + return Collections.unmodifiableMap( linkedMap ); + } + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/dto/ItemDTO.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/dto/ItemDTO.java new file mode 100644 index 0000000000..c38e37f08a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3089/dto/ItemDTO.java @@ -0,0 +1,18 @@ +/* + * 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._3089.dto; + +import java.util.Map; + +/** + * @author Oliver Erhart + */ +public abstract class ItemDTO { + public abstract String getId(); + + public abstract Map getAttributes(); + +} From 032ee4d77adaa23e2012381eb76e9ab94f937cc3 Mon Sep 17 00:00:00 2001 From: Nikolas Charalambidis Date: Sat, 16 Sep 2023 23:13:20 +0700 Subject: [PATCH 381/597] #3374 Lombok compatibility documentation --- .../asciidoc/chapter-14-third-party-api-integration.asciidoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc index 0f12d67e22..ef2a58f82e 100644 --- a/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc +++ b/documentation/src/main/asciidoc/chapter-14-third-party-api-integration.asciidoc @@ -46,8 +46,10 @@ public @interface Default { MapStruct works together with https://projectlombok.org/[Project Lombok] as of MapStruct 1.2.0.Beta1 and Lombok 1.16.14. MapStruct takes advantage of generated getters, setters, and constructors and uses them to generate the mapper implementations. +Be reminded that the generated code by Lombok might not always be compatible with the expectations from the individual mappings. +In such a case, either Mapstruct mapping must be changed or Lombok must be configured accordingly using https://projectlombok.org/features/configuration[`lombok.config`] for mutual synergy. -[NOTE] +[WARNING] ==== Lombok 1.18.16 introduces a breaking change (https://projectlombok.org/changelog[changelog]). The additional annotation processor `lombok-mapstruct-binding` (https://mvnrepository.com/artifact/org.projectlombok/lombok-mapstruct-binding[Maven]) must be added otherwise MapStruct stops working with Lombok. From 97c389d58bd125c022edf4a6cba1a485c33eb754 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 19:42:54 +0000 Subject: [PATCH 382/597] Bump org.codehaus.plexus:plexus-utils from 3.0.20 to 3.0.24 in /parent Bumps [org.codehaus.plexus:plexus-utils](https://github.com/codehaus-plexus/plexus-utils) from 3.0.20 to 3.0.24. - [Release notes](https://github.com/codehaus-plexus/plexus-utils/releases) - [Commits](https://github.com/codehaus-plexus/plexus-utils/compare/plexus-utils-3.0.20...plexus-utils-3.0.24) --- updated-dependencies: - dependency-name: org.codehaus.plexus:plexus-utils dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 32f7e2897a..73239c87e3 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -304,7 +304,7 @@ org.codehaus.plexus plexus-utils - 3.0.20 + 3.0.24 commons-io From c59eca2a77a02ea97ab0b87c8a90c48266e106dc Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 30 Sep 2023 21:52:07 +0200 Subject: [PATCH 383/597] #3361 Inheriting mappings should only be applied if the target has been redefined --- .../model/source/MappingMethodOptions.java | 8 +- .../ap/test/bugs/_3361/Issue3361Mapper.java | 80 +++++++++++++++++++ .../ap/test/bugs/_3361/Issue3361Test.java | 34 ++++++++ 3 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3361/Issue3361Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3361/Issue3361Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 73474a8478..d17172211b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -278,20 +278,16 @@ private void addAllNonRedefined(SourceMethod sourceMethod, AnnotationMirror anno } private void addAllNonRedefined(Set inheritedMappings) { - Set redefinedSources = new HashSet<>(); + // We are only adding the targets here since this mappings have already been reversed Set redefinedTargets = new HashSet<>(); for ( MappingOptions redefinedMappings : mappings ) { - if ( redefinedMappings.getSourceName() != null ) { - redefinedSources.add( redefinedMappings.getSourceName() ); - } if ( redefinedMappings.getTargetName() != null ) { redefinedTargets.add( redefinedMappings.getTargetName() ); } } for ( MappingOptions inheritedMapping : inheritedMappings ) { if ( inheritedMapping.isIgnored() - || ( !isRedefined( redefinedSources, inheritedMapping.getSourceName() ) - && !isRedefined( redefinedTargets, inheritedMapping.getTargetName() ) ) + || !isRedefined( redefinedTargets, inheritedMapping.getTargetName() ) ) { mappings.add( inheritedMapping ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3361/Issue3361Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3361/Issue3361Mapper.java new file mode 100644 index 0000000000..a556b63306 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3361/Issue3361Mapper.java @@ -0,0 +1,80 @@ +/* + * 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._3361; + +import org.mapstruct.InheritConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +@Mapper +public abstract class Issue3361Mapper { + + public static final Issue3361Mapper INSTANCE = Mappers.getMapper( Issue3361Mapper.class ); + + @Mapping(target = "someAttribute", source = "source.attribute") + @Mapping(target = "otherAttribute", source = "otherSource.anotherAttribute") + public abstract Target mapFromSource(Source source, OtherSource otherSource); + + @InheritConfiguration(name = "mapFromSource") + @Mapping(target = "otherAttribute", source = "source", qualifiedByName = "otherMapping") + public abstract Target mapInherited(Source source, OtherSource otherSource); + + @Named("otherMapping") + protected Long otherMapping(Source source) { + return source.getAttribute() != null ? 1L : 0L; + } + + public static class Target { + private String someAttribute; + private Long otherAttribute; + + public String getSomeAttribute() { + return someAttribute; + } + + public Target setSomeAttribute(String someAttribute) { + this.someAttribute = someAttribute; + return this; + } + + public Long getOtherAttribute() { + return otherAttribute; + } + + public Target setOtherAttribute(Long otherAttribute) { + this.otherAttribute = otherAttribute; + return this; + } + } + + public static class Source { + private final String attribute; + + public Source(String attribute) { + this.attribute = attribute; + } + + public String getAttribute() { + return attribute; + } + } + + public static class OtherSource { + + private final Long anotherAttribute; + + public OtherSource(Long anotherAttribute) { + this.anotherAttribute = anotherAttribute; + } + + public Long getAnotherAttribute() { + return anotherAttribute; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3361/Issue3361Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3361/Issue3361Test.java new file mode 100644 index 0000000000..3fa16ec1ae --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3361/Issue3361Test.java @@ -0,0 +1,34 @@ +/* + * 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._3361; + +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 Filip Hrisafov + */ +@IssueKey("3361") +@WithClasses(Issue3361Mapper.class) +class Issue3361Test { + + @ProcessorTest + void multiSourceShouldInherit() { + Issue3361Mapper.Source source = new Issue3361Mapper.Source( "Test" ); + Issue3361Mapper.OtherSource otherSource = new Issue3361Mapper.OtherSource( 10L ); + + Issue3361Mapper.Target target = Issue3361Mapper.INSTANCE.mapFromSource( source, otherSource ); + assertThat( target.getSomeAttribute() ).isEqualTo( "Test" ); + assertThat( target.getOtherAttribute() ).isEqualTo( 10L ); + + target = Issue3361Mapper.INSTANCE.mapInherited( source, otherSource ); + assertThat( target.getSomeAttribute() ).isEqualTo( "Test" ); + assertThat( target.getOtherAttribute() ).isEqualTo( 1L ); + } +} From 5d39314bd216b8818face21ec8be95d9f7cfff18 Mon Sep 17 00:00:00 2001 From: Xiu Hong Kooi Date: Wed, 1 Nov 2023 06:55:11 +0800 Subject: [PATCH 384/597] #3376 support mapping from iterables to collection --- .../internal/conversion/ConversionUtils.java | 1 + .../ap/internal/model/PropertyMapping.java | 3 +- .../creation/MappingResolverImpl.java | 1 - .../test/collection/iterabletolist/Fruit.java | 27 ++++++++++ .../collection/iterabletolist/FruitSalad.java | 29 +++++++++++ .../iterabletolist/FruitsMapper.java | 24 +++++++++ .../collection/iterabletolist/FruitsMenu.java | 35 +++++++++++++ .../IterableToListMappingTest.java | 47 +++++++++++++++++ .../test/collection/iterabletoset/Fruit.java | 27 ++++++++++ .../collection/iterabletoset/FruitSalad.java | 29 +++++++++++ .../iterabletoset/FruitsMapper.java | 24 +++++++++ .../collection/iterabletoset/FruitsMenu.java | 35 +++++++++++++ .../IterableToSetMappingTest.java | 50 +++++++++++++++++++ 13 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/Fruit.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitSalad.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitsMenu.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/IterableToListMappingTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/Fruit.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitSalad.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitsMenu.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/IterableToSetMappingTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java index 496d11676f..aa01a73276 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java @@ -278,4 +278,5 @@ public static String uuid(ConversionContext conversionContext) { public static String url(ConversionContext conversionContext) { return typeReferenceName( conversionContext, URL.class ); } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index b81cf55447..50f23ecbff 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -301,7 +301,8 @@ else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.get private Assignment forge( ) { Assignment assignment; Type sourceType = rightHandSide.getSourceType(); - if ( (sourceType.isCollectionType() || sourceType.isArrayType()) && targetType.isIterableType() ) { + if ( ( sourceType.isCollectionType() || sourceType.isArrayType()) && targetType.isIterableType() + || ( sourceType.isIterableType() && targetType.isCollectionType() ) ) { assignment = forgeIterableMapping( sourceType, targetType, rightHandSide ); } else if ( sourceType.isMapType() && targetType.isMapType() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 32fa1af931..b290ff56bc 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -434,7 +434,6 @@ private boolean allow2Steps() { private ConversionAssignment resolveViaConversion(Type sourceType, Type targetType) { ConversionProvider conversionProvider = conversions.getConversion( sourceType, targetType ); - if ( conversionProvider == null ) { return null; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/Fruit.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/Fruit.java new file mode 100644 index 0000000000..c989aa4e19 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/Fruit.java @@ -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.collection.iterabletolist; + +/** + * + * @author Xiu-Hong Kooi + */ +public class Fruit { + + private String type; + + public Fruit(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitSalad.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitSalad.java new file mode 100644 index 0000000000..6a04121044 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitSalad.java @@ -0,0 +1,29 @@ +/* + * 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.collection.iterabletolist; + +import java.util.List; + +/** + * + * @author Xiu-Hong Kooi + */ +public class FruitSalad { + + private Iterable fruits; + + public FruitSalad(List fruits) { + this.fruits = fruits; + } + + public Iterable getFruits() { + return fruits; + } + + public void setFruits(List fruits) { + this.fruits = fruits; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitsMapper.java new file mode 100644 index 0000000000..bb4fdc66af --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitsMapper.java @@ -0,0 +1,24 @@ +/* + * 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.collection.iterabletolist; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * + * @author Xiu-Hong Kooi + */ +@Mapper +public interface FruitsMapper { + + FruitsMapper INSTANCE = Mappers.getMapper( + FruitsMapper.class ); + + FruitsMenu fruitSaladToMenu(FruitSalad salad); + + FruitSalad fruitsMenuToSalad(FruitsMenu menu); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitsMenu.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitsMenu.java new file mode 100644 index 0000000000..6f272dc454 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/FruitsMenu.java @@ -0,0 +1,35 @@ +/* + * 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.collection.iterabletolist; + +import java.util.Iterator; +import java.util.List; + +/** + * + * @author Xiu-Hong Kooi + */ +public class FruitsMenu implements Iterable { + + private List fruits; + + public FruitsMenu(List fruits) { + this.fruits = fruits; + } + + public List getFruits() { + return fruits; + } + + public void setFruits(List fruits) { + this.fruits = fruits; + } + + @Override + public Iterator iterator() { + return this.fruits.iterator(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/IterableToListMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/IterableToListMappingTest.java new file mode 100644 index 0000000000..915b9e5fd4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletolist/IterableToListMappingTest.java @@ -0,0 +1,47 @@ +/* + * 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.collection.iterabletolist; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * + * @author Xiu-Hong Kooi + */ +@WithClasses({ FruitsMenu.class, FruitSalad.class, Fruit.class, FruitsMapper.class }) +public class IterableToListMappingTest { + + @ProcessorTest + @IssueKey("3376") + public void shouldMapIterableToList() { + List fruits = Arrays.asList( new Fruit( "mango" ), new Fruit( "apple" ), + new Fruit( "banana" ) ); + FruitsMenu menu = new FruitsMenu(fruits); + FruitSalad salad = FruitsMapper.INSTANCE.fruitsMenuToSalad( menu ); + Iterator itr = salad.getFruits().iterator(); + assertThat( itr.next().getType() ).isEqualTo( "mango" ); + assertThat( itr.next().getType() ).isEqualTo( "apple" ); + assertThat( itr.next().getType() ).isEqualTo( "banana" ); + } + + @ProcessorTest + @IssueKey("3376") + public void shouldMapListToIterable() { + List fruits = Arrays.asList( new Fruit( "mango" ), new Fruit( "apple" ), + new Fruit( "banana" ) ); + FruitSalad salad = new FruitSalad(fruits); + FruitsMenu menu = FruitsMapper.INSTANCE.fruitSaladToMenu( salad ); + assertThat( menu.getFruits() ).extracting( Fruit::getType ).containsExactly( "mango", "apple", "banana" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/Fruit.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/Fruit.java new file mode 100644 index 0000000000..a15b344908 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/Fruit.java @@ -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.collection.iterabletoset; + +/** + * + * @author Xiu-Hong Kooi + */ +public class Fruit { + + private String type; + + public Fruit(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitSalad.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitSalad.java new file mode 100644 index 0000000000..b92ac3160c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitSalad.java @@ -0,0 +1,29 @@ +/* + * 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.collection.iterabletoset; + +import java.util.List; + +/** + * + * @author Xiu-Hong Kooi + */ +public class FruitSalad { + + private Iterable fruits; + + public FruitSalad(Iterable fruits) { + this.fruits = fruits; + } + + public Iterable getFruits() { + return fruits; + } + + public void setFruits(List fruits) { + this.fruits = fruits; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitsMapper.java new file mode 100644 index 0000000000..413dd73617 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitsMapper.java @@ -0,0 +1,24 @@ +/* + * 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.collection.iterabletoset; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * + * @author Xiu-Hong Kooi + */ +@Mapper +public interface FruitsMapper { + + FruitsMapper INSTANCE = Mappers.getMapper( + FruitsMapper.class ); + + FruitsMenu fruitSaladToMenu(FruitSalad salad); + + FruitSalad fruitsMenuToSalad(FruitsMenu menu); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitsMenu.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitsMenu.java new file mode 100644 index 0000000000..8ae4b8b632 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/FruitsMenu.java @@ -0,0 +1,35 @@ +/* + * 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.collection.iterabletoset; + +import java.util.Iterator; +import java.util.Set; + +/** + * + * @author Xiu-Hong Kooi + */ +public class FruitsMenu implements Iterable { + + private Set fruits; + + public FruitsMenu(Set fruits) { + this.fruits = fruits; + } + + public Set getFruits() { + return fruits; + } + + public void setFruits(Set fruits) { + this.fruits = fruits; + } + + @Override + public Iterator iterator() { + return this.fruits.iterator(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/IterableToSetMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/IterableToSetMappingTest.java new file mode 100644 index 0000000000..8b2373d4d4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/iterabletoset/IterableToSetMappingTest.java @@ -0,0 +1,50 @@ +/* + * 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.collection.iterabletoset; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * + * @author Xiu-Hong Kooi + */ +@WithClasses({ FruitsMenu.class, FruitSalad.class, Fruit.class, FruitsMapper.class }) +public class IterableToSetMappingTest { + + @ProcessorTest + @IssueKey("3376") + public void shouldMapIterableToSet() { + Set fruits = new HashSet<>( Arrays.asList( new Fruit( "mango" ), new Fruit( "apple" ), + new Fruit( "banana" ) ) ); + FruitsMenu menu = new FruitsMenu(fruits); + FruitSalad salad = FruitsMapper.INSTANCE.fruitsMenuToSalad( menu ); + Iterator itr = salad.getFruits().iterator(); + Set fruitTypes = fruits.stream().map( Fruit::getType ).collect( Collectors.toSet() ); + assertThat( fruitTypes.contains( itr.next().getType() ) ); + assertThat( fruitTypes.contains( itr.next().getType() ) ); + assertThat( fruitTypes.contains( itr.next().getType() ) ); + } + + @ProcessorTest + @IssueKey("3376") + public void shouldMapSetToIterable() { + Set fruits = new HashSet<>( Arrays.asList( new Fruit( "mango" ), new Fruit( "apple" ), + new Fruit( "banana" ) ) ); + FruitSalad salad = new FruitSalad(fruits); + FruitsMenu menu = FruitsMapper.INSTANCE.fruitSaladToMenu( salad ); + assertThat( menu.getFruits() ).extracting( Fruit::getType ).contains( "mango", "apple", "banana" ); + } +} From b77d321ffb06f42eab9219e120fdf1782bccef1b Mon Sep 17 00:00:00 2001 From: Oliver Erhart <8238759+thunderhook@users.noreply.github.com> Date: Wed, 1 Nov 2023 00:01:45 +0100 Subject: [PATCH 385/597] Added recent contributors (including myself) --- copyright.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/copyright.txt b/copyright.txt index 1b23a0e84a..a111489d01 100644 --- a/copyright.txt +++ b/copyright.txt @@ -44,6 +44,7 @@ Nikolas Charalambidis - https://github.com/Nikolas-Charalambidis Michael Pardo - https://github.com/pardom Mustafa Caylak - https://github.com/luxmeter Oliver Ehrenmüller - https://github.com/greuelpirat +Oliver Erhart - https://github.com/thunderhook Paul Strugnell - https://github.com/ps-powa Pascal Grün - https://github.com/pascalgn Pavel Makhov - https://github.com/streetturtle @@ -67,3 +68,4 @@ Timo Eckhardt - https://github.com/timoe Tomek Gubala - https://github.com/vgtworld Valentin Kulesh - https://github.com/unshare Vincent Alexander Beelte - https://github.com/grandmasterpixel +Xiu Hong Kooi - https://github.com/kooixh From 79f01e2de09c34ee08fef5288f8d8fa686250174 Mon Sep 17 00:00:00 2001 From: Oliver Erhart <8238759+thunderhook@users.noreply.github.com> Date: Sat, 4 Nov 2023 21:35:40 +0100 Subject: [PATCH 386/597] Change master to main branch and fix CI status badge (#3423) --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 8b8af9a69f..21d820dc58 100644 --- a/readme.md +++ b/readme.md @@ -2,10 +2,10 @@ [![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.5.Final-blue.svg)](https://search.maven.org/search?q=g:org.mapstruct%20AND%20v:1.*.Final) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](https://search.maven.org/search?q=g:org.mapstruct) -[![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/master/LICENSE.txt) +[![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/main/LICENSE.txt) -[![Build Status](https://github.com/mapstruct/mapstruct/workflows/CI/badge.svg?branch=master)](https://github.com/mapstruct/mapstruct/actions?query=branch%3Amaster+workflow%3ACI) -[![Coverage Status](https://img.shields.io/codecov/c/github/mapstruct/mapstruct.svg)](https://codecov.io/gh/mapstruct/mapstruct) +[![Build Status](https://github.com/mapstruct/mapstruct/workflows/CI/badge.svg?branch=main)](https://github.com/mapstruct/mapstruct/actions?query=branch%3Amain+workflow%3ACI) +[![Coverage Status](https://img.shields.io/codecov/c/github/mapstruct/mapstruct.svg)](https://codecov.io/gh/mapstruct/mapstruct/tree/main) [![Gitter](https://img.shields.io/gitter/room/mapstruct/mapstruct.svg)](https://gitter.im/mapstruct/mapstruct-users) [![Code Quality: Java](https://img.shields.io/lgtm/grade/java/g/mapstruct/mapstruct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/mapstruct/mapstruct/context:java) [![Total Alerts](https://img.shields.io/lgtm/alerts/g/mapstruct/mapstruct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/mapstruct/mapstruct/alerts) From 0ac0c42dbc481a86879e3796d0201c622da61200 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 4 Nov 2023 23:06:16 +0100 Subject: [PATCH 387/597] [maven-release-plugin] prepare release 1.6.0.Beta1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0bdec734cb..2e244492d9 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 160f963adf..ddf9514f19 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index ac993c73d6..5a6a8f500c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index afa8e31002..e04b31cd73 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 0a62a3ef3c..49f1dc3d6e 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index f888e226ff..f9f43c871c 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 73239c87e3..6701e069f7 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta1 pom MapStruct Parent @@ -71,7 +71,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.6.0.Beta1 diff --git a/pom.xml b/pom.xml index 25a5ead6bc..4c99f88d0a 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta1 parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - HEAD + 1.6.0.Beta1 diff --git a/processor/pom.xml b/processor/pom.xml index 12fc615f5f..5ab8d5027f 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta1 ../parent/pom.xml From 04deac2b3ae4b9ffe1c31ce293ee2d6ad3b20418 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 4 Nov 2023 23:06:17 +0100 Subject: [PATCH 388/597] [maven-release-plugin] prepare for next development iteration --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- processor/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 2e244492d9..0bdec734cb 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index ddf9514f19..160f963adf 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 5a6a8f500c..ac993c73d6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index e04b31cd73..afa8e31002 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 49f1dc3d6e..0a62a3ef3c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index f9f43c871c..f888e226ff 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 6701e069f7..73239c87e3 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta1 + 1.6.0-SNAPSHOT pom MapStruct Parent @@ -71,7 +71,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.6.0.Beta1 + HEAD diff --git a/pom.xml b/pom.xml index 4c99f88d0a..25a5ead6bc 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta1 + 1.6.0-SNAPSHOT parent/pom.xml @@ -54,7 +54,7 @@ scm:git:git://github.com/mapstruct/mapstruct.git scm:git:git@github.com:mapstruct/mapstruct.git https://github.com/mapstruct/mapstruct/ - 1.6.0.Beta1 + HEAD diff --git a/processor/pom.xml b/processor/pom.xml index 5ab8d5027f..12fc615f5f 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta1 + 1.6.0-SNAPSHOT ../parent/pom.xml From 2af291ce2f1a9978c9d3f449d8bb8a9bb3b969f2 Mon Sep 17 00:00:00 2001 From: wandi34 Date: Sat, 11 Nov 2023 21:48:52 +0100 Subject: [PATCH 389/597] Fix Typo Mappper in SubclassMapping Doc --- copyright.txt | 1 + core/src/main/java/org/mapstruct/SubclassMapping.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/copyright.txt b/copyright.txt index a111489d01..d6dc8d1b8a 100644 --- a/copyright.txt +++ b/copyright.txt @@ -68,4 +68,5 @@ Timo Eckhardt - https://github.com/timoe Tomek Gubala - https://github.com/vgtworld Valentin Kulesh - https://github.com/unshare Vincent Alexander Beelte - https://github.com/grandmasterpixel +Winter Andreas - https://github.dev/wandi34 Xiu Hong Kooi - https://github.com/kooixh diff --git a/core/src/main/java/org/mapstruct/SubclassMapping.java b/core/src/main/java/org/mapstruct/SubclassMapping.java index ccf8d4d472..f5463b9026 100644 --- a/core/src/main/java/org/mapstruct/SubclassMapping.java +++ b/core/src/main/java/org/mapstruct/SubclassMapping.java @@ -49,7 +49,7 @@ * } * * Example 2: For parents that can be created. (e.g. normal classes or interfaces with - * @Mappper( uses = ObjectFactory.class ) ) + * @Mapper( uses = ObjectFactory.class ) ) *

      
        * // generates
        * @Override
      
      From 2bb2aefed8957861bf297b8f953b40e8753a8c75 Mon Sep 17 00:00:00 2001
      From: Muhammad Usama 
      Date: Fri, 24 Nov 2023 10:34:15 +0500
      Subject: [PATCH 390/597] #3413 Using Mapping#expression and
       Mapping#conditionaQualifiedBy(Name) should lead to compile error
      
      ---
       copyright.txt                                 |  1 +
       .../internal/model/source/MappingOptions.java |  5 ++-
       .../mapstruct/ap/internal/util/Message.java   |  1 +
       .../test/bugs/_3413/Erroneous3413Mapper.java  | 45 +++++++++++++++++++
       .../ap/test/bugs/_3413/Issue3413Test.java     | 35 +++++++++++++++
       5 files changed, 86 insertions(+), 1 deletion(-)
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3413/Erroneous3413Mapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3413/Issue3413Test.java
      
      diff --git a/copyright.txt b/copyright.txt
      index d6dc8d1b8a..71d47a796a 100644
      --- a/copyright.txt
      +++ b/copyright.txt
      @@ -42,6 +42,7 @@ Kevin Grüneberg - https://github.com/kevcodez
       Lukas Lazar - https://github.com/LukeLaz
       Nikolas Charalambidis - https://github.com/Nikolas-Charalambidis
       Michael Pardo - https://github.com/pardom
      +Muhammad Usama - https://github.com/the-mgi
       Mustafa Caylak - https://github.com/luxmeter
       Oliver Ehrenmüller - https://github.com/greuelpirat
       Oliver Erhart - https://github.com/thunderhook
      diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java
      index 2af1c95f71..dd5b2f17b9 100644
      --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java
      +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java
      @@ -207,10 +207,13 @@ private static boolean isConsistent(MappingGem gem, ExecutableElement method,
               if ( gem.source().hasValue() && gem.constant().hasValue() ) {
                   message = Message.PROPERTYMAPPING_SOURCE_AND_CONSTANT_BOTH_DEFINED;
               }
      +        else if ( gem.expression().hasValue() && gem.conditionQualifiedByName().hasValue() ) {
      +            message = Message.PROPERTYMAPPING_EXPRESSION_AND_CONDITION_QUALIFIED_BY_NAME_BOTH_DEFINED;
      +        }
               else if ( gem.source().hasValue() && gem.expression().hasValue() ) {
                   message = Message.PROPERTYMAPPING_SOURCE_AND_EXPRESSION_BOTH_DEFINED;
               }
      -        else if (gem.expression().hasValue() && gem.constant().hasValue() ) {
      +        else if ( gem.expression().hasValue() && gem.constant().hasValue() ) {
                   message = Message.PROPERTYMAPPING_EXPRESSION_AND_CONSTANT_BOTH_DEFINED;
               }
               else if ( gem.expression().hasValue() && gem.defaultValue().hasValue() ) {
      diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
      index a24a43036c..4b43315390 100644
      --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
      +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
      @@ -84,6 +84,7 @@ public enum Message {
           PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PROPERTY_FROM_TARGET("The type of parameter \"%s\" has no property named \"%s\". Please define the source property explicitly."),
           PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET("No property named \"%s\" exists in source parameter(s). Please define the source explicitly."),
           PROPERTYMAPPING_NO_SUITABLE_COLLECTION_OR_MAP_CONSTRUCTOR( "%s does not have an accessible copy or no-args constructor." ),
      +    PROPERTYMAPPING_EXPRESSION_AND_CONDITION_QUALIFIED_BY_NAME_BOTH_DEFINED( "Expression and condition qualified by name are both defined in @Mapping, either define an expression or a condition qualified by name." ),
       
           CONVERSION_LOSSY_WARNING( "%s has a possibly lossy conversion from %s to %s.", Diagnostic.Kind.WARNING ),
           CONVERSION_LOSSY_ERROR( "Can't map %s. It has a possibly lossy conversion from %s to %s." ),
      diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3413/Erroneous3413Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3413/Erroneous3413Mapper.java
      new file mode 100644
      index 0000000000..784e47b8e7
      --- /dev/null
      +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3413/Erroneous3413Mapper.java
      @@ -0,0 +1,45 @@
      +/*
      + * 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._3413;
      +
      +import org.mapstruct.Mapper;
      +import org.mapstruct.Mapping;
      +import org.mapstruct.factory.Mappers;
      +
      +/**
      + * @author Muhammad Usama
      + */
      +@Mapper
      +public interface Erroneous3413Mapper {
      +    Erroneous3413Mapper INSTANCE = Mappers.getMapper( Erroneous3413Mapper.class );
      +
      +    @Mapping(target = "", expression = "", conditionQualifiedByName = "")
      +    ToPOJO map(FromPOJO fromPOJO);
      +
      +    class FromPOJO {
      +        private String value;
      +
      +        public String getValue() {
      +            return value;
      +        }
      +
      +        public void setValue(String value) {
      +            this.value = value;
      +        }
      +    }
      +
      +    class ToPOJO {
      +        private String value;
      +
      +        public String getValue() {
      +            return value;
      +        }
      +
      +        public void setValue(String value) {
      +            this.value = value;
      +        }
      +    }
      +}
      diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3413/Issue3413Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3413/Issue3413Test.java
      new file mode 100644
      index 0000000000..f98ed9cdbd
      --- /dev/null
      +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3413/Issue3413Test.java
      @@ -0,0 +1,35 @@
      +/*
      + * 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._3413;
      +
      +import org.mapstruct.ap.testutil.IssueKey;
      +import org.mapstruct.ap.testutil.ProcessorTest;
      +import org.mapstruct.ap.testutil.WithClasses;
      +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
      +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
      +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
      +
      +/**
      + * @author Muhammad Usama
      + */
      +@IssueKey("3413")
      +public class Issue3413Test {
      +    @ProcessorTest
      +    @WithClasses(Erroneous3413Mapper.class)
      +    @ExpectedCompilationOutcome(
      +        value = CompilationResult.FAILED,
      +        diagnostics = {
      +            @Diagnostic(
      +                kind = javax.tools.Diagnostic.Kind.ERROR,
      +                line = 19,
      +                message = "Expression and condition qualified by name are both defined in @Mapping, " +
      +                    "either define an expression or a condition qualified by name."
      +            )
      +        }
      +    )
      +    void errorExpectedBecauseExpressionAndConditionQualifiedByNameCannotCoExists() {
      +    }
      +}
      
      From 930f5709b65d6f8a573172247ad02293649c08a9 Mon Sep 17 00:00:00 2001
      From: Ravil Galeyev 
      Date: Wed, 29 Nov 2023 22:25:22 +0100
      Subject: [PATCH 391/597] #3400 Remove unnecessary casts to long and double
      
      ---
       .../java/org/mapstruct/ap/internal/util/NativeTypes.java    | 4 ++--
       .../ap/test/source/constants/ConstantsMapperImpl.java       | 6 +++---
       2 files changed, 5 insertions(+), 5 deletions(-)
      
      diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java
      index 769f7b83f0..5498d4c698 100644
      --- a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java
      +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java
      @@ -284,7 +284,7 @@ void parse(String val, int radix) {
       
               @Override
               public Class getLiteral() {
      -            return float.class;
      +            return double.class;
               }
       
           }
      @@ -363,7 +363,7 @@ else if ( new BigInteger( val, radix ).bitLength() > 64 ) {
       
               @Override
               public Class getLiteral() {
      -            return int.class;
      +            return long.class;
               }
           }
       
      diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/source/constants/ConstantsMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/source/constants/ConstantsMapperImpl.java
      index f5f37142c2..ec13eed3fc 100644
      --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/source/constants/ConstantsMapperImpl.java
      +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/source/constants/ConstantsMapperImpl.java
      @@ -33,12 +33,12 @@ public ConstantsTarget mapFromConstants(String dummy) {
               constantsTarget.setIntValue( -03777777 );
               constantsTarget.setIntBoxed( 15 );
               constantsTarget.setLongValue( 0x7fffffffffffffffL );
      -        constantsTarget.setLongBoxed( (long) 0xCAFEBABEL );
      +        constantsTarget.setLongBoxed( 0xCAFEBABEL );
               constantsTarget.setFloatValue( 1.40e-45f );
               constantsTarget.setFloatBoxed( 3.4028235e38f );
               constantsTarget.setDoubleValue( 1e137 );
      -        constantsTarget.setDoubleBoxed( (double) 0x0.001P-1062d );
      -        constantsTarget.setDoubleBoxedZero( (double) 0.0 );
      +        constantsTarget.setDoubleBoxed( 0x0.001P-1062d );
      +        constantsTarget.setDoubleBoxedZero( 0.0 );
       
               return constantsTarget;
           }
      
      From fa857e9ff46b5bb3e5a6c2c8b1c56009944f5ef3 Mon Sep 17 00:00:00 2001
      From: mosesonline 
      Date: Sun, 10 Dec 2023 15:26:47 +0100
      Subject: [PATCH 392/597] bump some lib versions (#3460)
      
      ---
       parent/pom.xml                                | 16 ++++-----
       .../testutil/assertions/JavaFileAssert.java   | 34 ++++---------------
       2 files changed, 15 insertions(+), 35 deletions(-)
      
      diff --git a/parent/pom.xml b/parent/pom.xml
      index 73239c87e3..1b2c27741d 100644
      --- a/parent/pom.xml
      +++ b/parent/pom.xml
      @@ -22,17 +22,17 @@
           
               UTF-8
               1.0.0.Alpha3
      -        3.0.0-M3
      -        3.0.0-M5
      +        3.4.1
      +        3.2.2
               3.1.0
               5.3.18
               1.6.0
               8.36.1
      -        5.8.0-M1
      -        1.4.2
      +        5.10.1
      +        2.2.0
               
               1
      -        3.17.2
      +        3.24.2
               
               
               jdt_apt
      @@ -226,7 +226,7 @@
                   
                       org.projectlombok
                       lombok
      -                1.18.22
      +                1.18.30
                   
                   
                       org.immutables
      @@ -253,7 +253,7 @@
                   
                       joda-time
                       joda-time
      -                2.9
      +                2.12.5
                   
       
                   
      @@ -309,7 +309,7 @@
                   
                       commons-io
                       commons-io
      -                2.7
      +                2.15.0
                   
       
                   
      diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java b/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java
      index e6eabd9f84..4c6da0d13c 100644
      --- a/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java
      +++ b/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java
      @@ -5,7 +5,11 @@
        */
       package org.mapstruct.ap.testutil.assertions;
       
      -import static java.lang.String.format;
      +import org.assertj.core.api.FileAssert;
      +import org.assertj.core.error.ShouldHaveSameContent;
      +import org.assertj.core.internal.Diff;
      +import org.assertj.core.internal.Failures;
      +import org.assertj.core.util.diff.Delta;
       
       import java.io.File;
       import java.io.IOException;
      @@ -15,14 +19,7 @@
       import java.util.ArrayList;
       import java.util.List;
       
      -import org.apache.commons.io.FileUtils;
      -import org.assertj.core.api.AbstractCharSequenceAssert;
      -import org.assertj.core.api.Assertions;
      -import org.assertj.core.api.FileAssert;
      -import org.assertj.core.error.ShouldHaveSameContent;
      -import org.assertj.core.internal.Diff;
      -import org.assertj.core.internal.Failures;
      -import org.assertj.core.util.diff.Delta;
      +import static java.lang.String.format;
       
       /**
        * Allows to perform assertions on .java source files.
      @@ -39,7 +36,7 @@ public class JavaFileAssert extends FileAssert {
           private static final String IMPORT_GENERATED_ANNOTATION_REGEX = "import javax\\.annotation\\.(processing\\.)?" +
               "Generated;";
       
      -    private Diff diff = new Diff();
      +    private final Diff diff = new Diff();
       
           /**
            * @param actual the actual file
      @@ -48,22 +45,6 @@ public JavaFileAssert(File actual) {
               super( actual );
           }
       
      -    /**
      -     * @return assertion on the file content
      -     */
      -    public AbstractCharSequenceAssert content() {
      -        exists();
      -        isFile();
      -
      -        try {
      -            return Assertions.assertThat( FileUtils.readFileToString( actual, StandardCharsets.UTF_8 ) );
      -        }
      -        catch ( IOException e ) {
      -            failWithMessage( "Unable to read" + actual + ". Exception: " + e.getMessage() );
      -        }
      -        return null;
      -    }
      -
           /**
            * Verifies that the specified class is imported in this Java file
            *
      @@ -118,7 +99,6 @@ public void hasSameMapperContent(File expected) {
            * or if it is a change delta for the date/comments part of a {@code @Generated} annotation.
            *
            * @param delta that needs to be checked
      -     *
            * @return {@code true} if this delta should be ignored, {@code false} otherwise
            */
           private boolean ignoreDelta(Delta delta) {
      
      From 6d99f7b8f312617c8a9526e81fbb2dbf3eaa96a5 Mon Sep 17 00:00:00 2001
      From: Filip Hrisafov 
      Date: Mon, 18 Dec 2023 07:35:26 +0100
      Subject: [PATCH 393/597] #3473 Add Java 21 and EA to build matrix
      
      Fix tests not running green on Java 21
      Update Spring to run correctly on Java 21
      ---
       .github/workflows/java-ea.yml                            | 6 +++---
       .github/workflows/main.yml                               | 2 +-
       parent/pom.xml                                           | 4 ++--
       .../ap/test/conversion/jodatime/JodaConversionTest.java  | 9 ++++-----
       4 files changed, 10 insertions(+), 11 deletions(-)
      
      diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml
      index 3cc488ca7e..d9b018bfb8 100644
      --- a/.github/workflows/java-ea.yml
      +++ b/.github/workflows/java-ea.yml
      @@ -17,9 +17,9 @@ jobs:
             - name: 'Checkout'
               uses: actions/checkout@v3
             - name: 'Set up JDK'
      -        uses: actions/setup-java@v3
      +        uses: oracle-actions/setup-java@v1
               with:
      -          distribution: 'zulu'
      -          java-version: ${{ matrix.java }}
      +          website: jdk.java.net
      +          release: EA
             - name: 'Test'
               run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true
      diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
      index 54cf0b1076..b27dfc075a 100644
      --- a/.github/workflows/main.yml
      +++ b/.github/workflows/main.yml
      @@ -12,7 +12,7 @@ jobs:
           strategy:
             fail-fast: false
             matrix:
      -        java: [13, 17, 18]
      +        java: [17, 21]
           name: 'Linux JDK ${{ matrix.java }}'
           runs-on: ubuntu-latest
           steps:
      diff --git a/parent/pom.xml b/parent/pom.xml
      index 1b2c27741d..e3f0d89627 100644
      --- a/parent/pom.xml
      +++ b/parent/pom.xml
      @@ -25,7 +25,7 @@
               3.4.1
               3.2.2
               3.1.0
      -        5.3.18
      +        5.3.31
               1.6.0
               8.36.1
               5.10.1
      @@ -576,7 +576,7 @@
                       
                           org.jacoco
                           jacoco-maven-plugin
      -                    0.8.8
      +                    0.8.11
                       
                       
                           org.jvnet.jaxb2.maven2
      diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java
      index 1ab73616d5..8574c7a6be 100644
      --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java
      +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java
      @@ -14,7 +14,6 @@
       import org.joda.time.LocalDateTime;
       import org.joda.time.LocalTime;
       import org.junit.jupiter.api.condition.EnabledForJreRange;
      -import org.junit.jupiter.api.condition.EnabledOnJre;
       import org.junit.jupiter.api.condition.JRE;
       import org.junitpioneer.jupiter.DefaultLocale;
       import org.mapstruct.ap.testutil.IssueKey;
      @@ -72,7 +71,7 @@ public void testLocalTimeToString() {
           }
       
           @ProcessorTest
      -    @EnabledOnJre(JRE.JAVA_8)
      +    @EnabledForJreRange(min = JRE.JAVA_21)
           // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+
           public void testSourceToTargetMappingForStrings() {
               Source src = new Source();
      @@ -93,14 +92,14 @@ public void testSourceToTargetMappingForStrings() {
               // and now with default mappings
               target = SourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( src );
               assertThat( target ).isNotNull();
      -        assertThat( target.getDateTime() ).isEqualTo( "1. Januar 2014 00:00:00 UTC" );
      -        assertThat( target.getLocalDateTime() ).isEqualTo( "1. Januar 2014 00:00:00" );
      +        assertThat( target.getDateTime() ).isEqualTo( "1. Januar 2014, 00:00:00 UTC" );
      +        assertThat( target.getLocalDateTime() ).isEqualTo( "1. Januar 2014, 00:00:00" );
               assertThat( target.getLocalDate() ).isEqualTo( "1. Januar 2014" );
               assertThat( target.getLocalTime() ).isEqualTo( "00:00:00" );
           }
       
           @ProcessorTest
      -    @EnabledForJreRange(min = JRE.JAVA_11)
      +    @EnabledForJreRange(min = JRE.JAVA_11, max = JRE.JAVA_17)
           // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+
           public void testSourceToTargetMappingForStringsJdk11() {
               Source src = new Source();
      
      From 7e6fee8714396cb79d81aaecae5f5cd69e178d09 Mon Sep 17 00:00:00 2001
      From: Oliver Erhart <8238759+thunderhook@users.noreply.github.com>
      Date: Wed, 27 Dec 2023 13:40:04 +0100
      Subject: [PATCH 394/597] #1064 Provide a switch to turn off CheckStyle on
       generated test sources
      
      ---
       .../mapstruct/ap/testutil/runner/CompilingExtension.java    | 6 +++++-
       1 file changed, 5 insertions(+), 1 deletion(-)
      
      diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java
      index 7c47d2a2e8..95874e4b3d 100644
      --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java
      +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java
      @@ -182,11 +182,15 @@ private void assertResult(CompilationOutcomeDescriptor actualResult, ExtensionCo
               assertDiagnostics( actualResult.getDiagnostics(), expectedResult.getDiagnostics() );
               assertNotes( actualResult.getNotes(), expectedResult.getNotes() );
       
      -        if ( !findAnnotation( testClass, DisableCheckstyle.class ).isPresent() ) {
      +        if ( !findAnnotation( testClass, DisableCheckstyle.class ).isPresent() && !skipCheckstyleBySystemProperty() ) {
                   assertCheckstyleRules();
               }
           }
       
      +    private static boolean skipCheckstyleBySystemProperty() {
      +        return Boolean.parseBoolean( System.getProperty( "checkstyle.skip" ) );
      +    }
      +
           private void assertCheckstyleRules() throws Exception {
               if ( sourceOutputDir != null ) {
                   Properties properties = new Properties();
      
      From 6cb126cd7cd03aaf8254b3f7f12a29cb6d2ee1b0 Mon Sep 17 00:00:00 2001
      From: Filip Hrisafov 
      Date: Sun, 31 Dec 2023 09:34:34 +0100
      Subject: [PATCH 395/597] #3462 Stream getters should not be treated as
       alternative setter
      
      ---
       .../ap/internal/model/common/Type.java        | 16 ++++--
       .../ap/test/bugs/_3462/Issue3462Mapper.java   | 56 +++++++++++++++++++
       .../ap/test/bugs/_3462/Issue3462Test.java     | 34 +++++++++++
       3 files changed, 100 insertions(+), 6 deletions(-)
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3462/Issue3462Mapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3462/Issue3462Test.java
      
      diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
      index f742e1ba0f..54305cffc1 100644
      --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
      +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
      @@ -821,12 +821,6 @@ else if ( candidate.getAccessorType() == AccessorType.GETTER ) {
                           candidate = adderMethod;
                       }
       
      -                if ( cmStrategy == CollectionMappingStrategyGem.TARGET_IMMUTABLE
      -                    && candidate.getAccessorType() == AccessorType.GETTER ) {
      -                    // If the collection mapping strategy is target immutable
      -                    // then the getter method cannot be used as a setter
      -                    continue;
      -                }
                   }
                   else if ( candidate.getAccessorType() == AccessorType.FIELD  && ( Executables.isFinal( candidate ) ||
                       result.containsKey( targetPropertyName ) ) ) {
      @@ -834,6 +828,16 @@ else if ( candidate.getAccessorType() == AccessorType.FIELD  && ( Executables.is
                       continue;
                   }
       
      +            if ( candidate.getAccessorType() == AccessorType.GETTER ) {
      +                // When the candidate is a getter then it can't be used in the following cases:
      +                // 1. The collection mapping strategy is target immutable
      +                // 2. The target type is a stream (streams are immutable)
      +                if ( cmStrategy == CollectionMappingStrategyGem.TARGET_IMMUTABLE ||
      +                    targetType != null && targetType.isStreamType() ) {
      +                    continue;
      +                }
      +            }
      +
                   Accessor previousCandidate = result.get( targetPropertyName );
                   if ( previousCandidate == null || preferredType == null || ( targetType != null
                       && typeUtils.isAssignable( preferredType.getTypeMirror(), targetType.getTypeMirror() ) ) ) {
      diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3462/Issue3462Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3462/Issue3462Mapper.java
      new file mode 100644
      index 0000000000..1bf87672c6
      --- /dev/null
      +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3462/Issue3462Mapper.java
      @@ -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._3462;
      +
      +import java.util.List;
      +import java.util.stream.Stream;
      +
      +import org.mapstruct.Mapper;
      +import org.mapstruct.factory.Mappers;
      +
      +/**
      + * @author Filip Hrisafov
      + */
      +@Mapper
      +public interface Issue3462Mapper {
      +
      +    Issue3462Mapper INSTANCE = Mappers.getMapper( Issue3462Mapper.class );
      +
      +    Target map(Source source);
      +
      +    class Source {
      +        private final List values;
      +
      +        public Source(List values) {
      +            this.values = values;
      +        }
      +
      +        public List getValues() {
      +            return values;
      +        }
      +
      +        public Stream getValuesStream() {
      +            return values != null ? values.stream() : Stream.empty();
      +        }
      +    }
      +
      +    class Target {
      +        private List values;
      +
      +        public List getValues() {
      +            return values;
      +        }
      +
      +        public void setValues(List values) {
      +            this.values = values;
      +        }
      +
      +        public Stream getValuesStream() {
      +            return values != null ? values.stream() : Stream.empty();
      +        }
      +    }
      +
      +}
      diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3462/Issue3462Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3462/Issue3462Test.java
      new file mode 100644
      index 0000000000..16be4f441e
      --- /dev/null
      +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3462/Issue3462Test.java
      @@ -0,0 +1,34 @@
      +/*
      + * 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._3462;
      +
      +import java.util.Arrays;
      +
      +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 Filip Hrisafov
      + */
      +@IssueKey("3462")
      +@WithClasses(Issue3462Mapper.class)
      +class Issue3462Test {
      +
      +    @ProcessorTest
      +    void shouldNotTreatStreamGettersAsAlternativeSetter() {
      +
      +        Issue3462Mapper.Source source = new Issue3462Mapper.Source( Arrays.asList( "first", "second" ) );
      +        Issue3462Mapper.Target target = Issue3462Mapper.INSTANCE.map( source );
      +
      +        assertThat( target ).isNotNull();
      +        assertThat( target.getValues() ).containsExactly( "first", "second" );
      +        assertThat( target.getValuesStream() ).containsExactly( "first", "second" );
      +
      +    }
      +}
      
      From 60f162ca881076ac0540822026f0bb1c0a65b36e Mon Sep 17 00:00:00 2001
      From: Filip Hrisafov 
      Date: Sun, 28 Jan 2024 17:47:39 +0100
      Subject: [PATCH 396/597] #3463 DefaultBuilderProvider should be able to handle
       methods in parent interfaces
      
      ---
       .../ap/spi/DefaultBuilderProvider.java        | 131 ++++++++++++++----
       .../ap/test/bugs/_3463/EntityBuilder.java     |  14 ++
       .../ap/test/bugs/_3463/Issue3463Mapper.java   |  21 +++
       .../ap/test/bugs/_3463/Issue3463Test.java     |  33 +++++
       .../mapstruct/ap/test/bugs/_3463/Person.java  |  50 +++++++
       .../ap/test/bugs/_3463/PersonDto.java         |  21 +++
       6 files changed, 242 insertions(+), 28 deletions(-)
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/EntityBuilder.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Issue3463Mapper.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Issue3463Test.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Person.java
       create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/PersonDto.java
      
      diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java
      index f89e99cd17..6395364a35 100644
      --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java
      +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java
      @@ -14,6 +14,7 @@
       import javax.lang.model.element.Modifier;
       import javax.lang.model.element.TypeElement;
       import javax.lang.model.type.DeclaredType;
      +import javax.lang.model.type.ExecutableType;
       import javax.lang.model.type.TypeKind;
       import javax.lang.model.type.TypeMirror;
       import javax.lang.model.util.ElementFilter;
      @@ -107,19 +108,17 @@ public BuilderInfo findBuilderInfo(TypeMirror type) {
            * @throws TypeHierarchyErroneousException if the {@link TypeMirror} is of kind {@link TypeKind#ERROR}
            */
           protected TypeElement getTypeElement(TypeMirror type) {
      -        if ( type.getKind() == TypeKind.ERROR ) {
      -            throw new TypeHierarchyErroneousException( type );
      -        }
      -        DeclaredType declaredType = type.accept(
      -            new SimpleTypeVisitor6() {
      -                @Override
      -                public DeclaredType visitDeclared(DeclaredType t, Void p) {
      -                    return t;
      -                }
      -            },
      -            null
      -        );
      +        DeclaredType declaredType = getDeclaredType( type );
      +        return getTypeElement( declaredType );
      +    }
       
      +    /**
      +     * Find the {@link TypeElement} for the given {@link DeclaredType}.
      +     *
      +     * @param declaredType for which the {@link TypeElement} needs to be found.
      +     * @return the type element or {@code null} if the declared type element is not {@link TypeElement}
      +     */
      +    private TypeElement getTypeElement(DeclaredType declaredType) {
               if ( declaredType == null ) {
                   return null;
               }
      @@ -135,6 +134,28 @@ public TypeElement visitType(TypeElement e, Void p) {
               );
           }
       
      +    /**
      +     * Find the {@link DeclaredType} for the given {@link TypeMirror}.
      +     *
      +     * @param type for which the {@link DeclaredType} needs to be found.
      +     * @return the declared or {@code null} if the {@link TypeMirror} is not a {@link DeclaredType}
      +     * @throws TypeHierarchyErroneousException if the {@link TypeMirror} is of kind {@link TypeKind#ERROR}
      +     */
      +    private DeclaredType getDeclaredType(TypeMirror type) {
      +        if ( type.getKind() == TypeKind.ERROR ) {
      +            throw new TypeHierarchyErroneousException( type );
      +        }
      +        return type.accept(
      +            new SimpleTypeVisitor6() {
      +                @Override
      +                public DeclaredType visitDeclared(DeclaredType t, Void p) {
      +                    return t;
      +                }
      +            },
      +            null
      +        );
      +    }
      +
           /**
            * Find the {@link BuilderInfo} for the given {@code typeElement}.
            * 

      @@ -218,21 +239,32 @@ protected boolean isPossibleBuilderCreationMethod(ExecutableElement method, Type * Searches for a build method for {@code typeElement} within the {@code builderElement}. *

      * The default implementation iterates over each method in {@code builderElement} and uses - * {@link DefaultBuilderProvider#isBuildMethod(ExecutableElement, TypeElement)} to check if the method is a - * build method for {@code typeElement}. + * {@link DefaultBuilderProvider#isBuildMethod(ExecutableElement, DeclaredType, TypeElement)} + * to check if the method is a build method for {@code typeElement}. *

      * The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the * {@code builderElement} should be ignored, i.e. not checked for build elements. *

      - * If there are multiple methods that satisfy - * {@link DefaultBuilderProvider#isBuildMethod(ExecutableElement, TypeElement)} and one of those methods - * is names {@code build} that that method would be considered as a build method. * @param builderElement the element for the builder * @param typeElement the element for the type that is being built * @return the build method for the {@code typeElement} if it exists, or {@code null} if it does not * {@code build} */ protected Collection findBuildMethods(TypeElement builderElement, TypeElement typeElement) { + if ( shouldIgnore( builderElement ) || typeElement == null ) { + return Collections.emptyList(); + } + DeclaredType builderType = getDeclaredType( builderElement.asType() ); + + if ( builderType == null ) { + return Collections.emptyList(); + } + + return findBuildMethods( builderElement, builderType, typeElement ); + } + + private Collection findBuildMethods(TypeElement builderElement, DeclaredType builderType, + TypeElement typeElement) { if ( shouldIgnore( builderElement ) ) { return Collections.emptyList(); } @@ -240,23 +272,57 @@ protected Collection findBuildMethods(TypeElement builderElem List builderMethods = ElementFilter.methodsIn( builderElement.getEnclosedElements() ); List buildMethods = new ArrayList<>(); for ( ExecutableElement buildMethod : builderMethods ) { - if ( isBuildMethod( buildMethod, typeElement ) ) { + if ( isBuildMethod( buildMethod, builderType, typeElement ) ) { buildMethods.add( buildMethod ); } } - if ( buildMethods.isEmpty() ) { - return findBuildMethods( - getTypeElement( builderElement.getSuperclass() ), + if ( !buildMethods.isEmpty() ) { + return buildMethods; + } + + Collection parentClassBuildMethods = findBuildMethods( + getTypeElement( builderElement.getSuperclass() ), + builderType, + typeElement + ); + + if ( !parentClassBuildMethods.isEmpty() ) { + return parentClassBuildMethods; + } + + List interfaces = builderElement.getInterfaces(); + if ( interfaces.isEmpty() ) { + return Collections.emptyList(); + } + + Collection interfaceBuildMethods = new ArrayList<>(); + + for ( TypeMirror builderInterface : interfaces ) { + interfaceBuildMethods.addAll( findBuildMethods( + getTypeElement( builderInterface ), + builderType, typeElement - ); + ) ); } - return buildMethods; + return interfaceBuildMethods; + } + + /** + * @see #isBuildMethod(ExecutableElement, DeclaredType, TypeElement) + * @deprecated use {@link #isBuildMethod(ExecutableElement, DeclaredType, TypeElement)} instead + */ + @Deprecated + protected boolean isBuildMethod(ExecutableElement buildMethod, TypeElement typeElement) { + return buildMethod.getParameters().isEmpty() && + buildMethod.getModifiers().contains( Modifier.PUBLIC ) + && typeUtils.isAssignable( buildMethod.getReturnType(), typeElement.asType() ); } /** - * Checks if the {@code buildMethod} is a method that creates {@code typeElement}. + * Checks if the {@code buildMethod} is a method that creates the {@code typeElement} + * as a member of the {@code builderType}. *

      * The default implementation considers a method to be a build method if the following is satisfied: *

        @@ -266,14 +332,23 @@ protected Collection findBuildMethods(TypeElement builderElem *
      * * @param buildMethod the method that should be checked + * @param builderType the type of the builder in which the {@code buildMethod} is located in * @param typeElement the type element that needs to be built * @return {@code true} if the {@code buildMethod} is a build method for {@code typeElement}, {@code false} * otherwise */ - protected boolean isBuildMethod(ExecutableElement buildMethod, TypeElement typeElement) { - return buildMethod.getParameters().isEmpty() && - buildMethod.getModifiers().contains( Modifier.PUBLIC ) - && typeUtils.isAssignable( buildMethod.getReturnType(), typeElement.asType() ); + protected boolean isBuildMethod(ExecutableElement buildMethod, DeclaredType builderType, TypeElement typeElement) { + if ( !buildMethod.getParameters().isEmpty() ) { + return false; + } + if ( !buildMethod.getModifiers().contains( Modifier.PUBLIC ) ) { + return false; + } + TypeMirror buildMethodType = typeUtils.asMemberOf( builderType, buildMethod ); + if ( buildMethodType instanceof ExecutableType ) { + return typeUtils.isAssignable( ( (ExecutableType) buildMethodType ).getReturnType(), typeElement.asType() ); + } + return false; } /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/EntityBuilder.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/EntityBuilder.java new file mode 100644 index 0000000000..6b0b71993e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/EntityBuilder.java @@ -0,0 +1,14 @@ +/* + * 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._3463; + +/** + * @author Filip Hrisafov + */ +public interface EntityBuilder { + + T build(); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Issue3463Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Issue3463Mapper.java new file mode 100644 index 0000000000..37ead3dfdf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Issue3463Mapper.java @@ -0,0 +1,21 @@ +/* + * 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._3463; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3463Mapper { + + Issue3463Mapper INSTANCE = Mappers.getMapper( Issue3463Mapper.class ); + + Person map(PersonDto dto); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Issue3463Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Issue3463Test.java new file mode 100644 index 0000000000..e6cfc3ece7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Issue3463Test.java @@ -0,0 +1,33 @@ +/* + * 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._3463; + +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 Filip Hrisafov + */ +@IssueKey("3463") +@WithClasses({ + EntityBuilder.class, + Issue3463Mapper.class, + Person.class, + PersonDto.class +}) +class Issue3463Test { + + @ProcessorTest + void shouldUseInterfaceBuildMethod() { + Person person = Issue3463Mapper.INSTANCE.map( new PersonDto( "Tester" ) ); + + assertThat( person ).isNotNull(); + assertThat( person.getName() ).isEqualTo( "Tester" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Person.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Person.java new file mode 100644 index 0000000000..977d619474 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/Person.java @@ -0,0 +1,50 @@ +/* + * 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._3463; + +/** + * @author Filip Hrisafov + */ +public class Person { + + private final String name; + + private Person(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static Builder builder() { + return new BuilderImpl(); + } + + public interface Builder extends EntityBuilder { + + Builder name(String name); + } + + private static final class BuilderImpl implements Builder { + + private String name; + + private BuilderImpl() { + } + + @Override + public Builder name(String name) { + this.name = name; + return this; + } + + @Override + public Person build() { + return new Person( this.name ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/PersonDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/PersonDto.java new file mode 100644 index 0000000000..447e4813cc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3463/PersonDto.java @@ -0,0 +1,21 @@ +/* + * 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._3463; + +/** + * @author Filip Hrisafov + */ +public class PersonDto { + private final String name; + + public PersonDto(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} From 632213802817629c413bb83c9aebfc9bf25ee245 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Sun, 28 Jan 2024 17:50:33 +0100 Subject: [PATCH 397/597] Add missing generic diamond operator to MappingOptions (#3498) --- .../org/mapstruct/ap/internal/model/source/MappingOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index dd5b2f17b9..f60eba1931 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -121,7 +121,7 @@ public static void addInstance(MappingGem mapping, ExecutableElement method, String defaultValue = mapping.defaultValue().getValue(); Set dependsOn = mapping.dependsOn().hasValue() ? - new LinkedHashSet( mapping.dependsOn().getValue() ) : + new LinkedHashSet<>( mapping.dependsOn().getValue() ) : Collections.emptySet(); FormattingParameters formattingParam = new FormattingParameters( From 6830258f77096c12b826ab15851d0dc17cd9a9b4 Mon Sep 17 00:00:00 2001 From: Chanyut Yuvacharuskul Date: Sun, 28 Jan 2024 23:51:57 +0700 Subject: [PATCH 398/597] Fix typo in chapter-10-advanced-mapping-options.asciidoc (#3500) --- .../main/asciidoc/chapter-10-advanced-mapping-options.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 87dffce31c..9d6c93f211 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -281,7 +281,7 @@ First calling a mapping method on the source property is not protected by a null The option `nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS` will always include a null check when source is non primitive, unless a source presence checker is defined on the source bean. -The strategy works in a hierarchical fashion. `@Mapping#nullValueCheckStrategy` will override `@BeanMapping#nullValueCheckStrategy`, `@BeanMapping#nullValueCheckStrategy` will override `@Mapper#nullValueCheckStrategy` and `@Mapper#nullValueCheckStrategy` will override `@MaperConfig#nullValueCheckStrategy`. +The strategy works in a hierarchical fashion. `@Mapping#nullValueCheckStrategy` will override `@BeanMapping#nullValueCheckStrategy`, `@BeanMapping#nullValueCheckStrategy` will override `@Mapper#nullValueCheckStrategy` and `@Mapper#nullValueCheckStrategy` will override `@MapperConfig#nullValueCheckStrategy`. [[source-presence-check]] === Source presence checking From 90a3ce0b464e96a82f6c7d81aaba1fd36fca7b8e Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Sun, 28 Jan 2024 18:06:42 +0100 Subject: [PATCH 399/597] Use primitive types in NativeTypes (#3501) --- .../java/org/mapstruct/ap/internal/util/NativeTypes.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java index 5498d4c698..729cb180ef 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java @@ -270,11 +270,11 @@ public void validate(String s) { @Override void parse(String val, int radix) { - Double d = Double.parseDouble( radix == 16 ? "0x" + val : val ); + double d = Double.parseDouble( radix == 16 ? "0x" + val : val ); if ( doubleHasBecomeZero( d ) ) { throw new NumberFormatException( "floating point number too small" ); } - if ( d.isInfinite() ) { + if ( Double.isInfinite( d ) ) { throw new NumberFormatException( "infinitive is not allowed" ); } } @@ -297,11 +297,11 @@ public void validate(String s) { NumberRepresentation br = new NumberRepresentation( s, false, false, true ) { @Override void parse(String val, int radix) { - Float f = Float.parseFloat( radix == 16 ? "0x" + val : val ); + float f = Float.parseFloat( radix == 16 ? "0x" + val : val ); if ( doubleHasBecomeZero( f ) ) { throw new NumberFormatException( "floating point number too small" ); } - if ( f.isInfinite() ) { + if ( Float.isInfinite( f ) ) { throw new NumberFormatException( "infinitive is not allowed" ); } } From 0a43bc088f2f0bd934438d5ec8ca58b72e09998a Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Sun, 28 Jan 2024 18:07:57 +0100 Subject: [PATCH 400/597] Add missing generic type to Javadoc Builder (#3499) --- .../src/main/java/org/mapstruct/ap/internal/model/Javadoc.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Javadoc.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Javadoc.java index a1efc8c022..1afb5258a6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Javadoc.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Javadoc.java @@ -31,7 +31,7 @@ public Builder value(String value) { return this; } - public Builder authors(List authors) { + public Builder authors(List authors) { this.authors = authors; return this; } From 8191c850e0c210a5215512fe9ff67380125e7120 Mon Sep 17 00:00:00 2001 From: Oliver Erhart <8238759+thunderhook@users.noreply.github.com> Date: Sun, 11 Feb 2024 10:42:23 +0100 Subject: [PATCH 401/597] #3323 Support access to the source property name --- .../main/java/org/mapstruct/Condition.java | 1 + .../org/mapstruct/SourcePropertyName.java | 26 ++ .../org/mapstruct/TargetPropertyName.java | 6 +- ...apter-10-advanced-mapping-options.asciidoc | 22 +- .../ap/internal/gem/GemGenerator.java | 2 + .../ap/internal/model/BeanMappingMethod.java | 1 + .../ap/internal/model/PropertyMapping.java | 17 +- .../ap/internal/model/common/Parameter.java | 21 +- .../model/common/ParameterBinding.java | 33 +- .../internal/model/source/SourceMethod.java | 9 +- .../source/selector/SelectionContext.java | 18 + .../model/source/selector/TypeSelector.java | 1 + .../processor/MethodRetrievalProcessor.java | 11 + .../mapstruct/ap/internal/util/Message.java | 1 + .../ap/internal/model/MethodReference.ftl | 7 +- .../model/MethodReferencePresenceCheck.ftl | 3 +- .../ap/internal/model/PropertyMapping.ftl | 1 + .../ap/internal/model/TypeConversion.ftl | 1 + .../ap/internal/model/macro/CommonMacros.ftl | 2 + .../Address.java | 2 +- .../AddressDto.java | 2 +- .../DomainModel.java | 2 +- .../Employee.java | 2 +- .../EmployeeDto.java | 22 +- ...ollectionMapperWithSourcePropertyName.java | 48 +++ ...onalMethodInMapperWithAllExceptTarget.java | 54 +++ ...nditionalMethodInMapperWithAllOptions.java | 64 ++++ ...lMethodInMapperWithSourcePropertyName.java | 39 +++ ...hodInUsesMapperWithSourcePropertyName.java | 43 +++ ...WithSourcePropertyNameInContextMapper.java | 108 ++++++ ...sNonStringSourcePropertyNameParameter.java | 26 ++ .../SourcePropertyNameTest.java | 312 ++++++++++++++++++ ...ollectionMapperWithTargetPropertyName.java | 11 +- ...onalMethodInMapperWithAllExceptTarget.java | 14 +- ...nditionalMethodInMapperWithAllOptions.java | 26 +- ...lMethodInMapperWithTargetPropertyName.java | 7 +- ...hodInUsesMapperWithTargetPropertyName.java | 7 +- ...WithTargetPropertyNameInContextMapper.java | 33 +- ...sNonStringTargetPropertyNameParameter.java | 7 +- .../TargetPropertyNameTest.java | 67 ++-- 40 files changed, 979 insertions(+), 100 deletions(-) create mode 100644 core/src/main/java/org/mapstruct/SourcePropertyName.java rename processor/src/test/java/org/mapstruct/ap/test/conditional/{targetpropertyname => propertyname}/Address.java (86%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{targetpropertyname => propertyname}/AddressDto.java (86%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{targetpropertyname => propertyname}/DomainModel.java (80%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{targetpropertyname => propertyname}/Employee.java (96%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{targetpropertyname => propertyname}/EmployeeDto.java (75%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodForCollectionMapperWithSourcePropertyName.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllExceptTarget.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllOptions.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithSourcePropertyName.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInUsesMapperWithSourcePropertyName.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodWithSourcePropertyNameInContextMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ErroneousNonStringSourcePropertyNameParameter.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/SourcePropertyNameTest.java rename processor/src/test/java/org/mapstruct/ap/test/conditional/{ => propertyname}/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java (76%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{ => propertyname}/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java (76%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{ => propertyname}/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java (61%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{ => propertyname}/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java (70%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{ => propertyname}/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java (74%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{ => propertyname}/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java (70%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{ => propertyname}/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java (59%) rename processor/src/test/java/org/mapstruct/ap/test/conditional/{ => propertyname}/targetpropertyname/TargetPropertyNameTest.java (88%) diff --git a/core/src/main/java/org/mapstruct/Condition.java b/core/src/main/java/org/mapstruct/Condition.java index 148ec4879d..37f1553be1 100644 --- a/core/src/main/java/org/mapstruct/Condition.java +++ b/core/src/main/java/org/mapstruct/Condition.java @@ -24,6 +24,7 @@ *
    3. The mapping source parameter
    4. *
    5. {@code @}{@link Context} parameter
    6. *
    7. {@code @}{@link TargetPropertyName} parameter
    8. + *
    9. {@code @}{@link SourcePropertyName} parameter
    10. * * * Note: The usage of this annotation is mandatory diff --git a/core/src/main/java/org/mapstruct/SourcePropertyName.java b/core/src/main/java/org/mapstruct/SourcePropertyName.java new file mode 100644 index 0000000000..a9d036d5d8 --- /dev/null +++ b/core/src/main/java/org/mapstruct/SourcePropertyName.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * This annotation marks a presence check method parameter as a source property name parameter. + *

      + * This parameter enables conditional filtering based on source property name at run-time. + * Parameter must be of type {@link String} and can be present only in {@link Condition} method. + *

      + * + * @author Oliver Erhart + * @since 1.6 + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.CLASS) +public @interface SourcePropertyName { +} diff --git a/core/src/main/java/org/mapstruct/TargetPropertyName.java b/core/src/main/java/org/mapstruct/TargetPropertyName.java index 17af966fa8..c7ab8d957d 100644 --- a/core/src/main/java/org/mapstruct/TargetPropertyName.java +++ b/core/src/main/java/org/mapstruct/TargetPropertyName.java @@ -11,10 +11,10 @@ import java.lang.annotation.Target; /** - * This annotation marks a presence check method parameter as a property name parameter. + * This annotation marks a presence check method parameter as a target property name parameter. *

      - * This parameter enables conditional filtering based on target property name at run-time. - * Parameter must be of type {@link String} and can be present only in {@link Condition} method. + * This parameter enables conditional filtering based on target property name at run-time. + * Parameter must be of type {@link String} and can be present only in {@link Condition} method. *

      * @author Nikola Ivačič * @since 1.6 diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 9d6c93f211..db13c0548b 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -406,9 +406,9 @@ public class CarMapperImpl implements CarMapper { ---- ==== -Additionally `@TargetPropertyName` of type `java.lang.String` can be used in custom condition check method: +Additionally `@TargetPropertyName` or `@SourcePropertyName` of type `java.lang.String` can be used in custom condition check method: -.Mapper using custom condition check method with `@TargetPropertyName` +.Mapper using custom condition check method with `@TargetPropertyName` and `@SourcePropertyName` ==== [source, java, linenums] [subs="verbatim,attributes"] @@ -416,11 +416,19 @@ Additionally `@TargetPropertyName` of type `java.lang.String` can be used in cus @Mapper public interface CarMapper { + @Mapping(target = "owner", source = "ownerName") CarDto carToCarDto(Car car, @MappingTarget CarDto carDto); @Condition - default boolean isNotEmpty(String value, @TargetPropertyName String name) { - if ( name.equals( "owner" ) { + default boolean isNotEmpty( + String value, + @TargetPropertyName String targetPropertyName, + @SourcePropertyName String sourcePropertyName + ) { + + if ( targetPropertyName.equals( "owner" ) + && sourcePropertyName.equals( "ownerName" ) ) { + return value != null && !value.isEmpty() && !value.equals( value.toLowerCase() ); @@ -431,7 +439,7 @@ public interface CarMapper { ---- ==== -The generated mapper with `@TargetPropertyName` will look like: +The generated mapper with `@TargetPropertyName` and `@SourcePropertyName` will look like: .Custom condition check in generated implementation ==== @@ -447,7 +455,7 @@ public class CarMapperImpl implements CarMapper { return carDto; } - if ( isNotEmpty( car.getOwner(), "owner" ) ) { + if ( isNotEmpty( car.getOwner(), "owner", "ownerName" ) ) { carDto.setOwner( car.getOwner() ); } else { carDto.setOwner( null ); @@ -470,7 +478,7 @@ If there is a custom `@Condition` method applicable for the property it will hav ==== Methods annotated with `@Condition` in addition to the value of the source property can also have the source parameter as an input. -`@TargetPropertyName` parameter can only be used in `@Condition` methods. +`@TargetPropertyName` and `@SourcePropertyName` parameters can only be used in `@Condition` methods. ==== <> is also valid for `@Condition` methods. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index 9ac13184cd..2ed9bd9a09 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -31,6 +31,7 @@ import org.mapstruct.Named; import org.mapstruct.ObjectFactory; import org.mapstruct.Qualifier; +import org.mapstruct.SourcePropertyName; import org.mapstruct.SubclassMapping; import org.mapstruct.SubclassMappings; import org.mapstruct.TargetPropertyName; @@ -57,6 +58,7 @@ @GemDefinition(BeanMapping.class) @GemDefinition(EnumMapping.class) @GemDefinition(MapMapping.class) +@GemDefinition(SourcePropertyName.class) @GemDefinition(SubclassMapping.class) @GemDefinition(SubclassMappings.class) @GemDefinition(TargetType.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index ee347ef35e..be7203507b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1522,6 +1522,7 @@ private void applyPropertyNameBasedMapping(List sourceReference MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false ); PropertyMapping propertyMapping = new PropertyMappingBuilder().mappingContext( ctx ) .sourceMethod( method ) + .sourcePropertyName( targetPropertyName ) .target( targetPropertyName, targetPropertyReadAccessor, targetPropertyWriteAccessor ) .sourceReference( sourceRef ) .existingVariableNames( existingVariableNames ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 50f23ecbff..2f30957962 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -70,6 +70,7 @@ public class PropertyMapping extends ModelElement { private final String name; + private final String sourcePropertyName; private final String sourceBeanName; private final String targetWriteAccessorName; private final ReadAccessor targetReadAccessorProvider; @@ -286,6 +287,7 @@ else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.get } return new PropertyMapping( + sourcePropertyName, targetPropertyName, rightHandSide.getSourceParameterName(), targetWriteAccessor.getSimpleName(), @@ -1099,16 +1101,17 @@ private PropertyMapping(String name, String targetWriteAccessorName, ReadAccessor targetReadAccessorProvider, Type targetType, Assignment propertyAssignment, Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) { - this( name, null, targetWriteAccessorName, targetReadAccessorProvider, + this( name, null, null, targetWriteAccessorName, targetReadAccessorProvider, targetType, propertyAssignment, dependsOn, defaultValueAssignment, constructorMapping ); } - private PropertyMapping(String name, String sourceBeanName, String targetWriteAccessorName, - ReadAccessor targetReadAccessorProvider, Type targetType, - Assignment assignment, - Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) { + private PropertyMapping(String sourcePropertyName, String name, String sourceBeanName, + String targetWriteAccessorName, ReadAccessor targetReadAccessorProvider, Type targetType, + Assignment assignment, + Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) { + this.sourcePropertyName = sourcePropertyName; this.name = name; this.sourceBeanName = sourceBeanName; this.targetWriteAccessorName = targetWriteAccessorName; @@ -1128,6 +1131,10 @@ public String getName() { return name; } + public String getSourcePropertyName() { + return sourcePropertyName; + } + public String getSourceBeanName() { return sourceBeanName; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java index 600c8ac337..44ac0eb7f0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java @@ -14,8 +14,9 @@ import org.mapstruct.ap.internal.gem.ContextGem; import org.mapstruct.ap.internal.gem.MappingTargetGem; -import org.mapstruct.ap.internal.gem.TargetTypeGem; +import org.mapstruct.ap.internal.gem.SourcePropertyNameGem; import org.mapstruct.ap.internal.gem.TargetPropertyNameGem; +import org.mapstruct.ap.internal.gem.TargetTypeGem; import org.mapstruct.ap.internal.util.Collections; /** @@ -32,6 +33,7 @@ public class Parameter extends ModelElement { private final boolean mappingTarget; private final boolean targetType; private final boolean mappingContext; + private final boolean sourcePropertyName; private final boolean targetPropertyName; private final boolean varArgs; @@ -44,12 +46,13 @@ private Parameter(Element element, Type type, boolean varArgs) { this.mappingTarget = MappingTargetGem.instanceOn( element ) != null; this.targetType = TargetTypeGem.instanceOn( element ) != null; this.mappingContext = ContextGem.instanceOn( element ) != null; + this.sourcePropertyName = SourcePropertyNameGem.instanceOn( element ) != null; this.targetPropertyName = TargetPropertyNameGem.instanceOn( element ) != null; this.varArgs = varArgs; } private Parameter(String name, Type type, boolean mappingTarget, boolean targetType, boolean mappingContext, - boolean targetPropertyName, + boolean sourcePropertyName, boolean targetPropertyName, boolean varArgs) { this.element = null; this.name = name; @@ -58,12 +61,13 @@ private Parameter(String name, Type type, boolean mappingTarget, boolean targetT this.mappingTarget = mappingTarget; this.targetType = targetType; this.mappingContext = mappingContext; + this.sourcePropertyName = sourcePropertyName; this.targetPropertyName = targetPropertyName; this.varArgs = varArgs; } public Parameter(String name, Type type) { - this( name, type, false, false, false, false, false ); + this( name, type, false, false, false, false, false, false ); } public Element getElement() { @@ -99,6 +103,7 @@ private String format() { return ( mappingTarget ? "@MappingTarget " : "" ) + ( targetType ? "@TargetType " : "" ) + ( mappingContext ? "@Context " : "" ) + + ( sourcePropertyName ? "@SourcePropertyName " : "" ) + ( targetPropertyName ? "@TargetPropertyName " : "" ) + "%s " + name; } @@ -120,6 +125,10 @@ public boolean isTargetPropertyName() { return targetPropertyName; } + public boolean isSourcePropertyName() { + return sourcePropertyName; + } + public boolean isVarArgs() { return varArgs; } @@ -165,6 +174,7 @@ public static Parameter forForgedMappingTarget(Type parameterType) { false, false, false, + false, false ); } @@ -206,6 +216,10 @@ public static Parameter getTargetTypeParameter(List parameters) { return parameters.stream().filter( Parameter::isTargetType ).findAny().orElse( null ); } + public static Parameter getSourcePropertyNameParameter(List parameters) { + return parameters.stream().filter( Parameter::isSourcePropertyName ).findAny().orElse( null ); + } + public static Parameter getTargetPropertyNameParameter(List parameters) { return parameters.stream().filter( Parameter::isTargetPropertyName ).findAny().orElse( null ); } @@ -214,6 +228,7 @@ private static boolean isSourceParameter( Parameter parameter ) { return !parameter.isMappingTarget() && !parameter.isTargetType() && !parameter.isMappingContext() && + !parameter.isSourcePropertyName() && !parameter.isTargetPropertyName(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java index 18274e5492..0791ee626a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java @@ -22,16 +22,19 @@ public class ParameterBinding { private final boolean targetType; private final boolean mappingTarget; private final boolean mappingContext; + private final boolean sourcePropertyName; private final boolean targetPropertyName; private final SourceRHS sourceRHS; private ParameterBinding(Type parameterType, String variableName, boolean mappingTarget, boolean targetType, - boolean mappingContext, boolean targetPropertyName, SourceRHS sourceRHS) { + boolean mappingContext, boolean sourcePropertyName, boolean targetPropertyName, + SourceRHS sourceRHS) { this.type = parameterType; this.variableName = variableName; this.targetType = targetType; this.mappingTarget = mappingTarget; this.mappingContext = mappingContext; + this.sourcePropertyName = sourcePropertyName; this.targetPropertyName = targetPropertyName; this.sourceRHS = sourceRHS; } @@ -64,11 +67,18 @@ public boolean isMappingContext() { return mappingContext; } + /** + * @return {@code true}, if the parameter being bound is a {@code @SourcePropertyName} parameter. + */ + public boolean isSourcePropertyName() { + return sourcePropertyName; + } + /** * @return {@code true}, if the parameter being bound is a {@code @TargetPropertyName} parameter. */ public boolean isTargetPropertyName() { - return targetPropertyName; + return targetPropertyName; } /** @@ -108,6 +118,7 @@ public static ParameterBinding fromParameter(Parameter parameter) { parameter.isMappingTarget(), parameter.isTargetType(), parameter.isMappingContext(), + parameter.isSourcePropertyName(), parameter.isTargetPropertyName(), null ); @@ -129,6 +140,7 @@ public static ParameterBinding fromTypeAndName(Type parameterType, String parame false, false, false, + false, null ); } @@ -138,14 +150,21 @@ public static ParameterBinding fromTypeAndName(Type parameterType, String parame * @return a parameter binding representing a target type parameter */ public static ParameterBinding forTargetTypeBinding(Type classTypeOf) { - return new ParameterBinding( classTypeOf, null, false, true, false, false, null ); + return new ParameterBinding( classTypeOf, null, false, true, false, false, false, null ); } /** * @return a parameter binding representing a target property name parameter */ public static ParameterBinding forTargetPropertyNameBinding(Type classTypeOf) { - return new ParameterBinding( classTypeOf, null, false, false, false, true, null ); + return new ParameterBinding( classTypeOf, null, false, false, false, false, true, null ); + } + + /** + * @return a parameter binding representing a source property name parameter + */ + public static ParameterBinding forSourcePropertyNameBinding(Type classTypeOf) { + return new ParameterBinding( classTypeOf, null, false, false, false, true, false, null ); } /** @@ -153,7 +172,7 @@ public static ParameterBinding forTargetPropertyNameBinding(Type classTypeOf) { * @return a parameter binding representing a mapping target parameter */ public static ParameterBinding forMappingTargetBinding(Type resultType) { - return new ParameterBinding( resultType, null, true, false, false, false, null ); + return new ParameterBinding( resultType, null, true, false, false, false, false, null ); } /** @@ -161,10 +180,10 @@ public static ParameterBinding forMappingTargetBinding(Type resultType) { * @return a parameter binding representing a mapping source type */ public static ParameterBinding forSourceTypeBinding(Type sourceType) { - return new ParameterBinding( sourceType, null, false, false, false, false, null ); + return new ParameterBinding( sourceType, null, false, false, false, false, false, null ); } public static ParameterBinding fromSourceRHS(SourceRHS sourceRHS) { - return new ParameterBinding( sourceRHS.getSourceType(), null, false, false, false, false, sourceRHS ); + return new ParameterBinding( sourceRHS.getSourceType(), null, false, false, false, false, false, sourceRHS ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 37b57d9025..42c318ca49 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -47,6 +47,7 @@ public class SourceMethod implements Method { private final List parameters; private final Parameter mappingTargetParameter; private final Parameter targetTypeParameter; + private final Parameter sourcePropertyNameParameter; private final Parameter targetPropertyNameParameter; private final boolean isObjectFactory; private final boolean isPresenceCheck; @@ -249,6 +250,7 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters ); this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters ); + this.sourcePropertyNameParameter = Parameter.getSourcePropertyNameParameter( parameters ); this.targetPropertyNameParameter = Parameter.getTargetPropertyNameParameter( parameters ); this.hasObjectFactoryAnnotation = ObjectFactoryGem.instanceOn( executable ) != null; this.isObjectFactory = determineIfIsObjectFactory(); @@ -265,9 +267,10 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) private boolean determineIfIsObjectFactory() { boolean hasNoSourceParameters = getSourceParameters().isEmpty(); boolean hasNoMappingTargetParam = getMappingTargetParameter() == null; + boolean hasNoSourcePropertyNameParam = getSourcePropertyNameParameter() == null; boolean hasNoTargetPropertyNameParam = getTargetPropertyNameParameter() == null; return !isLifecycleCallbackMethod() && !returnType.isVoid() - && hasNoMappingTargetParam && hasNoTargetPropertyNameParam + && hasNoMappingTargetParam && hasNoSourcePropertyNameParam && hasNoTargetPropertyNameParam && ( hasObjectFactoryAnnotation || hasNoSourceParameters ); } @@ -382,6 +385,10 @@ public Parameter getTargetTypeParameter() { return targetTypeParameter; } + public Parameter getSourcePropertyNameParameter() { + return sourcePropertyNameParameter; + } + public Parameter getTargetPropertyNameParameter() { return targetPropertyNameParameter; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java index 9e82dae25d..800278a4c4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java @@ -171,6 +171,7 @@ private static List getAvailableParameterBindingsFromMethod(Me if ( sourceRHS != null ) { availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) ); + addSourcePropertyNameBindings( availableParams, sourceRHS.getSourceType(), typeFactory ); } else { availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); @@ -189,6 +190,7 @@ private static List getAvailableParameterBindingsFromSourceTyp List availableParams = new ArrayList<>(); availableParams.add( ParameterBinding.forSourceTypeBinding( sourceType ) ); + addSourcePropertyNameBindings( availableParams, sourceType, typeFactory ); for ( Parameter param : mappingMethod.getParameters() ) { if ( param.isMappingContext() ) { @@ -201,6 +203,22 @@ private static List getAvailableParameterBindingsFromSourceTyp return availableParams; } + private static void addSourcePropertyNameBindings(List availableParams, Type sourceType, + TypeFactory typeFactory) { + + boolean sourcePropertyNameAvailable = false; + for ( ParameterBinding pb : availableParams ) { + if ( pb.isSourcePropertyName() ) { + sourcePropertyNameAvailable = true; + break; + } + } + if ( !sourcePropertyNameAvailable ) { + availableParams.add( ParameterBinding.forSourcePropertyNameBinding( typeFactory.getType( String.class ) ) ); + } + + } + /** * Adds default parameter bindings for the mapping-target and target-type if not already available. * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java index 24275f594b..50f834d43e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java @@ -215,6 +215,7 @@ private static List findCandidateBindingsForParameter(List parameters, Type returnType) { for ( Parameter param : parameters ) { + + if ( param.isSourcePropertyName() && !param.getType().isString() ) { + messager.printMessage( + param.getElement(), + SourcePropertyNameGem.instanceOn( param.getElement() ).mirror(), + Message.RETRIEVAL_SOURCE_PROPERTY_NAME_WRONG_TYPE + ); + return false; + } + if ( param.isTargetPropertyName() && !param.getType().isString() ) { messager.printMessage( param.getElement(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 4b43315390..d8f27791ee 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -177,6 +177,7 @@ public enum Message { RETRIEVAL_MAPPER_USES_CYCLE( "The mapper %s is referenced itself in Mapper#uses.", Diagnostic.Kind.WARNING ), RETRIEVAL_AFTER_METHOD_NOT_IMPLEMENTED( "@AfterMapping can only be applied to an implemented method." ), RETRIEVAL_BEFORE_METHOD_NOT_IMPLEMENTED( "@BeforeMapping can only be applied to an implemented method." ), + RETRIEVAL_SOURCE_PROPERTY_NAME_WRONG_TYPE( "@SourcePropertyName can only by applied to a String parameter." ), RETRIEVAL_TARGET_PROPERTY_NAME_WRONG_TYPE( "@TargetPropertyName can only by applied to a String parameter." ), INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ), diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl index 4b45643dc8..63f983df46 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl @@ -44,6 +44,8 @@ <#if ext.targetBeanName??>${ext.targetBeanName}<#else>${param.variableName}<#if ext.targetReadAccessorName??>.${ext.targetReadAccessorName}<#t> <#elseif param.mappingContext> ${param.variableName}<#t> + <#elseif param.sourcePropertyName> + "${ext.sourcePropertyName}"<#t> <#elseif param.targetPropertyName> "${ext.targetPropertyName}"<#t> <#elseif param.sourceRHS??> @@ -60,7 +62,7 @@ <#-- macro: assignment - purpose: note: takes its targetyType from the singleSourceParameterType + purpose: note: takes its targetType from the singleSourceParameterType --> <#macro _assignment assignmentToUse> <@includeModel object=assignmentToUse @@ -69,6 +71,7 @@ existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName targetWriteAccessorName=ext.targetWriteAccessorName + sourcePropertyName=ext.sourcePropertyName targetPropertyName=ext.targetPropertyName targetType=singleSourceParameterType/> - \ No newline at end of file + diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl index 0452e1699a..68b05d84ed 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl @@ -8,5 +8,6 @@ <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MethodReferencePresenceCheck" --> <#if isNegate()>!<@includeModel object=methodReference presenceCheck=true + sourcePropertyName=ext.sourcePropertyName targetPropertyName=ext.targetPropertyName - targetType=ext.targetType/> \ No newline at end of file + targetType=ext.targetType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl index e6aef4cecd..f45659cb5d 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl @@ -11,6 +11,7 @@ existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=targetReadAccessorName targetWriteAccessorName=targetWriteAccessorName + sourcePropertyName=sourcePropertyName targetPropertyName=name targetType=targetType defaultValueAssignment=defaultValueAssignment /> \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl index 4a9d356ce4..a5e5798d1c 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl @@ -14,6 +14,7 @@ ${openExpression}<@_assignment/>${closeExpression} existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName targetWriteAccessorName=ext.targetWriteAccessorName + sourcePropertyName=ext.sourcePropertyName targetPropertyName=ext.targetPropertyName targetType=ext.targetType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index bce28ebe19..278b441aa9 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -17,6 +17,7 @@ <#if sourcePresenceCheckerReference??> if ( <@includeModel object=sourcePresenceCheckerReference targetPropertyName=ext.targetPropertyName + sourcePropertyName=ext.sourcePropertyName targetType=ext.targetType/> ) { <#nested> } @@ -61,6 +62,7 @@ <#if sourcePresenceCheckerReference??> if ( <@includeModel object=sourcePresenceCheckerReference targetType=ext.targetType + sourcePropertyName=ext.sourcePropertyName targetPropertyName=ext.targetPropertyName /> ) { <#if needs_explicit_local_var> <@includeModel object=nullCheckLocalVarType/> ${nullCheckLocalVarName} = <@lib.handleAssignment/>; diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Address.java similarity index 86% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Address.java index 162ed118bb..339af75944 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Address.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname; /** * @author Nikola Ivačič diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/AddressDto.java similarity index 86% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/AddressDto.java index f4cbc71915..c6a63065d4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/AddressDto.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname; /** * @author Nikola Ivačič diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/DomainModel.java similarity index 80% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/DomainModel.java index 4d2c716a96..8e5fa8695d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/DomainModel.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname; /** * Target Property Name test entities diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Employee.java similarity index 96% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Employee.java index 59ee3426e1..497717a592 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Employee.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname; import java.util.List; diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/EmployeeDto.java similarity index 75% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/EmployeeDto.java index 5d81a9334b..f49f25e4a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/EmployeeDto.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname; import java.util.List; @@ -15,14 +15,14 @@ public class EmployeeDto implements DomainModel { private String firstName; private String lastName; private String title; - private String country; + private String originCountry; private boolean active; private int age; private EmployeeDto boss; private AddressDto primaryAddress; - private List addresses; + private List originAddresses; public String getFirstName() { return firstName; @@ -48,12 +48,12 @@ public void setTitle(String title) { this.title = title; } - public String getCountry() { - return country; + public String getOriginCountry() { + return originCountry; } - public void setCountry(String country) { - this.country = country; + public void setOriginCountry(String originCountry) { + this.originCountry = originCountry; } public boolean isActive() { @@ -88,11 +88,11 @@ public void setPrimaryAddress(AddressDto primaryAddress) { this.primaryAddress = primaryAddress; } - public List getAddresses() { - return addresses; + public List getOriginAddresses() { + return originAddresses; } - public void setAddresses(List addresses) { - this.addresses = addresses; + public void setOriginAddresses(List originAddresses) { + this.originAddresses = originAddresses; } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodForCollectionMapperWithSourcePropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodForCollectionMapperWithSourcePropertyName.java new file mode 100644 index 0000000000..944fe5d146 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodForCollectionMapperWithSourcePropertyName.java @@ -0,0 +1,48 @@ +/* + * 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.propertyname.sourcepropertyname; + +import java.util.Collection; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SourcePropertyName; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Nikola Ivačič + * @author Mohammad Al Zouabi + * @author Oliver Erhart + */ +@Mapper +public interface ConditionalMethodForCollectionMapperWithSourcePropertyName { + + ConditionalMethodForCollectionMapperWithSourcePropertyName INSTANCE + = Mappers.getMapper( ConditionalMethodForCollectionMapperWithSourcePropertyName.class ); + + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") + Employee map(EmployeeDto employee); + + @Condition + default boolean isNotEmpty(Collection collection, @SourcePropertyName String propName) { + if ( "originAddresses".equalsIgnoreCase( propName ) ) { + return false; + } + return collection != null && !collection.isEmpty(); + } + + @Condition + default boolean isNotBlank(String value, @SourcePropertyName String propName) { + if ( propName.equalsIgnoreCase( "originCountry" ) ) { + return false; + } + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllExceptTarget.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllExceptTarget.java new file mode 100644 index 0000000000..ea6a77d94e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllExceptTarget.java @@ -0,0 +1,54 @@ +/* + * 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.propertyname.sourcepropertyname; + +import java.util.LinkedHashSet; +import java.util.Set; + +import org.mapstruct.Condition; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SourcePropertyName; +import org.mapstruct.ap.test.conditional.propertyname.DomainModel; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + * @author Mohammad Al Zouabi + * @author Oliver Erhart + */ +@Mapper +public interface ConditionalMethodInMapperWithAllExceptTarget { + + ConditionalMethodInMapperWithAllExceptTarget INSTANCE + = Mappers.getMapper( ConditionalMethodInMapperWithAllExceptTarget.class ); + + class PresenceUtils { + Set visited = new LinkedHashSet<>(); + Set visitedSources = new LinkedHashSet<>(); + } + + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") + Employee map(EmployeeDto employee, @Context PresenceUtils utils); + + @Condition + default boolean isNotBlank(String value, + DomainModel source, + @SourcePropertyName String propName, + @Context PresenceUtils utils) { + utils.visited.add( propName ); + utils.visitedSources.add( source.getClass().getSimpleName() ); + if ( propName.equalsIgnoreCase( "firstName" ) ) { + return true; + } + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllOptions.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllOptions.java new file mode 100644 index 0000000000..ee3be57648 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllOptions.java @@ -0,0 +1,64 @@ +/* + * 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.propertyname.sourcepropertyname; + +import java.util.LinkedHashSet; +import java.util.Set; + +import org.mapstruct.Condition; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.SourcePropertyName; +import org.mapstruct.TargetPropertyName; +import org.mapstruct.ap.test.conditional.propertyname.DomainModel; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + * @author Mohammad Al Zouabi + * @author Oliver Erhart + */ +@Mapper +public interface ConditionalMethodInMapperWithAllOptions { + + ConditionalMethodInMapperWithAllOptions INSTANCE + = Mappers.getMapper( ConditionalMethodInMapperWithAllOptions.class ); + + class PresenceUtils { + Set visitedSourceNames = new LinkedHashSet<>(); + Set visitedTargetNames = new LinkedHashSet<>(); + Set visitedSources = new LinkedHashSet<>(); + Set visitedTargets = new LinkedHashSet<>(); + } + + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") + void map(EmployeeDto employeeDto, + @MappingTarget Employee employee, + @Context PresenceUtils utils); + + @Condition + default boolean isNotBlank(String value, + DomainModel source, + @MappingTarget DomainModel target, + @SourcePropertyName String sourcePropName, + @TargetPropertyName String targetPropName, + @Context PresenceUtils utils) { + utils.visitedSourceNames.add( sourcePropName ); + utils.visitedTargetNames.add( targetPropName ); + utils.visitedSources.add( source.getClass().getSimpleName() ); + utils.visitedTargets.add( target.getClass().getSimpleName() ); + if ( sourcePropName.equalsIgnoreCase( "lastName" ) ) { + return false; + } + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithSourcePropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithSourcePropertyName.java new file mode 100644 index 0000000000..41ff6c5264 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithSourcePropertyName.java @@ -0,0 +1,39 @@ +/* + * 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.propertyname.sourcepropertyname; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SourcePropertyName; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + * @author Mohammad Al Zouabi + * @author Oliver Erhart + */ +@Mapper +public interface ConditionalMethodInMapperWithSourcePropertyName { + + ConditionalMethodInMapperWithSourcePropertyName INSTANCE + = Mappers.getMapper( ConditionalMethodInMapperWithSourcePropertyName.class ); + + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") + Employee map(EmployeeDto employee); + + @Condition + default boolean isNotBlank(String value, @SourcePropertyName String propName) { + if ( propName.equalsIgnoreCase( "originCountry" ) ) { + return false; + } + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInUsesMapperWithSourcePropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInUsesMapperWithSourcePropertyName.java new file mode 100644 index 0000000000..8ba222512f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInUsesMapperWithSourcePropertyName.java @@ -0,0 +1,43 @@ +/* + * 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.propertyname.sourcepropertyname; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SourcePropertyName; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + * @author Mohammad Al Zouabi + * @author Oliver Erhart + */ +@Mapper(uses = ConditionalMethodInUsesMapperWithSourcePropertyName.PresenceUtils.class) +public interface ConditionalMethodInUsesMapperWithSourcePropertyName { + + ConditionalMethodInUsesMapperWithSourcePropertyName INSTANCE + = Mappers.getMapper( ConditionalMethodInUsesMapperWithSourcePropertyName.class ); + + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") + Employee map(EmployeeDto employee); + + class PresenceUtils { + + @Condition + public boolean isNotBlank(String value, @SourcePropertyName String propName) { + if ( propName.equalsIgnoreCase( "originCountry" ) ) { + return false; + } + return value != null && !value.trim().isEmpty(); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodWithSourcePropertyNameInContextMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodWithSourcePropertyNameInContextMapper.java new file mode 100644 index 0000000000..64b6fcbbab --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodWithSourcePropertyNameInContextMapper.java @@ -0,0 +1,108 @@ +/* + * 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.propertyname.sourcepropertyname; + +import java.util.Deque; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.Condition; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SourcePropertyName; +import org.mapstruct.TargetType; +import org.mapstruct.ap.test.conditional.propertyname.Address; +import org.mapstruct.ap.test.conditional.propertyname.AddressDto; +import org.mapstruct.ap.test.conditional.propertyname.DomainModel; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Nikola Ivačič + * @author Mohammad Al Zouabi + * @author Oliver Erhart + */ +@Mapper +public interface ConditionalMethodWithSourcePropertyNameInContextMapper { + + ConditionalMethodWithSourcePropertyNameInContextMapper INSTANCE + = Mappers.getMapper( ConditionalMethodWithSourcePropertyNameInContextMapper.class ); + + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") + Employee map(EmployeeDto employee, @Context PresenceUtils utils); + + Address map(AddressDto addressDto, @Context PresenceUtils utils); + + class PresenceUtils { + Set visited = new LinkedHashSet<>(); + + @Condition + public boolean isNotBlank(String value, @SourcePropertyName String propName) { + visited.add( propName ); + return value != null && !value.trim().isEmpty(); + } + } + + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") + Employee map(EmployeeDto employee, @Context PresenceUtilsAllProps utils); + + Address map(AddressDto addressDto, @Context PresenceUtilsAllProps utils); + + class PresenceUtilsAllProps { + Set visited = new LinkedHashSet<>(); + + @Condition + public boolean collect(@SourcePropertyName String propName) { + visited.add( propName ); + return true; + } + } + + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") + Employee map(EmployeeDto employee, @Context PresenceUtilsAllPropsWithSource utils); + + Address map(AddressDto addressDto, @Context PresenceUtilsAllPropsWithSource utils); + + @BeforeMapping + default void before(DomainModel source, @Context PresenceUtilsAllPropsWithSource utils) { + String lastProp = utils.visitedSegments.peekLast(); + if ( lastProp != null && source != null ) { + utils.path.offerLast( lastProp ); + } + } + + @AfterMapping + default void after(@TargetType Class targetClass, @Context PresenceUtilsAllPropsWithSource utils) { + // intermediate method for collection mapping must not change the path + if (targetClass != List.class) { + utils.path.pollLast(); + } + } + + class PresenceUtilsAllPropsWithSource { + Deque visitedSegments = new LinkedList<>(); + Deque visited = new LinkedList<>(); + Deque path = new LinkedList<>(); + + @Condition + public boolean collect(@SourcePropertyName String propName) { + visitedSegments.offerLast( propName ); + path.offerLast( propName ); + visited.offerLast( String.join( ".", path ) ); + path.pollLast(); + return true; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ErroneousNonStringSourcePropertyNameParameter.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ErroneousNonStringSourcePropertyNameParameter.java new file mode 100644 index 0000000000..5ed118676d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ErroneousNonStringSourcePropertyNameParameter.java @@ -0,0 +1,26 @@ +/* + * 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.propertyname.sourcepropertyname; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SourcePropertyName; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; + +@Mapper +public interface ErroneousNonStringSourcePropertyNameParameter { + + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") + Employee map(EmployeeDto employee); + + @Condition + default boolean isNotBlank(String value, @SourcePropertyName int propName) { + return value != null && !value.trim().isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/SourcePropertyNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/SourcePropertyNameTest.java new file mode 100644 index 0000000000..2471ed647d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/SourcePropertyNameTest.java @@ -0,0 +1,312 @@ +/* + * 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.propertyname.sourcepropertyname; + +import java.util.Collections; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.conditional.propertyname.Address; +import org.mapstruct.ap.test.conditional.propertyname.AddressDto; +import org.mapstruct.ap.test.conditional.propertyname.DomainModel; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + * @author Nikola Ivačič + * @author Mohammad Al Zouabi + * @author Oliver Erhart + */ +@IssueKey("3323") +@WithClasses({ + Address.class, + AddressDto.class, + Employee.class, + EmployeeDto.class, + DomainModel.class +}) +public class SourcePropertyNameTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses({ + ConditionalMethodInMapperWithSourcePropertyName.class + }) + public void conditionalMethodInMapperWithSourcePropertyName() { + ConditionalMethodInMapperWithSourcePropertyName mapper + = ConditionalMethodInMapperWithSourcePropertyName.INSTANCE; + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto ); + assertThat( employee.getLastName() ).isEqualTo( "Testirovich" ); + assertThat( employee.getFirstName() ).isNull(); + assertThat( employee.getCountry() ).isNull(); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodForCollectionMapperWithSourcePropertyName.class + }) + public void conditionalMethodForCollectionMapperWithSourcePropertyName() { + ConditionalMethodForCollectionMapperWithSourcePropertyName mapper + = ConditionalMethodForCollectionMapperWithSourcePropertyName.INSTANCE; + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto ); + assertThat( employee.getLastName() ).isEqualTo( "Testirovich" ); + assertThat( employee.getFirstName() ).isNull(); + assertThat( employee.getCountry() ).isNull(); + assertThat( employee.getAddresses() ).isNull(); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodInUsesMapperWithSourcePropertyName.class + }) + public void conditionalMethodInUsesMapperWithSourcePropertyName() { + ConditionalMethodInUsesMapperWithSourcePropertyName mapper + = ConditionalMethodInUsesMapperWithSourcePropertyName.INSTANCE; + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto ); + assertThat( employee.getLastName() ).isEqualTo( "Testirovich" ); + assertThat( employee.getFirstName() ).isNull(); + assertThat( employee.getCountry() ).isNull(); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodInMapperWithAllOptions.class + }) + public void conditionalMethodInMapperWithAllOptions() { + ConditionalMethodInMapperWithAllOptions mapper + = ConditionalMethodInMapperWithAllOptions.INSTANCE; + + ConditionalMethodInMapperWithAllOptions.PresenceUtils utils = + new ConditionalMethodInMapperWithAllOptions.PresenceUtils(); + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = new Employee(); + mapper.map( employeeDto, employee, utils ); + assertThat( employee.getLastName() ).isNull(); + assertThat( employee.getFirstName() ).isNull(); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + assertThat( utils.visitedSourceNames ) + .containsExactlyInAnyOrder( "firstName", "lastName", "title", "originCountry" ); + assertThat( utils.visitedTargetNames ) + .containsExactlyInAnyOrder( "firstName", "lastName", "title", "country" ); + assertThat( utils.visitedSources ).containsExactly( "EmployeeDto" ); + assertThat( utils.visitedTargets ).containsExactly( "Employee" ); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodInMapperWithAllExceptTarget.class + }) + public void conditionalMethodInMapperWithAllExceptTarget() { + ConditionalMethodInMapperWithAllExceptTarget mapper + = ConditionalMethodInMapperWithAllExceptTarget.INSTANCE; + + ConditionalMethodInMapperWithAllExceptTarget.PresenceUtils utils = + new ConditionalMethodInMapperWithAllExceptTarget.PresenceUtils(); + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setFirstName( " " ); + employeeDto.setLastName( "Testirovich" ); + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto, utils ); + assertThat( employee.getLastName() ).isEqualTo( "Testirovich" ); + assertThat( employee.getFirstName() ).isEqualTo( " " ); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + assertThat( utils.visited ) + .containsExactlyInAnyOrder( "firstName", "lastName", "title", "originCountry", "street" ); + assertThat( utils.visitedSources ).containsExactlyInAnyOrder( "EmployeeDto", "AddressDto" ); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodWithSourcePropertyNameInContextMapper.class + }) + public void conditionalMethodWithSourcePropertyNameInUsesContextMapper() { + ConditionalMethodWithSourcePropertyNameInContextMapper mapper + = ConditionalMethodWithSourcePropertyNameInContextMapper.INSTANCE; + + ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtils utils = + new ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtils(); + + EmployeeDto employeeDto = new EmployeeDto(); + employeeDto.setLastName( " " ); + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + Employee employee = mapper.map( employeeDto, utils ); + assertThat( employee.getLastName() ).isNull(); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + assertThat( utils.visited ) + .containsExactlyInAnyOrder( "firstName", "lastName", "title", "originCountry", "street" ); + + ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtilsAllProps allPropsUtils = + new ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtilsAllProps(); + + employeeDto = new EmployeeDto(); + employeeDto.setLastName( "Tester" ); + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + employee = mapper.map( employeeDto, allPropsUtils ); + assertThat( employee.getLastName() ).isEqualTo( "Tester" ); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 6" ); + assertThat( allPropsUtils.visited ) + .containsExactlyInAnyOrder( + "firstName", + "lastName", + "title", + "originCountry", + "active", + "age", + "boss", + "primaryAddress", + "originAddresses", + "street" + ); + + ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtilsAllPropsWithSource allPropsUtilsWithSource = + new ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtilsAllPropsWithSource(); + + EmployeeDto bossEmployeeDto = new EmployeeDto(); + bossEmployeeDto.setLastName( "Boss Tester" ); + bossEmployeeDto.setOriginCountry( "US" ); + bossEmployeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( + "Testing St. 10" ) ) ); + + employeeDto = new EmployeeDto(); + employeeDto.setLastName( "Tester" ); + employeeDto.setOriginCountry( "US" ); + employeeDto.setBoss( bossEmployeeDto ); + employeeDto.setOriginAddresses( + Collections.singletonList( new AddressDto( "Testing St. 6" ) ) + ); + + employee = mapper.map( employeeDto, allPropsUtilsWithSource ); + assertThat( employee.getLastName() ).isEqualTo( "Tester" ); + assertThat( employee.getCountry() ).isEqualTo( "US" ); + assertThat( employee.getAddresses() ).isNotEmpty(); + assertThat( employee.getAddresses().get( 0 ).getStreet() ).isEqualTo( "Testing St. 6" ); + assertThat( employee.getBoss() ).isNotNull(); + assertThat( employee.getBoss().getCountry() ).isEqualTo( "US" ); + assertThat( employee.getBoss().getLastName() ).isEqualTo( "Boss Tester" ); + assertThat( employee.getBoss().getAddresses() ) + .extracting( Address::getStreet ) + .containsExactly( "Testing St. 10" ); + assertThat( allPropsUtilsWithSource.visited ) + .containsExactly( + "originCountry", + "originAddresses", + "originAddresses.street", + "firstName", + "lastName", + "title", + "active", + "age", + "boss", + "boss.originCountry", + "boss.originAddresses", + "boss.originAddresses.street", + "boss.firstName", + "boss.lastName", + "boss.title", + "boss.active", + "boss.age", + "boss.boss", + "boss.primaryAddress", + "primaryAddress" + ); + } + + @ProcessorTest + @WithClasses({ + ErroneousNonStringSourcePropertyNameParameter.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousNonStringSourcePropertyNameParameter.class, + line = 23, + message = "@SourcePropertyName can only by applied to a String parameter." + ) + } + ) + public void nonStringSourcePropertyNameParameter() { + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java similarity index 76% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java index 71baa0942b..a51a318eb6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java @@ -3,15 +3,18 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname; + +import java.util.Collection; import org.mapstruct.Condition; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.TargetPropertyName; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; import org.mapstruct.factory.Mappers; -import java.util.Collection; - /** * @author Nikola Ivačič */ @@ -21,6 +24,8 @@ public interface ConditionalMethodForCollectionMapperWithTargetPropertyName { ConditionalMethodForCollectionMapperWithTargetPropertyName INSTANCE = Mappers.getMapper( ConditionalMethodForCollectionMapperWithTargetPropertyName.class ); + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") Employee map(EmployeeDto employee); @Condition diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java similarity index 76% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java index 430303d06d..1d0dd3fa51 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java @@ -3,17 +3,21 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname; + +import java.util.LinkedHashSet; +import java.util.Set; import org.mapstruct.Condition; import org.mapstruct.Context; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.TargetPropertyName; +import org.mapstruct.ap.test.conditional.propertyname.DomainModel; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; import org.mapstruct.factory.Mappers; -import java.util.LinkedHashSet; -import java.util.Set; - /** * @author Filip Hrisafov * @author Nikola Ivačič @@ -29,6 +33,8 @@ class PresenceUtils { Set visitedSources = new LinkedHashSet<>(); } + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") Employee map(EmployeeDto employee, @Context PresenceUtils utils); @Condition diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java similarity index 61% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java index a18ba0db73..d3ccb9ba2e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java @@ -3,18 +3,23 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname; + +import java.util.LinkedHashSet; +import java.util.Set; import org.mapstruct.Condition; import org.mapstruct.Context; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; +import org.mapstruct.SourcePropertyName; import org.mapstruct.TargetPropertyName; +import org.mapstruct.ap.test.conditional.propertyname.DomainModel; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; import org.mapstruct.factory.Mappers; -import java.util.LinkedHashSet; -import java.util.Set; - /** * @author Filip Hrisafov * @author Nikola Ivačič @@ -26,11 +31,14 @@ public interface ConditionalMethodInMapperWithAllOptions { = Mappers.getMapper( ConditionalMethodInMapperWithAllOptions.class ); class PresenceUtils { - Set visited = new LinkedHashSet<>(); + Set visitedSourceNames = new LinkedHashSet<>(); + Set visitedTargetNames = new LinkedHashSet<>(); Set visitedSources = new LinkedHashSet<>(); Set visitedTargets = new LinkedHashSet<>(); } + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") void map(EmployeeDto employeeDto, @MappingTarget Employee employee, @Context PresenceUtils utils); @@ -39,12 +47,14 @@ void map(EmployeeDto employeeDto, default boolean isNotBlank(String value, DomainModel source, @MappingTarget DomainModel target, - @TargetPropertyName String propName, + @SourcePropertyName String sourcePropName, + @TargetPropertyName String targetPropName, @Context PresenceUtils utils) { - utils.visited.add( propName ); + utils.visitedSourceNames.add( sourcePropName ); + utils.visitedTargetNames.add( targetPropName ); utils.visitedSources.add( source.getClass().getSimpleName() ); utils.visitedTargets.add( target.getClass().getSimpleName() ); - if ( propName.equalsIgnoreCase( "lastName" ) ) { + if ( targetPropName.equalsIgnoreCase( "lastName" ) ) { return false; } return value != null && !value.trim().isEmpty(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java similarity index 70% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java index 6d27bc9036..d5dc378f32 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java @@ -3,11 +3,14 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname; import org.mapstruct.Condition; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.TargetPropertyName; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; import org.mapstruct.factory.Mappers; /** @@ -20,6 +23,8 @@ public interface ConditionalMethodInMapperWithTargetPropertyName { ConditionalMethodInMapperWithTargetPropertyName INSTANCE = Mappers.getMapper( ConditionalMethodInMapperWithTargetPropertyName.class ); + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") Employee map(EmployeeDto employee); @Condition diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java similarity index 74% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java index 7c526884e9..381b99d4f4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java @@ -3,11 +3,14 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname; import org.mapstruct.Condition; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.TargetPropertyName; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; import org.mapstruct.factory.Mappers; /** @@ -20,6 +23,8 @@ public interface ConditionalMethodInUsesMapperWithTargetPropertyName { ConditionalMethodInUsesMapperWithTargetPropertyName INSTANCE = Mappers.getMapper( ConditionalMethodInUsesMapperWithTargetPropertyName.class ); + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") Employee map(EmployeeDto employee); class PresenceUtils { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java similarity index 70% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java index a7cde220c9..44bc262435 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java @@ -3,21 +3,29 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname; + +import java.util.Deque; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; import org.mapstruct.AfterMapping; import org.mapstruct.BeforeMapping; import org.mapstruct.Condition; import org.mapstruct.Context; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.TargetPropertyName; +import org.mapstruct.TargetType; +import org.mapstruct.ap.test.conditional.propertyname.Address; +import org.mapstruct.ap.test.conditional.propertyname.AddressDto; +import org.mapstruct.ap.test.conditional.propertyname.DomainModel; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; import org.mapstruct.factory.Mappers; -import java.util.Deque; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.Set; - /** * @author Nikola Ivačič */ @@ -27,6 +35,8 @@ public interface ConditionalMethodWithTargetPropertyNameInContextMapper { ConditionalMethodWithTargetPropertyNameInContextMapper INSTANCE = Mappers.getMapper( ConditionalMethodWithTargetPropertyNameInContextMapper.class ); + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") Employee map(EmployeeDto employee, @Context PresenceUtils utils); Address map(AddressDto addressDto, @Context PresenceUtils utils); @@ -41,6 +51,8 @@ public boolean isNotBlank(String value, @TargetPropertyName String propName) { } } + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") Employee map(EmployeeDto employee, @Context PresenceUtilsAllProps utils); Address map(AddressDto addressDto, @Context PresenceUtilsAllProps utils); @@ -55,6 +67,8 @@ public boolean collect(@TargetPropertyName String propName) { } } + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") Employee map(EmployeeDto employee, @Context PresenceUtilsAllPropsWithSource utils); Address map(AddressDto addressDto, @Context PresenceUtilsAllPropsWithSource utils); @@ -68,8 +82,11 @@ default void before(DomainModel source, @Context PresenceUtilsAllPropsWithSource } @AfterMapping - default void after(@Context PresenceUtilsAllPropsWithSource utils) { - utils.path.pollLast(); + default void after(@TargetType Class targetClass, @Context PresenceUtilsAllPropsWithSource utils) { + // intermediate method for collection mapping must not change the path + if (targetClass != List.class) { + utils.path.pollLast(); + } } class PresenceUtilsAllPropsWithSource { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java similarity index 59% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java index ec545a058b..d56277abf4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java @@ -3,15 +3,20 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname; import org.mapstruct.Condition; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.TargetPropertyName; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; @Mapper public interface ErroneousNonStringTargetPropertyNameParameter { + @Mapping(target = "country", source = "originCountry") + @Mapping(target = "addresses", source = "originAddresses") Employee map(EmployeeDto employee); @Condition diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/TargetPropertyNameTest.java similarity index 88% rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/TargetPropertyNameTest.java index 91e4b77de2..bb90c0b069 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/TargetPropertyNameTest.java @@ -3,9 +3,16 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.conditional.targetpropertyname; +package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname; + +import java.util.Collections; import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.conditional.propertyname.Address; +import org.mapstruct.ap.test.conditional.propertyname.AddressDto; +import org.mapstruct.ap.test.conditional.propertyname.DomainModel; +import org.mapstruct.ap.test.conditional.propertyname.Employee; +import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -14,8 +21,6 @@ import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.runner.GeneratedSource; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThat; /** @@ -46,8 +51,8 @@ public void conditionalMethodInMapperWithTargetPropertyName() { EmployeeDto employeeDto = new EmployeeDto(); employeeDto.setFirstName( " " ); employeeDto.setLastName( "Testirovich" ); - employeeDto.setCountry( "US" ); - employeeDto.setAddresses( + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( "Testing St. 6" ) ) ); @@ -71,8 +76,8 @@ public void conditionalMethodForCollectionMapperWithTargetPropertyName() { EmployeeDto employeeDto = new EmployeeDto(); employeeDto.setFirstName( " " ); employeeDto.setLastName( "Testirovich" ); - employeeDto.setCountry( "US" ); - employeeDto.setAddresses( + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( "Testing St. 6" ) ) ); @@ -94,8 +99,8 @@ public void conditionalMethodInUsesMapperWithTargetPropertyName() { EmployeeDto employeeDto = new EmployeeDto(); employeeDto.setFirstName( " " ); employeeDto.setLastName( "Testirovich" ); - employeeDto.setCountry( "US" ); - employeeDto.setAddresses( + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( "Testing St. 6" ) ) ); @@ -122,8 +127,8 @@ public void conditionalMethodInMapperWithAllOptions() { EmployeeDto employeeDto = new EmployeeDto(); employeeDto.setFirstName( " " ); employeeDto.setLastName( "Testirovich" ); - employeeDto.setCountry( "US" ); - employeeDto.setAddresses( + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( "Testing St. 6" ) ) ); @@ -135,7 +140,9 @@ public void conditionalMethodInMapperWithAllOptions() { assertThat( employee.getAddresses() ) .extracting( Address::getStreet ) .containsExactly( "Testing St. 6" ); - assertThat( utils.visited ) + assertThat( utils.visitedSourceNames ) + .containsExactlyInAnyOrder( "firstName", "lastName", "title", "originCountry" ); + assertThat( utils.visitedTargetNames ) .containsExactlyInAnyOrder( "firstName", "lastName", "title", "country" ); assertThat( utils.visitedSources ).containsExactly( "EmployeeDto" ); assertThat( utils.visitedTargets ).containsExactly( "Employee" ); @@ -155,8 +162,8 @@ public void conditionalMethodInMapperWithAllExceptTarget() { EmployeeDto employeeDto = new EmployeeDto(); employeeDto.setFirstName( " " ); employeeDto.setLastName( "Testirovich" ); - employeeDto.setCountry( "US" ); - employeeDto.setAddresses( + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( "Testing St. 6" ) ) ); @@ -185,8 +192,8 @@ public void conditionalMethodWithTargetPropertyNameInUsesContextMapper() { EmployeeDto employeeDto = new EmployeeDto(); employeeDto.setLastName( " " ); - employeeDto.setCountry( "US" ); - employeeDto.setAddresses( + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( "Testing St. 6" ) ) ); @@ -204,8 +211,8 @@ public void conditionalMethodWithTargetPropertyNameInUsesContextMapper() { employeeDto = new EmployeeDto(); employeeDto.setLastName( "Tester" ); - employeeDto.setCountry( "US" ); - employeeDto.setAddresses( + employeeDto.setOriginCountry( "US" ); + employeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( "Testing St. 6" ) ) ); @@ -234,15 +241,15 @@ public void conditionalMethodWithTargetPropertyNameInUsesContextMapper() { EmployeeDto bossEmployeeDto = new EmployeeDto(); bossEmployeeDto.setLastName( "Boss Tester" ); - bossEmployeeDto.setCountry( "US" ); - bossEmployeeDto.setAddresses( Collections.singletonList( new AddressDto( + bossEmployeeDto.setOriginCountry( "US" ); + bossEmployeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( "Testing St. 10" ) ) ); employeeDto = new EmployeeDto(); employeeDto.setLastName( "Tester" ); - employeeDto.setCountry( "US" ); + employeeDto.setOriginCountry( "US" ); employeeDto.setBoss( bossEmployeeDto ); - employeeDto.setAddresses( + employeeDto.setOriginAddresses( Collections.singletonList( new AddressDto( "Testing St. 6" ) ) ); @@ -259,26 +266,26 @@ public void conditionalMethodWithTargetPropertyNameInUsesContextMapper() { .containsExactly( "Testing St. 10" ); assertThat( allPropsUtilsWithSource.visited ) .containsExactly( + "country", + "addresses", + "addresses.street", "firstName", "lastName", "title", - "country", "active", "age", "boss", + "boss.country", + "boss.addresses", + "boss.addresses.street", "boss.firstName", "boss.lastName", "boss.title", - "boss.country", "boss.active", "boss.age", "boss.boss", "boss.primaryAddress", - "boss.addresses", - "boss.addresses.street", - "primaryAddress", - "addresses", - "addresses.street" + "primaryAddress" ); } @@ -293,7 +300,7 @@ public void conditionalMethodWithTargetPropertyNameInUsesContextMapper() { @Diagnostic( kind = javax.tools.Diagnostic.Kind.ERROR, type = ErroneousNonStringTargetPropertyNameParameter.class, - line = 18, + line = 23, message = "@TargetPropertyName can only by applied to a String parameter." ) } From ca1fd0d85dc76f914167c94f1e034e8dfd55bb61 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 11 Feb 2024 12:51:19 +0100 Subject: [PATCH 402/597] #3331 Do not handle defined mappings if the result type is abstract due to runtime exception subclass exhaustive strategy (#3487) --- .../ap/internal/model/BeanMappingMethod.java | 43 +++++++++++-- .../ap/test/bugs/_3331/Issue3331Mapper.java | 27 ++++++++ .../ap/test/bugs/_3331/Issue3331Test.java | 48 ++++++++++++++ .../mapstruct/ap/test/bugs/_3331/Vehicle.java | 64 +++++++++++++++++++ .../ap/test/bugs/_3331/VehicleDto.java | 51 +++++++++++++++ 5 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Issue3331Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Issue3331Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Vehicle.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/VehicleDto.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index be7203507b..8979901f80 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -284,10 +284,15 @@ else if ( !method.isUpdateMethod() ) { initializeMappingReferencesIfNeeded( resultTypeToMap ); - // map properties with mapping - boolean mappingErrorOccurred = handleDefinedMappings( resultTypeToMap ); - if ( mappingErrorOccurred ) { - return null; + boolean shouldHandledDefinedMappings = shouldHandledDefinedMappings( resultTypeToMap ); + + + if ( shouldHandledDefinedMappings ) { + // map properties with mapping + boolean mappingErrorOccurred = handleDefinedMappings( resultTypeToMap ); + if ( mappingErrorOccurred ) { + return null; + } } boolean applyImplicitMappings = !mappingReferences.isRestrictToDefinedMappings(); @@ -1045,6 +1050,36 @@ private List getValueAsList(AnnotationValue av) { return (List) av.getValue(); } + /** + * Determine whether defined mappings should be handled on the result type. + * They should be, if any of the following is true: + *
        + *
      • The {@code resultTypeToMap} is not abstract
      • + *
      • There is a factory method
      • + *
      • The method is an update method
      • + *
      + * Otherwise, it means that we have reached this because subclass mappings are being used + * and the chosen strategy is runtime exception. + * + * @param resultTypeToMap the type in which the defined target properties are defined + * @return {@code true} if defined mappings should be handled for the result type, {@code false} otherwise + */ + private boolean shouldHandledDefinedMappings(Type resultTypeToMap) { + if ( !resultTypeToMap.isAbstract() ) { + return true; + } + + if ( hasFactoryMethod ) { + return true; + } + + if ( method.isUpdateMethod() ) { + return true; + } + + return false; + } + /** * Iterates over all defined mapping methods ({@code @Mapping(s)}), either directly given or inherited from the * inverse mapping method. diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Issue3331Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Issue3331Mapper.java new file mode 100644 index 0000000000..3011479c65 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Issue3331Mapper.java @@ -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._3331; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION) +public interface Issue3331Mapper { + + Issue3331Mapper INSTANCE = Mappers.getMapper( Issue3331Mapper.class ); + + @SubclassMapping(source = Vehicle.Car.class, target = VehicleDto.Car.class) + @SubclassMapping(source = Vehicle.Motorbike.class, target = VehicleDto.Motorbike.class) + @Mapping(target = "name", constant = "noname") + VehicleDto map(Vehicle vehicle); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Issue3331Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Issue3331Test.java new file mode 100644 index 0000000000..3871868226 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Issue3331Test.java @@ -0,0 +1,48 @@ +/* + * 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._3331; + +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; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * @author Filip Hrisafov + */ +@IssueKey("3331") +@WithClasses({ + Issue3331Mapper.class, + Vehicle.class, + VehicleDto.class, +}) +class Issue3331Test { + + @ProcessorTest + void shouldCorrectCompileAndThrowExceptionOnRuntime() { + VehicleDto target = Issue3331Mapper.INSTANCE.map( new Vehicle.Car( "Test car", 4 ) ); + + assertThat( target.getName() ).isEqualTo( "noname" ); + assertThat( target ) + .isInstanceOfSatisfying( VehicleDto.Car.class, car -> { + assertThat( car.getNumOfDoors() ).isEqualTo( 4 ); + } ); + + target = Issue3331Mapper.INSTANCE.map( new Vehicle.Motorbike( "Test bike", true ) ); + + assertThat( target.getName() ).isEqualTo( "noname" ); + assertThat( target ) + .isInstanceOfSatisfying( VehicleDto.Motorbike.class, bike -> { + assertThat( bike.isAllowedForMinor() ).isTrue(); + } ); + + assertThatThrownBy( () -> Issue3331Mapper.INSTANCE.map( new Vehicle.Truck( "Test truck", 3 ) ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Not all subclasses are supported for this mapping. Missing for " + Vehicle.Truck.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Vehicle.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Vehicle.java new file mode 100644 index 0000000000..3b68a3acc4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/Vehicle.java @@ -0,0 +1,64 @@ +/* + * 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._3331; + +/** + * @author Filip Hrisafov + */ +public abstract class Vehicle { + + private final String name; + + protected Vehicle(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static class Car extends Vehicle { + + private final int numOfDoors; + + public Car(String name, int numOfDoors) { + super( name ); + this.numOfDoors = numOfDoors; + } + + public int getNumOfDoors() { + return numOfDoors; + } + } + + public static class Motorbike extends Vehicle { + + private final boolean allowedForMinor; + + public Motorbike(String name, boolean allowedForMinor) { + super( name ); + this.allowedForMinor = allowedForMinor; + } + + public boolean isAllowedForMinor() { + return allowedForMinor; + } + } + + public static class Truck extends Vehicle { + + private final int numOfAxis; + + public Truck(String name, int numOfAxis) { + super( name ); + this.numOfAxis = numOfAxis; + } + + public int getNumOfAxis() { + return numOfAxis; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/VehicleDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/VehicleDto.java new file mode 100644 index 0000000000..8c2ef13dd2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3331/VehicleDto.java @@ -0,0 +1,51 @@ +/* + * 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._3331; + +/** + * @author Filip Hrisafov + */ +public abstract class VehicleDto { + + private final String name; + + protected VehicleDto(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static class Car extends VehicleDto { + + private final int numOfDoors; + + public Car(String name, int numOfDoors) { + super( name ); + this.numOfDoors = numOfDoors; + } + + public int getNumOfDoors() { + return numOfDoors; + } + } + + public static class Motorbike extends VehicleDto { + + private final boolean allowedForMinor; + + public Motorbike(String name, boolean allowedForMinor) { + super( name ); + this.allowedForMinor = allowedForMinor; + } + + public boolean isAllowedForMinor() { + return allowedForMinor; + } + } + +} From bb1cd6348502c592dfb77aa5ac86cfe62cc0ba32 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sun, 11 Feb 2024 13:29:34 +0100 Subject: [PATCH 403/597] #2788 Improve unmapped source properties message for forged methods --- .../ap/internal/model/BeanMappingMethod.java | 115 +++---- .../mapstruct/ap/internal/util/Message.java | 2 + .../nestedbeans/DottedErrorMessageTest.java | 285 ++++++++++++++---- ...SourceCollectionElementPropertyMapper.java | 17 ++ .../UnmappableSourceDeepListMapper.java | 17 ++ .../UnmappableSourceDeepMapKeyMapper.java | 17 ++ .../UnmappableSourceDeepMapValueMapper.java | 17 ++ .../UnmappableSourceDeepNestingMapper.java | 17 ++ ...r.java => UnmappableSourceEnumMapper.java} | 2 +- .../UnmappableSourceValuePropertyMapper.java | 17 ++ ...argetCollectionElementPropertyMapper.java} | 4 +- ...va => UnmappableTargetDeepListMapper.java} | 4 +- ... => UnmappableTargetDeepMapKeyMapper.java} | 4 +- ...> UnmappableTargetDeepMapValueMapper.java} | 4 +- ...=> UnmappableTargetDeepNestingMapper.java} | 4 +- ... UnmappableTargetValuePropertyMapper.java} | 4 +- ...eWarnCollectionElementPropertyMapper.java} | 7 +- ...> UnmappableSourceWarnDeepListMapper.java} | 7 +- ...UnmappableSourceWarnDeepMapKeyMapper.java} | 7 +- ...mappableSourceWarnDeepMapValueMapper.java} | 7 +- ...nmappableSourceWarnDeepNestingMapper.java} | 7 +- ...appableSourceWarnValuePropertyMapper.java} | 7 +- ...etWarnCollectionElementPropertyMapper.java | 17 ++ .../UnmappableTargetWarnDeepListMapper.java | 17 ++ .../UnmappableTargetWarnDeepMapKeyMapper.java | 17 ++ ...nmappableTargetWarnDeepMapValueMapper.java | 17 ++ ...UnmappableTargetWarnDeepNestingMapper.java | 17 ++ ...mappableTargetWarnValuePropertyMapper.java | 17 ++ 28 files changed, 546 insertions(+), 128 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/UnmappableSourceCollectionElementPropertyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/UnmappableSourceDeepListMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/UnmappableSourceDeepMapKeyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/UnmappableSourceDeepMapValueMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/UnmappableSourceDeepNestingMapper.java rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/{UnmappableEnumMapper.java => UnmappableSourceEnumMapper.java} (96%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/UnmappableSourceValuePropertyMapper.java rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/{UnmappableCollectionElementPropertyMapper.java => UnmappableTargetCollectionElementPropertyMapper.java} (68%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/{UnmappableDeepListMapper.java => UnmappableTargetDeepListMapper.java} (71%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/{UnmappableDeepMapKeyMapper.java => UnmappableTargetDeepMapKeyMapper.java} (71%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/{UnmappableDeepMapValueMapper.java => UnmappableTargetDeepMapValueMapper.java} (70%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/{UnmappableDeepNestingMapper.java => UnmappableTargetDeepNestingMapper.java} (71%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/erroneous/{UnmappableValuePropertyMapper.java => UnmappableTargetValuePropertyMapper.java} (70%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/{UnmappableWarnCollectionElementPropertyMapper.java => UnmappableSourceWarnCollectionElementPropertyMapper.java} (55%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/{UnmappableWarnDeepListMapper.java => UnmappableSourceWarnDeepListMapper.java} (56%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/{UnmappableWarnDeepMapKeyMapper.java => UnmappableSourceWarnDeepMapKeyMapper.java} (56%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/{UnmappableWarnDeepMapValueMapper.java => UnmappableSourceWarnDeepMapValueMapper.java} (56%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/{UnmappableWarnDeepNestingMapper.java => UnmappableSourceWarnDeepNestingMapper.java} (56%) rename processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/{UnmappableWarnValuePropertyMapper.java => UnmappableSourceWarnValuePropertyMapper.java} (56%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/UnmappableTargetWarnCollectionElementPropertyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/UnmappableTargetWarnDeepListMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/UnmappableTargetWarnDeepMapKeyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/UnmappableTargetWarnDeepMapValueMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/UnmappableTargetWarnDeepNestingMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nestedbeans/unmappable/warn/UnmappableTargetWarnValuePropertyMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 8979901f80..9abed3ec44 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1718,54 +1718,22 @@ private void reportErrorForUnmappedTargetPropertiesIfRequired() { } else if ( !unprocessedTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) { - if ( !( method instanceof ForgedMethod ) ) { - Message msg = unmappedTargetPolicy.getDiagnosticKind() == Diagnostic.Kind.ERROR ? - Message.BEANMAPPING_UNMAPPED_TARGETS_ERROR : Message.BEANMAPPING_UNMAPPED_TARGETS_WARNING; - Object[] args = new Object[] { - MessageFormat.format( - "{0,choice,1#property|1 unmappedProperties, + Message unmappedPropertiesMsg, + Message unmappedForgedPropertiesMsg) { + if ( !( method instanceof ForgedMethod ) ) { Object[] args = new Object[] { MessageFormat.format( "{0,choice,1#property|1 Date: Sun, 11 Feb 2024 19:53:38 +0100 Subject: [PATCH 404/597] #3360 Do not report unmapped source and target properties when result type is abstract due to runtime exception subclass exhaustive strategy (#3526) --- .../ap/internal/model/BeanMappingMethod.java | 6 +- .../ap/test/bugs/_3360/Issue3360Mapper.java | 34 ++++++++++++ .../ap/test/bugs/_3360/Issue3360Test.java | 43 +++++++++++++++ .../mapstruct/ap/test/bugs/_3360/Vehicle.java | 55 +++++++++++++++++++ .../ap/test/bugs/_3360/VehicleDto.java | 45 +++++++++++++++ 5 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Issue3360Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Issue3360Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Vehicle.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/VehicleDto.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 9abed3ec44..dd3a86e2cf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -319,8 +319,10 @@ else if ( !method.isUpdateMethod() ) { handleUnmappedConstructorProperties(); // report errors on unmapped properties - reportErrorForUnmappedTargetPropertiesIfRequired(); - reportErrorForUnmappedSourcePropertiesIfRequired(); + if ( shouldHandledDefinedMappings ) { + reportErrorForUnmappedTargetPropertiesIfRequired(); + reportErrorForUnmappedSourcePropertiesIfRequired(); + } reportErrorForMissingIgnoredSourceProperties(); reportErrorForUnusedSourceParameters(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Issue3360Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Issue3360Mapper.java new file mode 100644 index 0000000000..812d885489 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Issue3360Mapper.java @@ -0,0 +1,34 @@ +/* + * 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._3360; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper( + subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION, + unmappedTargetPolicy = ReportingPolicy.ERROR, + unmappedSourcePolicy = ReportingPolicy.ERROR +) +public interface Issue3360Mapper { + + Issue3360Mapper INSTANCE = Mappers.getMapper( Issue3360Mapper.class ); + + @SubclassMapping(target = VehicleDto.Car.class, source = Vehicle.Car.class) + VehicleDto map(Vehicle vehicle); + + @Mapping(target = "model", source = "modelName") + @BeanMapping(ignoreUnmappedSourceProperties = "computedName") + VehicleDto.Car map(Vehicle.Car car); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Issue3360Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Issue3360Test.java new file mode 100644 index 0000000000..307c2b265a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Issue3360Test.java @@ -0,0 +1,43 @@ +/* + * 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._3360; + +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; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * @author Filip Hrisafov + */ +@IssueKey("3360") +@WithClasses({ + Issue3360Mapper.class, + Vehicle.class, + VehicleDto.class, +}) +class Issue3360Test { + + @ProcessorTest + void shouldCompileWithoutErrorsAndWarnings() { + + Vehicle vehicle = new Vehicle.Car( "Test", "car", 4 ); + + VehicleDto target = Issue3360Mapper.INSTANCE.map( vehicle ); + + assertThat( target.getName() ).isEqualTo( "Test" ); + assertThat( target.getModel() ).isEqualTo( "car" ); + assertThat( target ).isInstanceOfSatisfying( VehicleDto.Car.class, car -> { + assertThat( car.getNumOfDoors() ).isEqualTo( 4 ); + } ); + + assertThatThrownBy( () -> Issue3360Mapper.INSTANCE.map( new Vehicle.Motorbike( "Test", "bike" ) ) ) + .isInstanceOf( IllegalArgumentException.class ) + .hasMessage( "Not all subclasses are supported for this mapping. Missing for " + Vehicle.Motorbike.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Vehicle.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Vehicle.java new file mode 100644 index 0000000000..0cc2011bc9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/Vehicle.java @@ -0,0 +1,55 @@ +/* + * 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._3360; + +/** + * @author Filip Hrisafov + */ +public abstract class Vehicle { + + private final String name; + private final String modelName; + + protected Vehicle(String name, String modelName) { + this.name = name; + this.modelName = modelName; + } + + public String getName() { + return name; + } + + public String getModelName() { + return modelName; + } + + public String getComputedName() { + return null; + } + + public static class Car extends Vehicle { + + private final int numOfDoors; + + public Car(String name, String modelName, int numOfDoors) { + super( name, modelName ); + this.numOfDoors = numOfDoors; + } + + public int getNumOfDoors() { + return numOfDoors; + } + } + + public static class Motorbike extends Vehicle { + + public Motorbike(String name, String modelName) { + super( name, modelName ); + } + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/VehicleDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/VehicleDto.java new file mode 100644 index 0000000000..fc93a2fcae --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3360/VehicleDto.java @@ -0,0 +1,45 @@ +/* + * 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._3360; + +/** + * @author Filip Hrisafov + */ +public abstract class VehicleDto { + + private String name; + private String model; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public static class Car extends VehicleDto { + + private int numOfDoors; + + public int getNumOfDoors() { + return numOfDoors; + } + + public void setNumOfDoors(int numOfDoors) { + this.numOfDoors = numOfDoors; + } + } + +} From 2c12e75bfcae80aed0aa2674ccdf248280a6f4dc Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Sun, 3 Mar 2024 18:59:32 +0100 Subject: [PATCH 405/597] #3485 Exception for Mapping to target = "." without source --- .../internal/model/source/MappingOptions.java | 3 ++ .../mapstruct/ap/internal/util/Message.java | 1 + .../bugs/_3485/ErroneousIssue3485Mapper.java | 35 +++++++++++++++++++ .../ap/test/bugs/_3485/Issue3485Test.java | 33 +++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/ErroneousIssue3485Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index f60eba1931..e0994d746f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -260,6 +260,9 @@ else if ( gem.nullValuePropertyMappingStrategy().hasValue() else if ( ".".equals( gem.target().get() ) && gem.ignore().hasValue() && gem.ignore().getValue() ) { message = Message.PROPERTYMAPPING_TARGET_THIS_AND_IGNORE; } + else if ( ".".equals( gem.target().get() ) && !gem.source().hasValue() ) { + message = Message.PROPERTYMAPPING_TARGET_THIS_NO_SOURCE; + } if ( message == null ) { return true; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index ab8fa01010..68c079cb96 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -71,6 +71,7 @@ public enum Message { PROPERTYMAPPING_DEFAULT_EXPERSSION_AND_NVPMS( "DefaultExpression and nullValuePropertyMappingStrategy are both defined in @Mapping, either define a defaultExpression or an nullValuePropertyMappingStrategy." ), PROPERTYMAPPING_IGNORE_AND_NVPMS( "Ignore and nullValuePropertyMappingStrategy are both defined in @Mapping, either define ignore or an nullValuePropertyMappingStrategy." ), PROPERTYMAPPING_TARGET_THIS_AND_IGNORE( "Using @Mapping( target = \".\", ignore = true ) is not allowed. You need to use @BeanMapping( ignoreByDefault = true ) if you would like to ignore all non explicitly mapped target properties." ), + PROPERTYMAPPING_TARGET_THIS_NO_SOURCE( "Using @Mapping( target = \".\") requires a source property. Expression or constant cannot be used as a source."), PROPERTYMAPPING_EXPRESSION_AND_QUALIFIER_BOTH_DEFINED("Expression and a qualifier both defined in @Mapping, either define an expression or a qualifier."), PROPERTYMAPPING_INVALID_EXPRESSION( "Value for expression must be given in the form \"java()\"." ), PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION( "Value for default expression must be given in the form \"java()\"." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/ErroneousIssue3485Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/ErroneousIssue3485Mapper.java new file mode 100644 index 0000000000..5714e04684 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/ErroneousIssue3485Mapper.java @@ -0,0 +1,35 @@ +/* + * 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._3485; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author hduelme + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface ErroneousIssue3485Mapper { + + ErroneousIssue3485Mapper INSTANCE = Mappers.getMapper( ErroneousIssue3485Mapper.class ); + class Target { + private final String value; + + public Target( String value ) { + this.value = value; + } + + public String getValue() { + return value; + } + + } + + @Mapping(target = ".") + Target targetFromExpression(String s); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java new file mode 100644 index 0000000000..cf7e0563af --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java @@ -0,0 +1,33 @@ +/* + * 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._3485; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +/** + * @author hduelme + */ +@IssueKey("3463") +public class Issue3485Test { + + @ProcessorTest + @WithClasses(ErroneousIssue3485Mapper.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousIssue3485Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 33, + message = "Using @Mapping( target = \".\") requires a source property. Expression or " + + "constant cannot be used as a source.") + }) + void thisMappingWithoutSource() { + } +} From e815e3cb1eabfef2930accf7d5f2c442ff2a456e Mon Sep 17 00:00:00 2001 From: Oliver Erhart <8238759+thunderhook@users.noreply.github.com> Date: Sun, 10 Mar 2024 09:15:37 +0100 Subject: [PATCH 406/597] #3524 Provide tests with Lombok style super builders --- .../ap/test/superbuilder/AbstractVehicle.java | 46 +++++ .../test/superbuilder/AbstractVehicleDto.java | 20 +++ .../mapstruct/ap/test/superbuilder/Car.java | 63 +++++++ .../ap/test/superbuilder/CarDto.java | 21 +++ .../ap/test/superbuilder/CarMapper.java | 43 +++++ .../superbuilder/ChainedAccessorsCar.java | 58 +++++++ .../superbuilder/ChainedAccessorsVehicle.java | 72 ++++++++ .../superbuilder/InheritedAbstractCar.java | 54 ++++++ .../superbuilder/InheritedAbstractCarDto.java | 21 +++ .../ap/test/superbuilder/MuscleCar.java | 52 ++++++ .../ap/test/superbuilder/MuscleCarDto.java | 21 +++ .../ap/test/superbuilder/Passenger.java | 50 ++++++ .../ap/test/superbuilder/PassengerDto.java | 20 +++ .../test/superbuilder/SuperBuilderTest.java | 160 ++++++++++++++++++ .../ap/test/superbuilder/Vehicle.java | 50 ++++++ .../ap/test/superbuilder/VehicleDto.java | 25 +++ 16 files changed, 776 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/AbstractVehicle.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/AbstractVehicleDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/Car.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/CarDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/CarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/ChainedAccessorsCar.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/ChainedAccessorsVehicle.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/InheritedAbstractCar.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/InheritedAbstractCarDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/MuscleCar.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/MuscleCarDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/Passenger.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/PassengerDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/SuperBuilderTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/Vehicle.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/superbuilder/VehicleDto.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/AbstractVehicle.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/AbstractVehicle.java new file mode 100644 index 0000000000..d9cf159888 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/AbstractVehicle.java @@ -0,0 +1,46 @@ +/* + * 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.superbuilder; + +public abstract class AbstractVehicle { + + private final int amountOfTires; + private final Passenger passenger; + + protected AbstractVehicle(AbstractVehicleBuilder b) { + this.amountOfTires = b.amountOfTires; + this.passenger = b.passenger; + } + + public int getAmountOfTires() { + return this.amountOfTires; + } + + public Passenger getPassenger() { + return this.passenger; + } + + public abstract static class AbstractVehicleBuilder> { + + private int amountOfTires; + private Passenger passenger; + + public B amountOfTires(int amountOfTires) { + this.amountOfTires = amountOfTires; + return self(); + } + + public B passenger(Passenger passenger) { + this.passenger = passenger; + return self(); + } + + protected abstract B self(); + + public abstract C build(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/AbstractVehicleDto.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/AbstractVehicleDto.java new file mode 100644 index 0000000000..0b97398ae0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/AbstractVehicleDto.java @@ -0,0 +1,20 @@ +/* + * 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.superbuilder; + +public abstract class AbstractVehicleDto { + + private final int amountOfTires; + + public AbstractVehicleDto(int amountOfTires) { + this.amountOfTires = amountOfTires; + } + + public int getAmountOfTires() { + return amountOfTires; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/Car.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/Car.java new file mode 100644 index 0000000000..af15ee6631 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/Car.java @@ -0,0 +1,63 @@ +/* + * 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.superbuilder; + +public class Car extends Vehicle { + + private final String manufacturer; + private final Passenger passenger; + + protected Car(CarBuilder b) { + super( b ); + this.manufacturer = b.manufacturer; + this.passenger = b.passenger; + } + + public static CarBuilder builder() { + return new CarBuilderImpl(); + } + + public String getManufacturer() { + return this.manufacturer; + } + + public Passenger getPassenger() { + return this.passenger; + } + + public abstract static class CarBuilder> extends VehicleBuilder { + + private String manufacturer; + private Passenger passenger; + + public B manufacturer(String manufacturer) { + this.manufacturer = manufacturer; + return self(); + } + + public B passenger(Passenger passenger) { + this.passenger = passenger; + return self(); + } + + protected abstract B self(); + + public abstract C build(); + } + + private static final class CarBuilderImpl extends CarBuilder { + private CarBuilderImpl() { + } + + protected CarBuilderImpl self() { + return this; + } + + public Car build() { + return new Car( this ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/CarDto.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/CarDto.java new file mode 100644 index 0000000000..9877e27d71 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/CarDto.java @@ -0,0 +1,21 @@ +/* + * 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.superbuilder; + +public class CarDto extends VehicleDto { + + private final String manufacturer; + + public CarDto(int amountOfTires, String manufacturer, PassengerDto passenger) { + super( amountOfTires, passenger ); + this.manufacturer = manufacturer; + } + + public String getManufacturer() { + return manufacturer; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/CarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/CarMapper.java new file mode 100644 index 0000000000..a3def64ead --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/CarMapper.java @@ -0,0 +1,43 @@ +/* + * 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.superbuilder; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface CarMapper { + + CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); + + @Mapping(target = "amountOfTires", source = "tireCount") + Car carDtoToCar(CarDto source); + + @InheritInverseConfiguration(name = "carDtoToCar") + CarDto carToCarDto(Car source); + + @Mapping(target = "amountOfTires", source = "tireCount") + ChainedAccessorsCar carDtoToChainedAccessorsCar(CarDto source); + + @InheritInverseConfiguration(name = "carDtoToChainedAccessorsCar") + CarDto chainedAccessorsCarToCarDto(ChainedAccessorsCar source); + + @Mapping(target = "amountOfTires", source = "tireCount") + InheritedAbstractCar carDtoToInheritedAbstractCar(CarDto source); + + @InheritInverseConfiguration(name = "carDtoToInheritedAbstractCar") + CarDto inheritedAbstractCarToCarDto(InheritedAbstractCar source); + + @Mapping(target = "amountOfTires", source = "tireCount") + @Mapping(target = "horsePower", constant = "140.5f") + MuscleCar carDtoToMuscleCar(CarDto source); + + @InheritInverseConfiguration(name = "carDtoToMuscleCar") + CarDto muscleCarToCarDto(MuscleCar source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/ChainedAccessorsCar.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/ChainedAccessorsCar.java new file mode 100644 index 0000000000..48faa101c3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/ChainedAccessorsCar.java @@ -0,0 +1,58 @@ +/* + * 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.superbuilder; + +public class ChainedAccessorsCar extends ChainedAccessorsVehicle { + + private String manufacturer; + + protected ChainedAccessorsCar(ChainedAccessorsCarBuilder b) { + super( b ); + this.manufacturer = b.manufacturer; + } + + public static ChainedAccessorsCarBuilder builder() { + return new ChainedAccessorsCarBuilderImpl(); + } + + public String getManufacturer() { + return this.manufacturer; + } + + public ChainedAccessorsCar setManufacturer(String manufacturer) { + this.manufacturer = manufacturer; + return this; + } + + public abstract static class ChainedAccessorsCarBuilder> extends ChainedAccessorsVehicleBuilder { + + private String manufacturer; + + public B manufacturer(String manufacturer) { + this.manufacturer = manufacturer; + return self(); + } + + protected abstract B self(); + + public abstract C build(); + } + + private static final class ChainedAccessorsCarBuilderImpl + extends ChainedAccessorsCarBuilder { + private ChainedAccessorsCarBuilderImpl() { + } + + protected ChainedAccessorsCarBuilderImpl self() { + return this; + } + + public ChainedAccessorsCar build() { + return new ChainedAccessorsCar( this ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/ChainedAccessorsVehicle.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/ChainedAccessorsVehicle.java new file mode 100644 index 0000000000..3344fb26f9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/ChainedAccessorsVehicle.java @@ -0,0 +1,72 @@ +/* + * 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.superbuilder; + +public class ChainedAccessorsVehicle { + private int amountOfTires; + private Passenger passenger; + + protected ChainedAccessorsVehicle(ChainedAccessorsVehicleBuilder b) { + this.amountOfTires = b.amountOfTires; + this.passenger = b.passenger; + } + + public static ChainedAccessorsVehicleBuilder builder() { + return new ChainedAccessorsVehicleBuilderImpl(); + } + + public int getAmountOfTires() { + return this.amountOfTires; + } + + public Passenger getPassenger() { + return this.passenger; + } + + public ChainedAccessorsVehicle setAmountOfTires(int amountOfTires) { + this.amountOfTires = amountOfTires; + return this; + } + + public ChainedAccessorsVehicle setPassenger(Passenger passenger) { + this.passenger = passenger; + return this; + } + + public abstract static class ChainedAccessorsVehicleBuilder> { + private int amountOfTires; + private Passenger passenger; + + public B amountOfTires(int amountOfTires) { + this.amountOfTires = amountOfTires; + return self(); + } + + public B passenger(Passenger passenger) { + this.passenger = passenger; + return self(); + } + + protected abstract B self(); + + public abstract C build(); + } + + private static final class ChainedAccessorsVehicleBuilderImpl + extends ChainedAccessorsVehicleBuilder { + private ChainedAccessorsVehicleBuilderImpl() { + } + + protected ChainedAccessorsVehicleBuilderImpl self() { + return this; + } + + public ChainedAccessorsVehicle build() { + return new ChainedAccessorsVehicle( this ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/InheritedAbstractCar.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/InheritedAbstractCar.java new file mode 100644 index 0000000000..1d07106af8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/InheritedAbstractCar.java @@ -0,0 +1,54 @@ +/* + * 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.superbuilder; + +public class InheritedAbstractCar extends AbstractVehicle { + + private final String manufacturer; + + protected InheritedAbstractCar(InheritedAbstractCarBuilder b) { + super( b ); + this.manufacturer = b.manufacturer; + } + + public static InheritedAbstractCarBuilder builder() { + return new InheritedAbstractCarBuilderImpl(); + } + + public String getManufacturer() { + return this.manufacturer; + } + + public abstract static class InheritedAbstractCarBuilder> + + extends AbstractVehicleBuilder { + private String manufacturer; + + public B manufacturer(String manufacturer) { + this.manufacturer = manufacturer; + return self(); + } + + protected abstract B self(); + + public abstract C build(); + } + + private static final class InheritedAbstractCarBuilderImpl + extends InheritedAbstractCarBuilder { + private InheritedAbstractCarBuilderImpl() { + } + + protected InheritedAbstractCarBuilderImpl self() { + return this; + } + + public InheritedAbstractCar build() { + return new InheritedAbstractCar( this ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/InheritedAbstractCarDto.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/InheritedAbstractCarDto.java new file mode 100644 index 0000000000..a9cc6bbdbb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/InheritedAbstractCarDto.java @@ -0,0 +1,21 @@ +/* + * 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.superbuilder; + +public class InheritedAbstractCarDto extends AbstractVehicleDto { + + private final String manufacturer; + + public InheritedAbstractCarDto(int amountOfTires, String manufacturer) { + super( amountOfTires ); + this.manufacturer = manufacturer; + } + + public String getManufacturer() { + return manufacturer; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/MuscleCar.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/MuscleCar.java new file mode 100644 index 0000000000..ec330536ff --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/MuscleCar.java @@ -0,0 +1,52 @@ +/* + * 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.superbuilder; + +public class MuscleCar extends Car { + + private final float horsePower; + + protected MuscleCar(MuscleCarBuilder b) { + super( b ); + this.horsePower = b.horsePower; + } + + public static MuscleCarBuilder builder() { + return new MuscleCarBuilderImpl(); + } + + public float getHorsePower() { + return this.horsePower; + } + + public abstract static class MuscleCarBuilder> + extends CarBuilder { + + private float horsePower; + + public B horsePower(float horsePower) { + this.horsePower = horsePower; + return self(); + } + + protected abstract B self(); + + public abstract C build(); + } + + private static final class MuscleCarBuilderImpl extends MuscleCarBuilder { + private MuscleCarBuilderImpl() { + } + + protected MuscleCarBuilderImpl self() { + return this; + } + + public MuscleCar build() { + return new MuscleCar( this ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/MuscleCarDto.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/MuscleCarDto.java new file mode 100644 index 0000000000..7ffe150ecf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/MuscleCarDto.java @@ -0,0 +1,21 @@ +/* + * 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.superbuilder; + +public class MuscleCarDto extends CarDto { + + private final float horsePower; + + public MuscleCarDto(int amountOfTires, String manufacturer, PassengerDto passenger, float horsePower) { + super( amountOfTires, manufacturer, passenger ); + this.horsePower = horsePower; + } + + public float getHorsePower() { + return horsePower; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/Passenger.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/Passenger.java new file mode 100644 index 0000000000..2b6740af78 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/Passenger.java @@ -0,0 +1,50 @@ +/* + * 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.superbuilder; + +public class Passenger { + + private final String name; + + protected Passenger(PassengerBuilder b) { + this.name = b.name; + } + + public static PassengerBuilder builder() { + return new PassengerBuilderImpl(); + } + + public String getName() { + return this.name; + } + + public abstract static class PassengerBuilder> { + + private String name; + + public B name(String name) { + this.name = name; + return self(); + } + + protected abstract B self(); + + public abstract C build(); + } + + private static final class PassengerBuilderImpl extends PassengerBuilder { + private PassengerBuilderImpl() { + } + + protected PassengerBuilderImpl self() { + return this; + } + + public Passenger build() { + return new Passenger( this ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/PassengerDto.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/PassengerDto.java new file mode 100644 index 0000000000..f63f6e0683 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/PassengerDto.java @@ -0,0 +1,20 @@ +/* + * 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.superbuilder; + +public class PassengerDto { + + private final String name; + + public PassengerDto(String name) { + this.name = name; + } + + public String getName() { + return name; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/SuperBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/SuperBuilderTest.java new file mode 100644 index 0000000000..93dfb746b0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/SuperBuilderTest.java @@ -0,0 +1,160 @@ +/* + * 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.superbuilder; + +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 the mapping of models annotated with @SuperBuilder as source and target. + * + * @author Oliver Erhart + */ +@WithClasses({ + AbstractVehicleDto.class, + CarDto.class, + InheritedAbstractCarDto.class, + MuscleCarDto.class, + PassengerDto.class, + VehicleDto.class, + AbstractVehicle.class, + Car.class, + ChainedAccessorsCar.class, + ChainedAccessorsVehicle.class, + InheritedAbstractCar.class, + MuscleCar.class, + Passenger.class, + Vehicle.class, + CarMapper.class +}) +@IssueKey("3524") +public class SuperBuilderTest { + + @ProcessorTest + public void simpleMapping() { + + PassengerDto passenger = new PassengerDto( "Tom" ); + CarDto carDto = new CarDto( 4, "BMW", passenger ); + + Car car = CarMapper.INSTANCE.carDtoToCar( carDto ); + + assertThat( car.getManufacturer() ).isEqualTo( "BMW" ); + assertThat( car.getAmountOfTires() ).isEqualTo( 4 ); + assertThat( car.getPassenger().getName() ).isEqualTo( "Tom" ); + } + + @ProcessorTest + public void simpleMappingInverse() { + + Passenger passenger = Passenger.builder().name( "Tom" ).build(); + Car car = Car.builder() + .manufacturer( "BMW" ) + .amountOfTires( 4 ) + .passenger( passenger ) + .build(); + + CarDto carDto = CarMapper.INSTANCE.carToCarDto( car ); + + assertThat( carDto.getManufacturer() ).isEqualTo( "BMW" ); + assertThat( carDto.getTireCount() ).isEqualTo( 4 ); + assertThat( carDto.getPassenger().getName() ).isEqualTo( "Tom" ); + } + + @ProcessorTest + public void chainedMapping() { + + PassengerDto passenger = new PassengerDto( "Tom" ); + CarDto carDto = new CarDto( 4, "BMW", passenger ); + + ChainedAccessorsCar car = CarMapper.INSTANCE.carDtoToChainedAccessorsCar( carDto ); + + assertThat( car.getManufacturer() ).isEqualTo( "BMW" ); + assertThat( car.getAmountOfTires() ).isEqualTo( 4 ); + assertThat( car.getPassenger().getName() ).isEqualTo( "Tom" ); + } + + @ProcessorTest + public void chainedMappingInverse() { + + Passenger passenger = Passenger.builder().name( "Tom" ).build(); + ChainedAccessorsCar chainedAccessorsCar = ChainedAccessorsCar.builder() + .manufacturer( "BMW" ) + .amountOfTires( 4 ) + .passenger( passenger ) + .build(); + + CarDto carDto = CarMapper.INSTANCE.chainedAccessorsCarToCarDto( chainedAccessorsCar ); + + assertThat( carDto.getManufacturer() ).isEqualTo( "BMW" ); + assertThat( carDto.getTireCount() ).isEqualTo( 4 ); + assertThat( carDto.getPassenger().getName() ).isEqualTo( "Tom" ); + } + + @ProcessorTest + public void inheritedAbstractMapping() { + + PassengerDto passenger = new PassengerDto( "Tom" ); + CarDto carDto = new CarDto( 4, "BMW", passenger ); + + InheritedAbstractCar car = CarMapper.INSTANCE.carDtoToInheritedAbstractCar( carDto ); + + assertThat( car.getManufacturer() ).isEqualTo( "BMW" ); + assertThat( car.getAmountOfTires() ).isEqualTo( 4 ); + assertThat( car.getPassenger().getName() ).isEqualTo( "Tom" ); + } + + @ProcessorTest + public void inheritedAbstractMappingInverse() { + + Passenger passenger = Passenger.builder().name( "Tom" ).build(); + InheritedAbstractCar inheritedAbstractCar = InheritedAbstractCar.builder() + .manufacturer( "BMW" ) + .amountOfTires( 4 ) + .passenger( passenger ) + .build(); + + CarDto carDto = CarMapper.INSTANCE.inheritedAbstractCarToCarDto( inheritedAbstractCar ); + + assertThat( carDto.getManufacturer() ).isEqualTo( "BMW" ); + assertThat( carDto.getTireCount() ).isEqualTo( 4 ); + assertThat( carDto.getPassenger().getName() ).isEqualTo( "Tom" ); + } + + @ProcessorTest + public void secondLevelMapping() { + + PassengerDto passenger = new PassengerDto( "Tom" ); + CarDto carDto = new CarDto( 4, "BMW", passenger ); + + MuscleCar car = CarMapper.INSTANCE.carDtoToMuscleCar( carDto ); + + assertThat( car.getManufacturer() ).isEqualTo( "BMW" ); + assertThat( car.getAmountOfTires() ).isEqualTo( 4 ); + assertThat( car.getHorsePower() ).isEqualTo( 140.5f ); + assertThat( car.getPassenger().getName() ).isEqualTo( "Tom" ); + } + + @ProcessorTest + public void secondLevelMappingInverse() { + + Passenger passenger = Passenger.builder().name( "Tom" ).build(); + MuscleCar muscleCar = MuscleCar.builder() + .manufacturer( "BMW" ) + .amountOfTires( 4 ) + .passenger( passenger ) + .build(); + + CarDto carDto = CarMapper.INSTANCE.muscleCarToCarDto( muscleCar ); + + assertThat( carDto.getManufacturer() ).isEqualTo( "BMW" ); + assertThat( carDto.getTireCount() ).isEqualTo( 4 ); + assertThat( carDto.getPassenger().getName() ).isEqualTo( "Tom" ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/Vehicle.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/Vehicle.java new file mode 100644 index 0000000000..2f4a19d842 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/Vehicle.java @@ -0,0 +1,50 @@ +/* + * 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.superbuilder; + +public class Vehicle { + + private final int amountOfTires; + + protected Vehicle(VehicleBuilder b) { + this.amountOfTires = b.amountOfTires; + } + + public static VehicleBuilder builder() { + return new VehicleBuilderImpl(); + } + + public int getAmountOfTires() { + return this.amountOfTires; + } + + public abstract static class VehicleBuilder> { + + private int amountOfTires; + + public B amountOfTires(int amountOfTires) { + this.amountOfTires = amountOfTires; + return self(); + } + + protected abstract B self(); + + public abstract C build(); + } + + private static final class VehicleBuilderImpl extends VehicleBuilder { + private VehicleBuilderImpl() { + } + + protected VehicleBuilderImpl self() { + return this; + } + + public Vehicle build() { + return new Vehicle( this ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/superbuilder/VehicleDto.java b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/VehicleDto.java new file mode 100644 index 0000000000..04dfefb205 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/superbuilder/VehicleDto.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.ap.test.superbuilder; + +public class VehicleDto { + + private final int tireCount; + private final PassengerDto passenger; + + public VehicleDto(int tireCount, PassengerDto passenger) { + this.tireCount = tireCount; + this.passenger = passenger; + } + + public int getTireCount() { + return tireCount; + } + + public PassengerDto getPassenger() { + return passenger; + } +} From 8e66445fe9f13b37ea66191548067a80bcc6d048 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:00:41 +0200 Subject: [PATCH 407/597] #3564 Correct issue key for `Issue3485Test` --- .../java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java index cf7e0563af..7427b63a4e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java @@ -15,7 +15,7 @@ /** * @author hduelme */ -@IssueKey("3463") +@IssueKey("3485") public class Issue3485Test { @ProcessorTest From 5fbd36c44392b7f910e24ceec35fa47a98abba89 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Sun, 28 Apr 2024 20:20:11 +0200 Subject: [PATCH 408/597] #3577 Improve `Mapping#ignoreByDefault` documentation --- core/src/main/java/org/mapstruct/BeanMapping.java | 6 ++++++ .../src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java index a03546b07d..e94ff98f2d 100644 --- a/core/src/main/java/org/mapstruct/BeanMapping.java +++ b/core/src/main/java/org/mapstruct/BeanMapping.java @@ -19,6 +19,8 @@ /** * Configures the mapping between two bean types. *

      + * Unless otherwise specified these properties are inherited to the generated bean mapping methods. + *

      * Either {@link #resultType()}, {@link #qualifiedBy()} or {@link #nullValueMappingStrategy()} must be specified. *

      *

      Example: Determining the result type

      @@ -58,6 +60,8 @@ /** * Specifies the result type of the factory method to be used in case several factory methods qualify. + *

      + * NOTE: This property is not inherited to generated mapping methods * * @return the resultType to select */ @@ -145,6 +149,8 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() * source properties report. *

      * NOTE: This does not support ignoring nested source properties + *

      + * NOTE: This property is not inherited to generated mapping methods * * @return The source properties that should be ignored when performing a report * diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index eb3d5a80ec..2174822359 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -39,7 +39,7 @@ The property name as defined in the http://www.oracle.com/technetwork/java/javas ==== [TIP] ==== -By means of the `@BeanMapping(ignoreByDefault = true)` the default behavior will be *explicit mapping*, meaning that all mappings have to be specified by means of the `@Mapping` and no warnings will be issued on missing target properties. +By means of the `@BeanMapping(ignoreByDefault = true)` the default behavior will be *explicit mapping*, meaning that all mappings (including nested ones) have to be specified by means of the `@Mapping` and no warnings will be issued on missing target properties. This allows to ignore all fields, except the ones that are explicitly defined through `@Mapping`. ==== [TIP] From 0a935c67a7c9ca17a3c6980b96d964c46be527a1 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 28 Apr 2024 19:04:57 +0200 Subject: [PATCH 409/597] #3565 Presence check methods should not be considered as valid mapping candidates --- .../creation/MappingResolverImpl.java | 3 + .../ap/test/bugs/_3565/Issue3565Mapper.java | 57 +++++++++++++++++++ .../ap/test/bugs/_3565/Issue3565Test.java | 33 +++++++++++ 3 files changed, 93 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3565/Issue3565Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3565/Issue3565Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index b290ff56bc..e6a26ba045 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -470,6 +470,9 @@ private ConversionAssignment resolveViaConversion(Type sourceType, Type targetTy } private boolean isCandidateForMapping(Method methodCandidate) { + if ( methodCandidate.isPresenceCheck() ) { + return false; + } return isCreateMethodForMapping( methodCandidate ) || isUpdateMethodForMapping( methodCandidate ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3565/Issue3565Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3565/Issue3565Mapper.java new file mode 100644 index 0000000000..b86e667e56 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3565/Issue3565Mapper.java @@ -0,0 +1,57 @@ +/* + * 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._3565; + +import java.util.Optional; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3565Mapper { + + Issue3565Mapper INSTANCE = Mappers.getMapper( Issue3565Mapper.class ); + + default T mapFromOptional(Optional value) { + return value.orElse( (T) null ); + } + + @Condition + default boolean isOptionalPresent(Optional value) { + return value.isPresent(); + } + + Target map(Source source); + + class Source { + + private final Boolean condition; + + public Source(Boolean condition) { + this.condition = condition; + } + + public Optional getCondition() { + return Optional.ofNullable( this.condition ); + } + } + + class Target { + private String condition; + + public Optional getCondition() { + return Optional.ofNullable( this.condition ); + } + + public void setCondition(String condition) { + this.condition = condition; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3565/Issue3565Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3565/Issue3565Test.java new file mode 100644 index 0000000000..c4cd028d45 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3565/Issue3565Test.java @@ -0,0 +1,33 @@ +/* + * 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._3565; + +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 Filip Hrisafov + */ +@WithClasses(Issue3565Mapper.class) +@IssueKey("3565") +class Issue3565Test { + + @ProcessorTest + void shouldGenerateValidCode() { + Issue3565Mapper.Target target = Issue3565Mapper.INSTANCE.map( new Issue3565Mapper.Source( null ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getCondition() ).isEmpty(); + + target = Issue3565Mapper.INSTANCE.map( new Issue3565Mapper.Source( false ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getCondition() ).hasValue( "false" ); + } +} From 0a2a0aa526fe1c970baba61c4f9dcc23e975c517 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 29 Apr 2024 08:05:52 +0200 Subject: [PATCH 410/597] #2610 Add support for conditions on source parameters + fix incorrect use of source parameter in presence check method (#3543) The new `@SourceParameterCondition` is also going to cover the problems in #3270 and #3459. The changes in the `MethodFamilySelector` are also fixing #3561 --- .../main/java/org/mapstruct/Condition.java | 40 ++-- .../java/org/mapstruct/ConditionStrategy.java | 23 ++ .../mapstruct/SourceParameterCondition.java | 74 +++++++ ...apter-10-advanced-mapping-options.asciidoc | 53 ++++- .../ap/internal/gem/ConditionStrategyGem.java | 15 ++ .../ap/internal/model/BeanMappingMethod.java | 34 ++- .../model/PresenceCheckMethodResolver.java | 92 ++++++-- .../ap/internal/model/common/Parameter.java | 16 +- .../model/common/ParameterBinding.java | 109 +++++++--- .../model/source/ConditionMethodOptions.java | 45 ++++ .../model/source/ConditionOptions.java | 170 +++++++++++++++ .../ap/internal/model/source/Method.java | 12 +- .../internal/model/source/SourceMethod.java | 43 ++-- .../selector/CreateOrUpdateSelector.java | 1 + .../source/selector/MethodFamilySelector.java | 18 +- .../source/selector/SelectionContext.java | 39 ++++ .../source/selector/SelectionCriteria.java | 12 ++ .../processor/MethodRetrievalProcessor.java | 46 +++- .../creation/MappingResolverImpl.java | 2 +- .../mapstruct/ap/internal/util/Message.java | 5 + .../ap/internal/util/MetaAnnotations.java | 85 ++++++++ .../basic/ConditionalMappingTest.java | 198 ++++++++++++++++++ .../ConditionalMethodForSourceBeanMapper.java | 64 ++++++ ...odForSourceParameterAndPropertyMapper.java | 85 ++++++++ ...ourceParameterConditionalMethodMapper.java | 28 +++ ...nditionalWithoutAppliesToMethodMapper.java | 24 +++ ...terConditionalWithMappingTargetMapper.java | 26 +++ ...nditionalWithSourcePropertyNameMapper.java | 26 +++ ...nditionalWithTargetPropertyNameMapper.java | 26 +++ ...ameterConditionalWithTargetTypeMapper.java | 26 +++ .../mapstruct/ap/test/gem/EnumGemsTest.java | 8 + 31 files changed, 1326 insertions(+), 119 deletions(-) create mode 100644 core/src/main/java/org/mapstruct/ConditionStrategy.java create mode 100644 core/src/main/java/org/mapstruct/SourceParameterCondition.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/gem/ConditionStrategyGem.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionMethodOptions.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionOptions.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/MetaAnnotations.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForSourceBeanMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForSourceParameterAndPropertyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousAmbiguousSourceParameterConditionalMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousConditionalWithoutAppliesToMethodMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithMappingTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithSourcePropertyNameMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithTargetPropertyNameMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithTargetTypeMapper.java diff --git a/core/src/main/java/org/mapstruct/Condition.java b/core/src/main/java/org/mapstruct/Condition.java index 37f1553be1..1273deab0a 100644 --- a/core/src/main/java/org/mapstruct/Condition.java +++ b/core/src/main/java/org/mapstruct/Condition.java @@ -11,26 +11,35 @@ import java.lang.annotation.Target; /** - * This annotation marks a method as a presence check method to check check for presence in beans. + * This annotation marks a method as a presence check method to check for presence in beans + * or it can be used to define additional check methods for something like source parameters. *

      - * By default bean properties are checked against {@code null} or using a presence check method in the source bean. + * By default, bean properties are checked against {@code null} or using a presence check method in the source bean. * If a presence check method is available then it will be used instead. *

      * Presence check methods have to return {@code boolean}. * The following parameters are accepted for the presence check methods: *

        *
      • The parameter with the value of the source property. - * e.g. the value given by calling {@code getName()} for the name property of the source bean
      • + * e.g. the value given by calling {@code getName()} for the name property of the source bean + * - only possible when using the {@link ConditionStrategy#PROPERTIES} + * *
      • The mapping source parameter
      • *
      • {@code @}{@link Context} parameter
      • - *
      • {@code @}{@link TargetPropertyName} parameter
      • - *
      • {@code @}{@link SourcePropertyName} parameter
      • + *
      • + * {@code @}{@link TargetPropertyName} parameter - + * only possible when using the {@link ConditionStrategy#PROPERTIES} + *
      • + *
      • + * {@code @}{@link SourcePropertyName} parameter - + * only possible when using the {@link ConditionStrategy#PROPERTIES} + *
      • *
      * * Note: The usage of this annotation is mandatory * for a method to be considered as a presence check method. * - *
      
      + * 
      
        * public class PresenceCheckUtils {
        *
        *   @Condition
      @@ -45,11 +54,10 @@
        *     MovieDto map(Movie movie);
        * }
        * 
      - * + *

      * The following implementation of {@code MovieMapper} will be generated: * - *

      - * 
      + * 
      
        * public class MovieMapperImpl implements MovieMapper {
        *
        *     @Override
      @@ -67,14 +75,22 @@
        *         return movieDto;
        *     }
        * }
      - * 
      - * 
      + *
      + *

      + * This annotation can also be used as a meta-annotation to define the condition strategy. * * @author Filip Hrisafov + * @see SourceParameterCondition * @since 1.5 */ -@Target({ ElementType.METHOD }) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.CLASS) public @interface Condition { + /** + * @return the places where the condition should apply to + * @since 1.6 + */ + ConditionStrategy[] appliesTo() default ConditionStrategy.PROPERTIES; + } diff --git a/core/src/main/java/org/mapstruct/ConditionStrategy.java b/core/src/main/java/org/mapstruct/ConditionStrategy.java new file mode 100644 index 0000000000..6b042017c2 --- /dev/null +++ b/core/src/main/java/org/mapstruct/ConditionStrategy.java @@ -0,0 +1,23 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct; + +/** + * Strategy for defining what to what a condition (check) method is applied to + * + * @author Filip Hrisafov + * @since 1.6 + */ +public enum ConditionStrategy { + /** + * The condition method should be applied whether a property should be mapped. + */ + PROPERTIES, + /** + * The condition method should be applied to check if a source parameters should be mapped. + */ + SOURCE_PARAMETERS, +} diff --git a/core/src/main/java/org/mapstruct/SourceParameterCondition.java b/core/src/main/java/org/mapstruct/SourceParameterCondition.java new file mode 100644 index 0000000000..8bff97abc4 --- /dev/null +++ b/core/src/main/java/org/mapstruct/SourceParameterCondition.java @@ -0,0 +1,74 @@ +/* + * 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; + +/** + * This annotation marks a method as a check method to check if a source parameter needs to be mapped. + *

      + * By default, source parameters are checked against {@code null}, unless they are primitives. + *

      + * Check methods have to return {@code boolean}. + * The following parameters are accepted for the presence check methods: + *

        + *
      • The mapping source parameter
      • + *
      • {@code @}{@link Context} parameter
      • + *
      + * + * Note: The usage of this annotation is mandatory + * for a method to be considered as a source check method. + * + *
      
      + * public class PresenceCheckUtils {
      + *
      + *   @SourceParameterCondition
      + *   public static boolean isDefined(Car car) {
      + *      return car != null && car.getId() != null;
      + *   }
      + * }
      + *
      + * @Mapper(uses = PresenceCheckUtils.class)
      + * public interface CarMapper {
      + *
      + *     CarDto map(Car car);
      + * }
      + * 
      + * + * The following implementation of {@code CarMapper} will be generated: + * + *
      
      + * public class CarMapperImpl implements CarMapper {
      + *
      + *     @Override
      + *     public CarDto map(Car car) {
      + *         if ( !PresenceCheckUtils.isDefined( car ) ) {
      + *             return null;
      + *         }
      + *
      + *         CarDto carDto = new CarDto();
      + *
      + *         carDto.setId( car.getId() );
      + *         // ...
      + *
      + *         return carDto;
      + *     }
      + * }
      + * 
      + * + * @author Filip Hrisafov + * @since 1.6 + * @see Condition @Condition + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.CLASS) +@Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS) +public @interface SourceParameterCondition { + +} diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index db13c0548b..1e2bd133d4 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -303,8 +303,10 @@ null check, regardless the value of the `NullValueCheckStrategy` to avoid additi Conditional Mapping is a type of <>. The difference is that it allows users to write custom condition methods that will be invoked to check if a property needs to be mapped or not. +Conditional mapping can also be used to check if a source parameter should be mapped or not. -A custom condition method is a method that is annotated with `org.mapstruct.Condition` and returns `boolean`. +A custom condition method for properties is a method that is annotated with `org.mapstruct.Condition` and returns `boolean`. +A custom condition method for source parameters is annotated with `org.mapstruct.SourceParameterCondition`, `org.mapstruct.Condition(appliesTo = org.mapstruct.ConditionStrategy#SOURCE_PARAMETERS)` or meta-annotated with `Condition(appliesTo = ConditionStrategy#SOURCE_PARAMETERS)` e.g. if you only want to map a String property when it is not `null`, and it is not empty then you can do something like: @@ -484,6 +486,55 @@ Methods annotated with `@Condition` in addition to the value of the source prope <> is also valid for `@Condition` methods. In order to use a more specific condition method you will need to use one of `Mapping#conditionQualifiedByName` or `Mapping#conditionQualifiedBy`. +If we want to only map cars that have an id provided then we can do something like: + + +.Mapper using custom condition source parameter check method +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface CarMapper { + + CarDto carToCarDto(Car car); + + @SourceParameterCondition + default boolean hasCar(Car car) { + return car != null && car.getId() != null; + } +} +---- +==== + +The generated mapper will look like: + +.Custom condition source parameter check generated implementation +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class CarMapperImpl implements CarMapper { + + @Override + public CarDto carToCarDto(Car car) { + if ( !hasCar( car ) ) { + return null; + } + + CarDto carDto = new CarDto(); + + carDto.setOwner( car.getOwner() ); + + // Mapping of other properties + + return carDto; + } +} +---- +==== + [[exceptions]] === Exceptions 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 new file mode 100644 index 0000000000..adea4b4c1f --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/ConditionStrategyGem.java @@ -0,0 +1,15 @@ +/* + * 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.internal.gem; + +/** + * @author Filip Hrisafov + */ +public enum ConditionStrategyGem { + + PROPERTIES, + SOURCE_PARAMETERS +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index dd3a86e2cf..cf5179f9cb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -411,6 +411,26 @@ else if ( !method.isUpdateMethod() ) { removeMappingReferencesWithoutSourceParameters( afterMappingReferencesWithFinalizedReturnType ); } + Map presenceChecksByParameter = new LinkedHashMap<>(); + for ( Parameter sourceParameter : method.getSourceParameters() ) { + PresenceCheck parameterPresenceCheck = PresenceCheckMethodResolver.getPresenceCheckForSourceParameter( + method, + selectionParameters, + sourceParameter, + ctx + ); + if ( parameterPresenceCheck != null ) { + presenceChecksByParameter.put( sourceParameter.getName(), parameterPresenceCheck ); + } + else if ( !sourceParameter.getType().isPrimitive() ) { + presenceChecksByParameter.put( + sourceParameter.getName(), + new NullPresenceCheck( sourceParameter.getName() ) + ); + } + } + + return new BeanMappingMethod( method, getMethodAnnotations(), @@ -426,7 +446,8 @@ else if ( !method.isUpdateMethod() ) { afterMappingReferencesWithFinalizedReturnType, finalizeMethod, mappingReferences, - subclasses + subclasses, + presenceChecksByParameter ); } @@ -1891,7 +1912,8 @@ private BeanMappingMethod(Method method, List afterMappingReferencesWithFinalizedReturnType, MethodReference finalizerMethod, MappingReferences mappingReferences, - List subclassMappings) { + List subclassMappings, + Map presenceChecksByParameter) { super( method, annotations, @@ -1923,18 +1945,12 @@ private BeanMappingMethod(Method method, // parameter mapping. this.mappingsByParameter = new HashMap<>(); this.constantMappings = new ArrayList<>( propertyMappings.size() ); - this.presenceChecksByParameter = new LinkedHashMap<>(); + this.presenceChecksByParameter = presenceChecksByParameter; this.constructorMappingsByParameter = new LinkedHashMap<>(); this.constructorConstantMappings = new ArrayList<>(); Set sourceParameterNames = new HashSet<>(); for ( Parameter sourceParameter : getSourceParameters() ) { sourceParameterNames.add( sourceParameter.getName() ); - if ( !sourceParameter.getType().isPrimitive() ) { - presenceChecksByParameter.put( - sourceParameter.getName(), - new NullPresenceCheck( sourceParameter.getName() ) - ); - } } for ( PropertyMapping mapping : propertyMappings ) { if ( mapping.isConstructorMapping() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java index a5c873743d..5906db8219 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.stream.Collectors; +import org.mapstruct.ap.internal.gem.ConditionStrategyGem; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.source.Method; @@ -18,6 +19,7 @@ import org.mapstruct.ap.internal.model.source.selector.MethodSelectors; import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; import org.mapstruct.ap.internal.model.source.selector.SelectionContext; +import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; import org.mapstruct.ap.internal.util.Message; /** @@ -34,36 +36,53 @@ public static PresenceCheck getPresenceCheck( SelectionParameters selectionParameters, MappingBuilderContext ctx ) { - SelectedMethod matchingMethod = findMatchingPresenceCheckMethod( + List> matchingMethods = findMatchingMethods( method, - selectionParameters, + SelectionContext.forPresenceCheckMethods( method, selectionParameters, ctx.getTypeFactory() ), ctx ); - if ( matchingMethod == null ) { + if ( matchingMethods.isEmpty() ) { + return null; + } + + if ( matchingMethods.size() > 1 ) { + ctx.getMessager().printMessage( + method.getExecutable(), + Message.GENERAL_AMBIGUOUS_PRESENCE_CHECK_METHOD, + selectionParameters.getSourceRHS().getSourceType().describe(), + matchingMethods.stream() + .map( SelectedMethod::getMethod ) + .map( Method::describe ) + .collect( Collectors.joining( ", " ) ) + ); + return null; } + SelectedMethod matchingMethod = matchingMethods.get( 0 ); + MethodReference methodReference = getPresenceCheckMethodReference( method, matchingMethod, ctx ); return new MethodReferencePresenceCheck( methodReference ); } - private static SelectedMethod findMatchingPresenceCheckMethod( + public static PresenceCheck getPresenceCheckForSourceParameter( Method method, SelectionParameters selectionParameters, + Parameter sourceParameter, MappingBuilderContext ctx ) { - MethodSelectors selectors = new MethodSelectors( - ctx.getTypeUtils(), - ctx.getElementUtils(), - ctx.getMessager() - ); - - List> matchingMethods = selectors.getMatchingMethods( - getAllAvailableMethods( method, ctx.getSourceModel() ), - SelectionContext.forPresenceCheckMethods( method, selectionParameters, ctx.getTypeFactory() ) + List> matchingMethods = findMatchingMethods( + method, + SelectionContext.forSourceParameterPresenceCheckMethods( + method, + selectionParameters, + sourceParameter, + ctx.getTypeFactory() + ), + ctx ); if ( matchingMethods.isEmpty() ) { @@ -73,8 +92,8 @@ private static SelectedMethod findMatchingPresenceCheckMethod( if ( matchingMethods.size() > 1 ) { ctx.getMessager().printMessage( method.getExecutable(), - Message.GENERAL_AMBIGUOUS_PRESENCE_CHECK_METHOD, - selectionParameters.getSourceRHS().getSourceType().describe(), + Message.GENERAL_AMBIGUOUS_SOURCE_PARAMETER_CHECK_METHOD, + sourceParameter.getType().describe(), matchingMethods.stream() .map( SelectedMethod::getMethod ) .map( Method::describe ) @@ -84,7 +103,29 @@ private static SelectedMethod findMatchingPresenceCheckMethod( return null; } - return matchingMethods.get( 0 ); + SelectedMethod matchingMethod = matchingMethods.get( 0 ); + + MethodReference methodReference = getPresenceCheckMethodReference( method, matchingMethod, ctx ); + + return new MethodReferencePresenceCheck( methodReference ); + + } + + private static List> findMatchingMethods( + Method method, + SelectionContext selectionContext, + MappingBuilderContext ctx + ) { + MethodSelectors selectors = new MethodSelectors( + ctx.getTypeUtils(), + ctx.getElementUtils(), + ctx.getMessager() + ); + + return selectors.getMatchingMethods( + getAllAvailableMethods( method, ctx.getSourceModel(), selectionContext.getSelectionCriteria() ), + selectionContext + ); } private static MethodReference getPresenceCheckMethodReference( @@ -116,7 +157,8 @@ private static MethodReference getPresenceCheckMethodReference( } } - private static List getAllAvailableMethods(Method method, List sourceModelMethods) { + private static List getAllAvailableMethods(Method method, List sourceModelMethods, + SelectionCriteria selectionCriteria) { ParameterProvidedMethods contextProvidedMethods = method.getContextProvidedMethods(); if ( contextProvidedMethods.isEmpty() ) { return sourceModelMethods; @@ -129,9 +171,19 @@ private static List getAllAvailableMethods(Method method, List( methodsProvidedByParams.size() + sourceModelMethods.size() ); for ( SourceMethod methodProvidedByParams : methodsProvidedByParams ) { - // add only methods from context that do have the @Condition annotation - if ( methodProvidedByParams.isPresenceCheck() ) { - availableMethods.add( methodProvidedByParams ); + if ( selectionCriteria.isPresenceCheckRequired() ) { + // add only methods from context that do have the @Condition for properties annotation + if ( methodProvidedByParams.getConditionOptions() + .isStrategyApplicable( ConditionStrategyGem.PROPERTIES ) ) { + availableMethods.add( methodProvidedByParams ); + } + } + else if ( selectionCriteria.isSourceParameterCheckRequired() ) { + // add only methods from context that do have the @Condition for source parameters annotation + if ( methodProvidedByParams.getConditionOptions() + .isStrategyApplicable( ConditionStrategyGem.SOURCE_PARAMETERS ) ) { + availableMethods.add( methodProvidedByParams ); + } } } availableMethods.addAll( sourceModelMethods ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java index 44ac0eb7f0..aaab7f46ca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java @@ -133,6 +133,14 @@ public boolean isVarArgs() { return varArgs; } + public boolean isSourceParameter() { + return !isMappingTarget() && + !isTargetType() && + !isMappingContext() && + !isSourcePropertyName() && + !isTargetPropertyName(); + } + @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; @@ -224,12 +232,4 @@ public static Parameter getTargetPropertyNameParameter(List parameter return parameters.stream().filter( Parameter::isTargetPropertyName ).findAny().orElse( null ); } - private static boolean isSourceParameter( Parameter parameter ) { - return !parameter.isMappingTarget() && - !parameter.isTargetType() && - !parameter.isMappingContext() && - !parameter.isSourcePropertyName() && - !parameter.isTargetPropertyName(); - } - } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java index 0791ee626a..c1a594c73a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java @@ -6,7 +6,9 @@ package org.mapstruct.ap.internal.model.common; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.List; import java.util.Set; @@ -19,23 +21,14 @@ public class ParameterBinding { private final Type type; private final String variableName; - private final boolean targetType; - private final boolean mappingTarget; - private final boolean mappingContext; - private final boolean sourcePropertyName; - private final boolean targetPropertyName; private final SourceRHS sourceRHS; + private final Collection bindingTypes; - private ParameterBinding(Type parameterType, String variableName, boolean mappingTarget, boolean targetType, - boolean mappingContext, boolean sourcePropertyName, boolean targetPropertyName, + private ParameterBinding(Type parameterType, String variableName, Collection bindingTypes, SourceRHS sourceRHS) { this.type = parameterType; this.variableName = variableName; - this.targetType = targetType; - this.mappingTarget = mappingTarget; - this.mappingContext = mappingContext; - this.sourcePropertyName = sourcePropertyName; - this.targetPropertyName = targetPropertyName; + this.bindingTypes = bindingTypes; this.sourceRHS = sourceRHS; } @@ -46,39 +39,47 @@ public String getVariableName() { return variableName; } + public boolean isSourceParameter() { + return bindingTypes.contains( BindingType.PARAMETER ); + } + /** * @return {@code true}, if the parameter being bound is a {@code @TargetType} parameter. */ public boolean isTargetType() { - return targetType; + return bindingTypes.contains( BindingType.TARGET_TYPE ); } /** * @return {@code true}, if the parameter being bound is a {@code @MappingTarget} parameter. */ public boolean isMappingTarget() { - return mappingTarget; + return bindingTypes.contains( BindingType.MAPPING_TARGET ); } /** * @return {@code true}, if the parameter being bound is a {@code @MappingContext} parameter. */ public boolean isMappingContext() { - return mappingContext; + return bindingTypes.contains( BindingType.CONTEXT ); + } + + public boolean isForSourceRhs() { + return bindingTypes.contains( BindingType.SOURCE_RHS ); } /** * @return {@code true}, if the parameter being bound is a {@code @SourcePropertyName} parameter. */ public boolean isSourcePropertyName() { - return sourcePropertyName; + return bindingTypes.contains( BindingType.SOURCE_PROPERTY_NAME ); } /** * @return {@code true}, if the parameter being bound is a {@code @TargetPropertyName} parameter. */ public boolean isTargetPropertyName() { - return targetPropertyName; + return bindingTypes.contains( BindingType.TARGET_PROPERTY_NAME ); } /** @@ -96,7 +97,7 @@ public SourceRHS getSourceRHS() { } public Set getImportTypes() { - if ( targetType ) { + if ( isTargetType() ) { return type.getImportTypes(); } @@ -112,14 +113,31 @@ public Set getImportTypes() { * @return a parameter binding reflecting the given parameter as being used as argument for a method call */ public static ParameterBinding fromParameter(Parameter parameter) { + EnumSet bindingTypes = EnumSet.of( BindingType.PARAMETER ); + if ( parameter.isMappingTarget() ) { + bindingTypes.add( BindingType.MAPPING_TARGET ); + } + + if ( parameter.isTargetType() ) { + bindingTypes.add( BindingType.TARGET_TYPE ); + } + + if ( parameter.isMappingContext() ) { + bindingTypes.add( BindingType.CONTEXT ); + } + + if ( parameter.isSourcePropertyName() ) { + bindingTypes.add( BindingType.SOURCE_PROPERTY_NAME ); + } + + if ( parameter.isTargetPropertyName() ) { + bindingTypes.add( BindingType.TARGET_PROPERTY_NAME ); + } + return new ParameterBinding( parameter.getType(), parameter.getName(), - parameter.isMappingTarget(), - parameter.isTargetType(), - parameter.isMappingContext(), - parameter.isSourcePropertyName(), - parameter.isTargetPropertyName(), + bindingTypes, null ); } @@ -136,11 +154,7 @@ public static ParameterBinding fromTypeAndName(Type parameterType, String parame return new ParameterBinding( parameterType, parameterName, - false, - false, - false, - false, - false, + Collections.emptySet(), null ); } @@ -150,21 +164,31 @@ public static ParameterBinding fromTypeAndName(Type parameterType, String parame * @return a parameter binding representing a target type parameter */ public static ParameterBinding forTargetTypeBinding(Type classTypeOf) { - return new ParameterBinding( classTypeOf, null, false, true, false, false, false, null ); + return new ParameterBinding( classTypeOf, null, Collections.singleton( BindingType.TARGET_TYPE ), null ); } /** * @return a parameter binding representing a target property name parameter */ public static ParameterBinding forTargetPropertyNameBinding(Type classTypeOf) { - return new ParameterBinding( classTypeOf, null, false, false, false, false, true, null ); + return new ParameterBinding( + classTypeOf, + null, + Collections.singleton( BindingType.TARGET_PROPERTY_NAME ), + null + ); } /** * @return a parameter binding representing a source property name parameter */ public static ParameterBinding forSourcePropertyNameBinding(Type classTypeOf) { - return new ParameterBinding( classTypeOf, null, false, false, false, true, false, null ); + return new ParameterBinding( + classTypeOf, + null, + Collections.singleton( BindingType.SOURCE_PROPERTY_NAME ), + null + ); } /** @@ -172,7 +196,7 @@ public static ParameterBinding forSourcePropertyNameBinding(Type classTypeOf) { * @return a parameter binding representing a mapping target parameter */ public static ParameterBinding forMappingTargetBinding(Type resultType) { - return new ParameterBinding( resultType, null, true, false, false, false, false, null ); + return new ParameterBinding( resultType, null, Collections.singleton( BindingType.MAPPING_TARGET ), null ); } /** @@ -180,10 +204,27 @@ public static ParameterBinding forMappingTargetBinding(Type resultType) { * @return a parameter binding representing a mapping source type */ public static ParameterBinding forSourceTypeBinding(Type sourceType) { - return new ParameterBinding( sourceType, null, false, false, false, false, false, null ); + return new ParameterBinding( sourceType, null, Collections.singleton( BindingType.SOURCE_TYPE ), null ); } public static ParameterBinding fromSourceRHS(SourceRHS sourceRHS) { - return new ParameterBinding( sourceRHS.getSourceType(), null, false, false, false, false, false, sourceRHS ); + return new ParameterBinding( + sourceRHS.getSourceType(), + null, + Collections.singleton( BindingType.SOURCE_RHS ), + sourceRHS + ); + } + + enum BindingType { + PARAMETER, + FROM_TYPE_AND_NAME, + TARGET_TYPE, + TARGET_PROPERTY_NAME, + SOURCE_PROPERTY_NAME, + MAPPING_TARGET, + CONTEXT, + SOURCE_TYPE, + SOURCE_RHS } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionMethodOptions.java new file mode 100644 index 0000000000..dfb0865ecc --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionMethodOptions.java @@ -0,0 +1,45 @@ +/* + * 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.internal.model.source; + +import java.util.Collection; +import java.util.Collections; + +import org.mapstruct.ap.internal.gem.ConditionStrategyGem; + +/** + * Encapsulates all options specific for a condition check method. + * + * @author Filip Hrisafov + */ +public class ConditionMethodOptions { + + private static final ConditionMethodOptions EMPTY = new ConditionMethodOptions( Collections.emptyList() ); + + private final Collection conditionOptions; + + public ConditionMethodOptions(Collection conditionOptions) { + this.conditionOptions = conditionOptions; + } + + public boolean isStrategyApplicable(ConditionStrategyGem strategy) { + for ( ConditionOptions conditionOption : conditionOptions ) { + if ( conditionOption.getConditionStrategies().contains( strategy ) ) { + return true; + } + } + + return false; + } + + public boolean isAnyStrategyApplicable() { + return !conditionOptions.isEmpty(); + } + + public static ConditionMethodOptions empty() { + return EMPTY; + } +} 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 new file mode 100644 index 0000000000..936d049af8 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionOptions.java @@ -0,0 +1,170 @@ +/* + * 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.internal.model.source; + +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.internal.gem.ConditionGem; +import org.mapstruct.ap.internal.gem.ConditionStrategyGem; +import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.Message; + +/** + * @author Filip Hrisafov + */ +public class ConditionOptions { + + private final Set conditionStrategies; + + private ConditionOptions(Set conditionStrategies) { + this.conditionStrategies = conditionStrategies; + } + + public Collection getConditionStrategies() { + return conditionStrategies; + } + + public static ConditionOptions getInstanceOn(ConditionGem condition, ExecutableElement method, + List parameters, + FormattingMessager messager) { + if ( condition == null ) { + return null; + } + + TypeMirror returnType = method.getReturnType(); + TypeKind returnTypeKind = returnType.getKind(); + // We only allow methods that return boolean or Boolean to be condition methods + if ( returnTypeKind != TypeKind.BOOLEAN ) { + if ( returnTypeKind != TypeKind.DECLARED ) { + return null; + } + DeclaredType declaredType = (DeclaredType) returnType; + TypeElement returnTypeElement = (TypeElement) declaredType.asElement(); + if ( !returnTypeElement.getQualifiedName().contentEquals( Boolean.class.getCanonicalName() ) ) { + return null; + } + } + + Set strategies = condition.appliesTo().get() + .stream() + .map( ConditionStrategyGem::valueOf ) + .collect( Collectors.toCollection( () -> EnumSet.noneOf( ConditionStrategyGem.class ) ) ); + + if ( strategies.isEmpty() ) { + messager.printMessage( + method, + condition.mirror(), + condition.appliesTo().getAnnotationValue(), + Message.CONDITION_MISSING_APPLIES_TO_STRATEGY + ); + + return null; + } + + boolean allStrategiesValid = true; + + for ( ConditionStrategyGem strategy : strategies ) { + boolean isStrategyValid = isValid( strategy, condition, method, parameters, messager ); + allStrategiesValid &= isStrategyValid; + } + + return allStrategiesValid ? new ConditionOptions( strategies ) : null; + } + + protected static boolean isValid(ConditionStrategyGem strategy, ConditionGem condition, + ExecutableElement method, List parameters, + FormattingMessager messager) { + if ( strategy == ConditionStrategyGem.SOURCE_PARAMETERS ) { + return hasValidStrategyForSourceProperties( condition, method, parameters, messager ); + } + else if ( strategy == ConditionStrategyGem.PROPERTIES ) { + return hasValidStrategyForProperties( condition, method, parameters, messager ); + } + else { + throw new IllegalStateException( "Invalid condition strategy: " + strategy ); + } + } + + protected static boolean hasValidStrategyForSourceProperties(ConditionGem condition, ExecutableElement method, + List parameters, + FormattingMessager messager) { + for ( Parameter parameter : parameters ) { + if ( parameter.isSourceParameter() ) { + // source parameter is a valid parameter for a source condition check + continue; + } + + if ( parameter.isMappingContext() ) { + // mapping context parameter is a valid parameter for a source condition check + continue; + } + + messager.printMessage( + method, + condition.mirror(), + Message.CONDITION_SOURCE_PARAMETERS_INVALID_PARAMETER, + parameter.describe() + ); + return false; + } + return true; + } + + protected static boolean hasValidStrategyForProperties(ConditionGem condition, ExecutableElement method, + List parameters, + FormattingMessager messager) { + for ( Parameter parameter : parameters ) { + if ( parameter.isSourceParameter() ) { + // source parameter is a valid parameter for a property condition check + continue; + } + + if ( parameter.isMappingContext() ) { + // mapping context parameter is a valid parameter for a property condition check + continue; + } + + if ( parameter.isTargetType() ) { + // target type parameter is a valid parameter for a property condition check + continue; + } + + if ( parameter.isMappingTarget() ) { + // mapping target parameter is a valid parameter for a property condition check + continue; + } + + if ( parameter.isSourcePropertyName() ) { + // source property name parameter is a valid parameter for a property condition check + continue; + } + + if ( parameter.isTargetPropertyName() ) { + // target property name parameter is a valid parameter for a property condition check + continue; + } + + messager.printMessage( + method, + condition.mirror(), + Message.CONDITION_PROPERTIES_INVALID_PARAMETER, + parameter + ); + return false; + } + return true; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index 0c60f41364..ad2882080a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -90,14 +90,6 @@ public interface Method { */ boolean isObjectFactory(); - /** - * Returns whether the method is designated as a presence check method - * @return {@code true} if it is a presence check method - */ - default boolean isPresenceCheck() { - return false; - } - /** * Returns the parameter designated as target type (if present) {@link org.mapstruct.TargetType } * @@ -187,6 +179,10 @@ default boolean isPresenceCheck() { */ MappingMethodOptions getOptions(); + default ConditionMethodOptions getConditionOptions() { + return ConditionMethodOptions.empty(); + } + /** * * @return true when @MappingTarget annotated parameter is the same type as the return type. The method has diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 42c318ca49..7103fb2858 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -15,7 +15,6 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; -import org.mapstruct.ap.internal.gem.ConditionGem; import org.mapstruct.ap.internal.gem.ObjectFactoryGem; import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.Parameter; @@ -50,11 +49,11 @@ public class SourceMethod implements Method { private final Parameter sourcePropertyNameParameter; private final Parameter targetPropertyNameParameter; private final boolean isObjectFactory; - private final boolean isPresenceCheck; private final Type returnType; private final Accessibility accessibility; private final List exceptionTypes; private final MappingMethodOptions mappingMethodOptions; + private final ConditionMethodOptions conditionMethodOptions; private final List prototypeMethods; private final Type mapperToImplement; @@ -95,6 +94,7 @@ public static class Builder { private List valueMappings; private EnumMappingOptions enumMappingOptions; private ParameterProvidedMethods contextProvidedMethods; + private Set conditionOptions; private List typeParameters; private Set subclassMappings; @@ -196,6 +196,11 @@ public Builder setContextProvidedMethods(ParameterProvidedMethods contextProvide return this; } + public Builder setConditionOptions(Set conditionOptions) { + this.conditionOptions = conditionOptions; + return this; + } + public Builder setVerboseLogging(boolean verboseLogging) { this.verboseLogging = verboseLogging; return this; @@ -223,17 +228,22 @@ public SourceMethod build() { subclassValidator ); + ConditionMethodOptions conditionMethodOptions = + conditionOptions != null ? new ConditionMethodOptions( conditionOptions ) : + ConditionMethodOptions.empty(); + this.typeParameters = this.executable.getTypeParameters() .stream() .map( Element::asType ) .map( typeFactory::getType ) .collect( Collectors.toList() ); - return new SourceMethod( this, mappingMethodOptions ); + return new SourceMethod( this, mappingMethodOptions, conditionMethodOptions ); } } - private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) { + private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions, + ConditionMethodOptions conditionMethodOptions) { this.declaringMapper = builder.declaringMapper; this.executable = builder.executable; this.parameters = builder.parameters; @@ -242,6 +252,7 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) this.accessibility = Accessibility.fromModifiers( builder.executable.getModifiers() ); this.mappingMethodOptions = mappingMethodOptions; + this.conditionMethodOptions = conditionMethodOptions; this.sourceParameters = Parameter.getSourceParameters( parameters ); this.contextParameters = Parameter.getContextParameters( parameters ); @@ -254,7 +265,6 @@ private SourceMethod(Builder builder, MappingMethodOptions mappingMethodOptions) this.targetPropertyNameParameter = Parameter.getTargetPropertyNameParameter( parameters ); this.hasObjectFactoryAnnotation = ObjectFactoryGem.instanceOn( executable ) != null; this.isObjectFactory = determineIfIsObjectFactory(); - this.isPresenceCheck = determineIfIsPresenceCheck(); this.typeUtils = builder.typeUtils; this.typeFactory = builder.typeFactory; @@ -274,19 +284,6 @@ private boolean determineIfIsObjectFactory() { && ( hasObjectFactoryAnnotation || hasNoSourceParameters ); } - private boolean determineIfIsPresenceCheck() { - if ( returnType.isPrimitive() ) { - if ( !returnType.getName().equals( "boolean" ) ) { - return false; - } - } - else if ( !returnType.getFullyQualifiedName().equals( Boolean.class.getCanonicalName() ) ) { - return false; - } - - return ConditionGem.instanceOn( executable ) != null; - } - @Override public Type getDeclaringMapper() { return declaringMapper; @@ -547,6 +544,11 @@ public MappingMethodOptions getOptions() { return mappingMethodOptions; } + @Override + public ConditionMethodOptions getConditionOptions() { + return conditionMethodOptions; + } + @Override public boolean isStatic() { return executable.getModifiers().contains( Modifier.STATIC ); @@ -567,11 +569,6 @@ public boolean isLifecycleCallbackMethod() { return Executables.isLifecycleCallbackMethod( getExecutable() ); } - @Override - public boolean isPresenceCheck() { - return isPresenceCheck; - } - public boolean isAfterMappingMethod() { return Executables.isAfterMappingMethod( getExecutable() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java index 03a671de57..b6e5ca0a53 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/CreateOrUpdateSelector.java @@ -32,6 +32,7 @@ public List> getMatchingMethods(List> result = new ArrayList<>( methods.size() ); for ( SelectedMethod method : methods ) { - if ( method.getMethod().isObjectFactory() == criteria.isObjectFactoryRequired() + if ( criteria.isPresenceCheckRequired() ) { + if ( method.getMethod() + .getConditionOptions() + .isStrategyApplicable( ConditionStrategyGem.PROPERTIES ) ) { + result.add( method ); + } + } + else if ( criteria.isSourceParameterCheckRequired() ) { + if ( method.getMethod() + .getConditionOptions() + .isStrategyApplicable( ConditionStrategyGem.SOURCE_PARAMETERS ) ) { + result.add( method ); + } + } + else if ( method.getMethod().isObjectFactory() == criteria.isObjectFactoryRequired() && method.getMethod().isLifecycleCallbackMethod() == criteria.isLifecycleCallbackRequired() - && method.getMethod().isPresenceCheck() == criteria.isPresenceCheckRequired() ) { result.add( method ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java index 800278a4c4..84bd04d7db 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java @@ -163,6 +163,45 @@ public static SelectionContext forPresenceCheckMethods(Method mappingMethod, ); } + public static SelectionContext forSourceParameterPresenceCheckMethods(Method mappingMethod, + SelectionParameters selectionParameters, + Parameter sourceParameter, + TypeFactory typeFactory) { + SelectionCriteria criteria = SelectionCriteria.forSourceParameterCheckMethods( selectionParameters ); + Type booleanType = typeFactory.getType( Boolean.class ); + return new SelectionContext( + null, + criteria, + mappingMethod, + booleanType, + booleanType, + () -> getParameterBindingsForSourceParameterPresenceCheck( + mappingMethod, + booleanType, + sourceParameter, + typeFactory + ) + ); + } + + private static List getParameterBindingsForSourceParameterPresenceCheck(Method method, + Type targetType, + Parameter sourceParameter, + TypeFactory typeFactory) { + + List availableParams = new ArrayList<>( method.getParameters().size() + 3 ); + + availableParams.add( ParameterBinding.fromParameter( sourceParameter ) ); + availableParams.add( ParameterBinding.forTargetTypeBinding( typeFactory.classTypeOf( targetType ) ) ); + for ( Parameter parameter : method.getParameters() ) { + if ( !parameter.isSourceParameter( ) ) { + availableParams.add( ParameterBinding.fromParameter( parameter ) ); + } + } + + return availableParams; + } + private static List getAvailableParameterBindingsFromMethod(Method method, Type targetType, SourceRHS sourceRHS, TypeFactory typeFactory) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java index 2d288dd56e..fa5e1c29c0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java @@ -97,6 +97,13 @@ public boolean isPresenceCheckRequired() { return type == Type.PRESENCE_CHECK; } + /** + * @return {@code true} if source parameter check methods should be selected, {@code false} otherwise + */ + public boolean isSourceParameterCheckRequired() { + return type == Type.SOURCE_PARAMETER_CHECK; + } + public void setIgnoreQualifiers(boolean ignoreQualifiers) { this.ignoreQualifiers = ignoreQualifiers; } @@ -177,6 +184,10 @@ public static SelectionCriteria forPresenceCheckMethods(SelectionParameters sele return new SelectionCriteria( selectionParameters, null, null, Type.PRESENCE_CHECK ); } + public static SelectionCriteria forSourceParameterCheckMethods(SelectionParameters selectionParameters) { + return new SelectionCriteria( selectionParameters, null, null, Type.SOURCE_PARAMETER_CHECK ); + } + public static SelectionCriteria forSubclassMappingMethods(SelectionParameters selectionParameters, MappingControl mappingControl) { return new SelectionCriteria( selectionParameters, mappingControl, null, Type.SELF_NOT_ALLOWED ); @@ -187,6 +198,7 @@ public enum Type { OBJECT_FACTORY, LIFECYCLE_CALLBACK, PRESENCE_CHECK, + SOURCE_PARAMETER_CHECK, SELF_NOT_ALLOWED, } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index a9099ea179..4bd2ed48a5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -36,6 +36,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.BeanMappingOptions; +import org.mapstruct.ap.internal.model.source.ConditionOptions; import org.mapstruct.ap.internal.model.source.EnumMappingOptions; import org.mapstruct.ap.internal.model.source.IterableMappingOptions; import org.mapstruct.ap.internal.model.source.MapMappingOptions; @@ -53,6 +54,7 @@ import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.MetaAnnotations; import org.mapstruct.ap.internal.util.RepeatableAnnotations; import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.spi.EnumTransformationStrategy; @@ -73,6 +75,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor contextProvidedMethods = new ArrayList<>( contextParamMethods.size() ); for ( SourceMethod sourceMethod : contextParamMethods ) { if ( sourceMethod.isLifecycleCallbackMethod() || sourceMethod.isObjectFactory() - || sourceMethod.isPresenceCheck() ) { + || sourceMethod.getConditionOptions().isAnyStrategyApplicable() ) { contextProvidedMethods.add( sourceMethod ); } } @@ -393,6 +396,7 @@ private SourceMethod getReferencedMethod(TypeElement usedMapper, ExecutableType .setExceptionTypes( exceptionTypes ) .setTypeUtils( typeUtils ) .setTypeFactory( typeFactory ) + .setConditionOptions( getConditionOptions( method, parameters ) ) .setVerboseLogging( options.isVerbose() ) .build(); } @@ -633,6 +637,18 @@ private Set getSubclassMappings(List sourcePa .getProcessedAnnotations( method ); } + /** + * Retrieves the conditions configured via {@code @Condition} from the given method. + * + * @param method The method of interest + * @param parameters + * @return The condition options for the given method + */ + + private Set getConditionOptions(ExecutableElement method, List parameters) { + return new MetaConditions( parameters ).getProcessedAnnotations( method ); + } + private class RepeatableMappings extends RepeatableAnnotations { private BeanMappingOptions beanMappingOptions; @@ -774,4 +790,32 @@ protected void addInstances(ValueMappingsGem gems, Element source, Set { + + protected final List parameters; + + protected MetaConditions(List parameters) { + super( elementUtils, CONDITION_FQN ); + this.parameters = parameters; + } + + @Override + protected ConditionGem instanceOn(Element element) { + return ConditionGem.instanceOn( element ); + } + + @Override + protected void addInstance(ConditionGem gem, Element source, Set values) { + ConditionOptions options = ConditionOptions.getInstanceOn( + gem, + (ExecutableElement) source, + parameters, + messager + ); + if ( options != null ) { + values.add( options ); + } + } + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index e6a26ba045..d84ba974db 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -470,7 +470,7 @@ private ConversionAssignment resolveViaConversion(Type sourceType, Type targetTy } private boolean isCandidateForMapping(Method methodCandidate) { - if ( methodCandidate.isPresenceCheck() ) { + if ( methodCandidate.getConditionOptions().isAnyStrategyApplicable() ) { return false; } return isCreateMethodForMapping( methodCandidate ) || isUpdateMethodForMapping( methodCandidate ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 68c079cb96..5887ee07f0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -46,6 +46,10 @@ public enum Message { BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON( "\"%s\" is no property of the method return type." ), BEANMAPPING_IGNORE_BY_DEFAULT_WITH_MAPPING_TARGET_THIS( "Using @BeanMapping( ignoreByDefault = true ) with @Mapping( target = \".\", ... ) is not allowed. You'll need to explicitly ignore the target properties that should be ignored instead." ), + CONDITION_MISSING_APPLIES_TO_STRATEGY("'appliesTo' has to have at least one value in @Condition" ), + CONDITION_SOURCE_PARAMETERS_INVALID_PARAMETER("Parameter \"%s\" cannot be used with the ConditionStrategy#SOURCE_PARAMETERS. Only source and @Context parameters are allowed for conditions applicable to source parameters." ), + CONDITION_PROPERTIES_INVALID_PARAMETER("Parameter \"%s\" cannot be used with the ConditionStrategy#PROPERTIES. Only source, @Context, @MappingTarget, @TargetType, @TargetPropertyName and @SourcePropertyName parameters are allowed for conditions applicable to properties." ), + PROPERTYMAPPING_MAPPING_NOTE( "mapping property: %s to: %s.", Diagnostic.Kind.NOTE ), PROPERTYMAPPING_CREATE_NOTE( "creating property mapping: %s.", Diagnostic.Kind.NOTE ), PROPERTYMAPPING_SELECT_NOTE( "selecting property mapping: %s.", Diagnostic.Kind.NOTE ), @@ -143,6 +147,7 @@ public enum Message { GENERAL_AMBIGUOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), GENERAL_AMBIGUOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), GENERAL_AMBIGUOUS_PRESENCE_CHECK_METHOD( "Ambiguous presence check methods found for checking %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), + GENERAL_AMBIGUOUS_SOURCE_PARAMETER_CHECK_METHOD( "Ambiguous source parameter check methods found for checking %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ), GENERAL_AMBIGUOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s: %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ), GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ), GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/MetaAnnotations.java b/processor/src/main/java/org/mapstruct/ap/internal/util/MetaAnnotations.java new file mode 100644 index 0000000000..f2328a91cc --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/MetaAnnotations.java @@ -0,0 +1,85 @@ +/* + * 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.internal.util; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; + +import org.mapstruct.tools.gem.Gem; + +/** + * @author Filip Hrisafov + */ +public abstract class MetaAnnotations { + + private static final String JAVA_LANG_ANNOTATION_PGK = "java.lang.annotation"; + + private final ElementUtils elementUtils; + private final String annotationFqn; + + protected MetaAnnotations(ElementUtils elementUtils, String annotationFqn) { + this.elementUtils = elementUtils; + this.annotationFqn = annotationFqn; + } + + /** + * Retrieves the processed annotations. + * + * @param source The source element of interest + * @return The processed annotations for the given element + */ + public Set getProcessedAnnotations(Element source) { + return getValues( source, source, new LinkedHashSet<>(), new HashSet<>() ); + } + + protected abstract G instanceOn(Element element); + + protected abstract void addInstance(G gem, Element source, Set values); + + /** + * Retrieves the processed annotations. + * + * @param source The source element of interest + * @param element Element of interest: method, or (meta) annotation + * @param values the set of values found so far + * @param handledElements The collection of already handled elements to handle recursion correctly. + * @return The processed annotations for the given element + */ + private Set getValues(Element source, Element element, Set values, Set handledElements) { + for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { + Element annotationElement = annotationMirror.getAnnotationType().asElement(); + if ( isAnnotation( annotationElement, annotationFqn ) ) { + G gem = instanceOn( element ); + addInstance( gem, source, values ); + } + else if ( isNotJavaAnnotation( element ) && !handledElements.contains( annotationElement ) ) { + handledElements.add( annotationElement ); + getValues( source, annotationElement, values, handledElements ); + } + } + return values; + } + + private boolean isNotJavaAnnotation(Element element) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return !elementUtils.getPackageOf( element ).getQualifiedName().contentEquals( JAVA_LANG_ANNOTATION_PGK ); + } + return true; + } + + private boolean isAnnotation(Element element, String annotationFqn) { + if ( ElementKind.ANNOTATION_TYPE == element.getKind() ) { + return ( (TypeElement) element ).getQualifiedName().contentEquals( annotationFqn ); + } + + return false; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java index 57a68a9526..ae309988c8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMappingTest.java @@ -208,6 +208,84 @@ public void conditionalMethodForCollection() { .containsExactly( "Test", "Test Vol. 2" ); } + @ProcessorTest + @WithClasses({ + ConditionalMethodForSourceBeanMapper.class + }) + public void conditionalMethodForSourceBean() { + ConditionalMethodForSourceBeanMapper mapper = ConditionalMethodForSourceBeanMapper.INSTANCE; + + ConditionalMethodForSourceBeanMapper.Employee employee = mapper.map( + new ConditionalMethodForSourceBeanMapper.EmployeeDto( + "1", + "Tester" + ) ); + + assertThat( employee ).isNotNull(); + assertThat( employee.getId() ).isEqualTo( "1" ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + + employee = mapper.map( null ); + + assertThat( employee ).isNull(); + + employee = mapper.map( new ConditionalMethodForSourceBeanMapper.EmployeeDto( null, "Tester" ) ); + + assertThat( employee ).isNull(); + + employee = mapper.map( new ConditionalMethodForSourceBeanMapper.EmployeeDto( "test-123", "Tester" ) ); + + assertThat( employee ).isNotNull(); + assertThat( employee.getId() ).isEqualTo( "test-123" ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + } + + @ProcessorTest + @WithClasses({ + ConditionalMethodForSourceParameterAndPropertyMapper.class + }) + public void conditionalMethodForSourceParameterAndProperty() { + ConditionalMethodForSourceParameterAndPropertyMapper mapper = + ConditionalMethodForSourceParameterAndPropertyMapper.INSTANCE; + + ConditionalMethodForSourceParameterAndPropertyMapper.Employee employee = mapper.map( + new ConditionalMethodForSourceParameterAndPropertyMapper.EmployeeDto( + "1", + "Tester" + ) ); + + assertThat( employee ).isNotNull(); + assertThat( employee.getId() ).isEqualTo( "1" ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + assertThat( employee.getManager() ).isNull(); + + employee = mapper.map( null ); + + assertThat( employee ).isNull(); + + employee = mapper.map( new ConditionalMethodForSourceParameterAndPropertyMapper.EmployeeDto( + "1", + "Tester", + new ConditionalMethodForSourceParameterAndPropertyMapper.EmployeeDto( null, "Manager" ) + ) ); + + assertThat( employee ).isNotNull(); + assertThat( employee.getManager() ).isNull(); + + employee = mapper.map( new ConditionalMethodForSourceParameterAndPropertyMapper.EmployeeDto( + "1", + "Tester", + new ConditionalMethodForSourceParameterAndPropertyMapper.EmployeeDto( "2", "Manager" ) + ) ); + + assertThat( employee ).isNotNull(); + assertThat( employee.getId() ).isEqualTo( "1" ); + assertThat( employee.getName() ).isEqualTo( "Tester" ); + assertThat( employee.getManager() ).isNotNull(); + assertThat( employee.getManager().getId() ).isEqualTo( "2" ); + assertThat( employee.getManager().getName() ).isEqualTo( "Manager" ); + } + @ProcessorTest @WithClasses({ OptionalLikeConditionalMapper.class @@ -244,4 +322,124 @@ public void conditionalMethodWithMappingTarget() { assertThat( targetEmployee.getName() ).isEqualTo( "CurrentName" ); } + + @ProcessorTest + @WithClasses({ + ErroneousConditionalWithoutAppliesToMethodMapper.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousConditionalWithoutAppliesToMethodMapper.class, + line = 19, + message = "'appliesTo' has to have at least one value in @Condition" + ) + } + ) + public void emptyConditional() { + } + + @ProcessorTest + @WithClasses({ + ErroneousSourceParameterConditionalWithMappingTargetMapper.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousSourceParameterConditionalWithMappingTargetMapper.class, + line = 21, + message = "Parameter \"@MappingTarget BasicEmployee employee\"" + + " cannot be used with the ConditionStrategy#SOURCE_PARAMETERS." + + " Only source and @Context parameters are allowed for conditions applicable to source parameters." + ) + } + ) + public void sourceParameterConditionalWithMappingTarget() { + } + + @ProcessorTest + @WithClasses({ + ErroneousSourceParameterConditionalWithTargetTypeMapper.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousSourceParameterConditionalWithTargetTypeMapper.class, + line = 21, + message = "Parameter \"@TargetType Class targetClass\"" + + " cannot be used with the ConditionStrategy#SOURCE_PARAMETERS." + + " Only source and @Context parameters are allowed for conditions applicable to source parameters." + ) + } + ) + public void sourceParameterConditionalWithTargetType() { + } + + @ProcessorTest + @WithClasses({ + ErroneousSourceParameterConditionalWithTargetPropertyNameMapper.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousSourceParameterConditionalWithTargetPropertyNameMapper.class, + line = 21, + message = "Parameter \"@TargetPropertyName String targetProperty\"" + + " cannot be used with the ConditionStrategy#SOURCE_PARAMETERS." + + " Only source and @Context parameters are allowed for conditions applicable to source parameters." + ) + } + ) + public void sourceParameterConditionalWithTargetPropertyName() { + } + + @ProcessorTest + @WithClasses({ + ErroneousSourceParameterConditionalWithSourcePropertyNameMapper.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousSourceParameterConditionalWithSourcePropertyNameMapper.class, + line = 21, + message = "Parameter \"@SourcePropertyName String sourceProperty\"" + + " cannot be used with the ConditionStrategy#SOURCE_PARAMETERS." + + " Only source and @Context parameters are allowed for conditions applicable to source parameters." + ) + } + ) + public void sourceParametersConditionalWithSourcePropertyName() { + } + + @ProcessorTest + @WithClasses({ + ErroneousAmbiguousSourceParameterConditionalMethodMapper.class + }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousAmbiguousSourceParameterConditionalMethodMapper.class, + line = 17, + message = "Ambiguous source parameter check methods found for checking BasicEmployeeDto: " + + "boolean hasName(BasicEmployeeDto value), " + + "boolean hasStrategy(BasicEmployeeDto value). " + + "See https://mapstruct.org/faq/#ambiguous for more info." + ) + } + ) + public void ambiguousSourceParameterConditionalMethod() { + + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForSourceBeanMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForSourceBeanMapper.java new file mode 100644 index 0000000000..dd2fe4ac90 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForSourceBeanMapper.java @@ -0,0 +1,64 @@ +/* + * 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.basic; + +import org.mapstruct.Mapper; +import org.mapstruct.SourceParameterCondition; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConditionalMethodForSourceBeanMapper { + + ConditionalMethodForSourceBeanMapper INSTANCE = Mappers.getMapper( ConditionalMethodForSourceBeanMapper.class ); + + Employee map(EmployeeDto employee); + + @SourceParameterCondition + default boolean canMapEmployeeDto(EmployeeDto employee) { + return employee != null && employee.getId() != null; + } + + class EmployeeDto { + + private final String id; + private final String name; + + public EmployeeDto(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + } + + class Employee { + + private final String id; + private final String name; + + public Employee(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForSourceParameterAndPropertyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForSourceParameterAndPropertyMapper.java new file mode 100644 index 0000000000..11a97b7239 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForSourceParameterAndPropertyMapper.java @@ -0,0 +1,85 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.ConditionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ConditionalMethodForSourceParameterAndPropertyMapper { + + ConditionalMethodForSourceParameterAndPropertyMapper INSTANCE = Mappers.getMapper( + ConditionalMethodForSourceParameterAndPropertyMapper.class ); + + Employee map(EmployeeDto employee); + + @Condition(appliesTo = { + ConditionStrategy.SOURCE_PARAMETERS, + ConditionStrategy.PROPERTIES + }) + default boolean canMapEmployeeDto(EmployeeDto employee) { + return employee != null && employee.getId() != null; + } + + class EmployeeDto { + + private final String id; + private final String name; + private final EmployeeDto manager; + + public EmployeeDto(String id, String name) { + this( id, name, null ); + } + + public EmployeeDto(String id, String name, EmployeeDto manager) { + this.id = id; + this.name = name; + this.manager = manager; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public EmployeeDto getManager() { + return manager; + } + } + + class Employee { + + private final String id; + private final String name; + private final Employee manager; + + public Employee(String id, String name, Employee manager) { + this.id = id; + this.name = name; + this.manager = manager; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public Employee getManager() { + return manager; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousAmbiguousSourceParameterConditionalMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousAmbiguousSourceParameterConditionalMethodMapper.java new file mode 100644 index 0000000000..39433e2641 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousAmbiguousSourceParameterConditionalMethodMapper.java @@ -0,0 +1,28 @@ +/* + * 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.basic; + +import org.mapstruct.Mapper; +import org.mapstruct.SourceParameterCondition; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousAmbiguousSourceParameterConditionalMethodMapper { + + BasicEmployee map(BasicEmployeeDto employee); + + @SourceParameterCondition + default boolean hasName(BasicEmployeeDto value) { + return value != null && value.getName() != null; + } + + @SourceParameterCondition + default boolean hasStrategy(BasicEmployeeDto value) { + return value != null && value.getStrategy() != null; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousConditionalWithoutAppliesToMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousConditionalWithoutAppliesToMethodMapper.java new file mode 100644 index 0000000000..76f62845d5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousConditionalWithoutAppliesToMethodMapper.java @@ -0,0 +1,24 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousConditionalWithoutAppliesToMethodMapper { + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition(appliesTo = {}) + default boolean isNotBlank(String value) { + return value != null && !value.trim().isEmpty(); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithMappingTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithMappingTargetMapper.java new file mode 100644 index 0000000000..b1206798d6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithMappingTargetMapper.java @@ -0,0 +1,26 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.ConditionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousSourceParameterConditionalWithMappingTargetMapper { + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS) + default boolean isNotBlank(String value, @MappingTarget BasicEmployee employee) { + return value != null && !value.trim().isEmpty(); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithSourcePropertyNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithSourcePropertyNameMapper.java new file mode 100644 index 0000000000..1bfd150afc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithSourcePropertyNameMapper.java @@ -0,0 +1,26 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.ConditionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.SourcePropertyName; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousSourceParameterConditionalWithSourcePropertyNameMapper { + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS) + default boolean isNotBlank(String value, @SourcePropertyName String sourceProperty) { + return value != null && !value.trim().isEmpty(); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithTargetPropertyNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithTargetPropertyNameMapper.java new file mode 100644 index 0000000000..e9dac1ece5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithTargetPropertyNameMapper.java @@ -0,0 +1,26 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.ConditionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.TargetPropertyName; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousSourceParameterConditionalWithTargetPropertyNameMapper { + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS) + default boolean isNotBlank(String value, @TargetPropertyName String targetProperty) { + return value != null && !value.trim().isEmpty(); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithTargetTypeMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithTargetTypeMapper.java new file mode 100644 index 0000000000..9ff55597ae --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ErroneousSourceParameterConditionalWithTargetTypeMapper.java @@ -0,0 +1,26 @@ +/* + * 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.basic; + +import org.mapstruct.Condition; +import org.mapstruct.ConditionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.TargetType; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface ErroneousSourceParameterConditionalWithTargetTypeMapper { + + BasicEmployee map(BasicEmployeeDto employee); + + @Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS) + default boolean isNotBlank(String value, @TargetType Class targetClass) { + return value != null && !value.trim().isEmpty(); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java b/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java index 0c92fc6bbe..f460180651 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/gem/EnumGemsTest.java @@ -11,12 +11,14 @@ import org.junit.jupiter.api.Test; import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.ConditionStrategy; import org.mapstruct.InjectionStrategy; import org.mapstruct.MappingInheritanceStrategy; import org.mapstruct.NullValueCheckStrategy; import org.mapstruct.NullValueMappingStrategy; import org.mapstruct.ReportingPolicy; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; +import org.mapstruct.ap.internal.gem.ConditionStrategyGem; import org.mapstruct.ap.internal.gem.InjectionStrategyGem; import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; @@ -67,6 +69,12 @@ public void injectionStrategyGemIsCorrect() { namesOf( InjectionStrategyGem.values() ) ); } + @Test + public void conditionStrategyGemIsCorrect() { + assertThat( namesOf( ConditionStrategy.values() ) ).isEqualTo( + namesOf( ConditionStrategyGem.values() ) ); + } + private static List namesOf(Enum[] values) { return Stream.of( values ) .map( Enum::name ) From b33942a0104af4b72554858ae2a21934ca759bc5 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 29 Apr 2024 08:13:46 +0200 Subject: [PATCH 411/597] #3561 Add test case --- .../ap/test/bugs/_3561/Issue3561Mapper.java | 60 +++++++++++++++++++ .../ap/test/bugs/_3561/Issue3561Test.java | 35 +++++++++++ 2 files changed, 95 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3561/Issue3561Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3561/Issue3561Test.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3561/Issue3561Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3561/Issue3561Mapper.java new file mode 100644 index 0000000000..fd1137137b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3561/Issue3561Mapper.java @@ -0,0 +1,60 @@ +/* + * 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._3561; + +import org.mapstruct.Condition; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue3561Mapper { + + Issue3561Mapper INSTANCE = Mappers.getMapper( Issue3561Mapper.class ); + + @Mapping(target = "value", conditionQualifiedByName = "shouldMapValue") + Target map(Source source, @Context boolean shouldMapValue); + + @Condition + @Named("shouldMapValue") + default boolean shouldMapValue(@Context boolean shouldMapValue) { + return shouldMapValue; + } + + class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Source { + + private String value; + private boolean valueInitialized; + + public String getValue() { + if ( valueInitialized ) { + return value; + } + + throw new IllegalStateException( "value is not initialized" ); + } + + public void setValue(String value) { + this.valueInitialized = true; + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3561/Issue3561Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3561/Issue3561Test.java new file mode 100644 index 0000000000..7cd0c26694 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3561/Issue3561Test.java @@ -0,0 +1,35 @@ +/* + * 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._3561; + +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; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * @author Filip Hrisafov + */ +@WithClasses(Issue3561Mapper.class) +@IssueKey("3561") +class Issue3561Test { + + @ProcessorTest + void shouldCorrectlyUseConditionWithContext() { + + Issue3561Mapper.Source source = new Issue3561Mapper.Source(); + + assertThatThrownBy( () -> Issue3561Mapper.INSTANCE.map( source, true ) ) + .isInstanceOf( IllegalStateException.class ) + .hasMessage( "value is not initialized" ); + + Issue3561Mapper.Target target = Issue3561Mapper.INSTANCE.map( source, false ); + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isNull(); + } +} From 9a5e6b1892036d6485fe29e2c45bc68314d1300b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 11 May 2024 08:27:20 +0200 Subject: [PATCH 412/597] #3602 Automate release with JReleaser Add JReleaser for automating the release and add a step for automating the publishing of the website --- .github/scripts/update-website.sh | 76 ++++++++++++++++ .github/workflows/release.yml | 123 ++++++++++++++++++++++++++ NEXT_RELEASE_CHANGELOG.md | 29 ++++++ parent/pom.xml | 142 ++++++++++++++++++++++-------- pom.xml | 13 ++- 5 files changed, 345 insertions(+), 38 deletions(-) create mode 100644 .github/scripts/update-website.sh create mode 100644 .github/workflows/release.yml create mode 100644 NEXT_RELEASE_CHANGELOG.md diff --git a/.github/scripts/update-website.sh b/.github/scripts/update-website.sh new file mode 100644 index 0000000000..8fa989a1dd --- /dev/null +++ b/.github/scripts/update-website.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# +# Copyright MapStruct Authors. +# +# Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 +# + +# env vars: +# VERSION +# GH_BOT_EMAIL + +# This script has been inspired by the JReleaser update-website.sh (https://github.com/jreleaser/jreleaser/blob/main/.github/scripts/update-website.sh) +set -e + +function computePlainVersion() { + echo $1 | sed 's/\([[:digit:]]*\)\.\([[:digit:]]*\)\.\([[:digit:]]*\).*/\1.\2.\3/' +} + +function computeMajorMinorVersion() { + echo $1 | sed 's/\([[:digit:]]*\)\.\([[:digit:]]*\).*/\1.\2/' +} + +function isStable() { + local PLAIN_VERSION=$(computePlainVersion $1) + if [ "${PLAIN_VERSION}" == "$1" ]; then + echo "yes" + else + echo "no" + fi +} + +STABLE=$(isStable $VERSION) +MAJOR_MINOR_VERSION=$(computeMajorMinorVersion $VERSION) + +DEV_VERSION=`grep devVersion config.toml | sed 's/.*"\(.*\)"/\1/'` +MAJOR_MINOR_DEV_VERSION=$(computeMajorMinorVersion $DEV_VERSION) +STABLE_VERSION=`grep stableVersion config.toml | sed 's/.*"\(.*\)"/\1/'` +MAJOR_MINOR_STABLE_VERSION=$(computeMajorMinorVersion $STABLE_VERSION) + +echo "📝 Updating versions" +sed -i '' -e "s/^devVersion = \"\(.*\)\"/devVersion = \"${VERSION}\"/g" config.toml + +if [ "${STABLE}" == "yes" ]; then + sed -i '' -e "s/^stableVersion = \"\(.*\)\"/stableVersion = \"${VERSION}\"/g" config.toml + if [ "${MAJOR_MINOR_STABLE_VERSION}" != ${MAJOR_MINOR_VERSION} ]; then + echo "📝 Updating new stable version" + # This means that we have a new stable version and we need to change the order of the releases. + sed -i '' -e "s/^order = \(.*\)/order = 500/g" data/releases/${MAJOR_MINOR_VERSION}.toml + NEXT_STABLE_ORDER=$((`ls -1 data/releases | wc -l` - 2)) + sed -i '' -e "s/^order = \(.*\)/order = ${NEXT_STABLE_ORDER}/g" data/releases/${MAJOR_MINOR_STABLE_VERSION}.toml + git add data/releases/${MAJOR_MINOR_STABLE_VERSION}.toml + fi +elif [ "${MAJOR_MINOR_DEV_VERSION}" != "${MAJOR_MINOR_VERSION}" ]; then + echo "📝 Updating new dev version" + # This means that we are updating for a new dev version, but the last dev version is not the one that we are doing. + # Therefore, we need to update add the new data configuration + cp data/releases/${MAJOR_MINOR_DEV_VERSION}.toml data/releases/${MAJOR_MINOR_VERSION}.toml + sed -i '' -e "s/^order = \(.*\)/order = 1000/g" data/releases/${MAJOR_MINOR_VERSION}.toml +fi + +sed -i '' -e "s/^name = \"\(.*\)\"/name = \"${VERSION}\"/g" data/releases/${MAJOR_MINOR_VERSION}.toml +sed -i '' -e "s/^releaseDate = \(.*\)/releaseDate = $(date +%F)/g" data/releases/${MAJOR_MINOR_VERSION}.toml +git add data/releases/${MAJOR_MINOR_VERSION}.toml +git add config.toml + +echo "📝 Updating distribution resources" +tar -xf tmp/mapstruct-${VERSION}-dist.tar.gz --directory tmp +rm -rf static/documentation/${MAJOR_MINOR_VERSION} +cp -R tmp/mapstruct-${VERSION}/docs static/documentation/${MAJOR_MINOR_VERSION} +mv static/documentation/${MAJOR_MINOR_VERSION}/reference/html/mapstruct-reference-guide.html static/documentation/${MAJOR_MINOR_VERSION}/reference/html/index.html +git add static/documentation/${MAJOR_MINOR_VERSION} + +git config --global user.email "${GH_BOT_EMAIL}" +git config --global user.name "GitHub Action" +git commit -a -m "Releasing version ${VERSION}" +git push diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..5e4cddbeed --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,123 @@ +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version' + required: true + next: + description: 'Next version' + required: false + +env: + JAVA_VERSION: '11' + JAVA_DISTRO: 'zulu' + +jobs: + release: + # This job has been inspired by the moditect release (https://github.com/moditect/moditect/blob/main/.github/workflows/release.yml) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: ${{ vars.JAVA_VERSION }} + distribution: ${{ vars.JAVA_DISTRO }} + cache: maven + + - name: Set release version + id: version + run: | + RELEASE_VERSION=${{ github.event.inputs.version }} + NEXT_VERSION=${{ github.event.inputs.next }} + PLAIN_VERSION=`echo ${RELEASE_VERSION} | awk 'match($0, /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)/) { print substr($0, RSTART, RLENGTH); }'` + COMPUTED_NEXT_VERSION="${PLAIN_VERSION}-SNAPSHOT" + if [ -z $NEXT_VERSION ] + then + NEXT_VERSION=$COMPUTED_NEXT_VERSION + fi + ./mvnw -ntp -B versions:set versions:commit -DnewVersion=$RELEASE_VERSION -pl :mapstruct-parent -DgenerateBackupPoms=false + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --global user.name "GitHub Action" + git commit -a -m "Releasing version $RELEASE_VERSION" + git push + echo "RELEASE_VERSION=$RELEASE_VERSION" >> $GITHUB_ENV + echo "NEXT_VERSION=$NEXT_VERSION" >> $GITHUB_ENV + echo "PLAIN_VERSION=$PLAIN_VERSION" >> $GITHUB_ENV + + - name: Stage + run: | + export GPG_TTY=$(tty) + ./mvnw -ntp -B --file pom.xml \ + -Dmaven.site.skip=true -Drelease=true -Ppublication,stage + + - name: Release + env: + JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JRELEASER_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.GPG_PUBLIC_KEY }} + JRELEASER_GPG_SECRET_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + JRELEASER_NEXUS2_MAVEN_CENTRAL_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + JRELEASER_NEXUS2_MAVEN_CENTRAL_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + run: | + ./mvnw -ntp -B --file pom.xml -pl :mapstruct-parent -Pjreleaser jreleaser:release + + - name: JReleaser output + if: always() + uses: actions/upload-artifact@v4 + with: + name: jreleaser-release + path: | + parent/target/jreleaser/trace.log + parent/target/jreleaser/output.properties + + - name: Set next version + run: | + ./mvnw -ntp -B versions:set versions:commit -DnewVersion=${{ env.NEXT_VERSION }} -pl :mapstruct-parent -DgenerateBackupPoms=false + sed -i -e "s@project.build.outputTimestamp>.*\${git.commit.author.time} UTF-8 + + mapstruct/mapstruct + /tmp/repository + 1.8 + ${java.version} + ${java.version} + + ${git.commit.author.time} + 1.0.0.Alpha3 3.4.1 3.2.2 @@ -30,7 +39,7 @@ 8.36.1 5.10.1 2.2.0 - + 1.12.0 1 3.24.2 @@ -392,10 +401,6 @@ org.apache.maven.plugins maven-compiler-plugin 3.8.1 - - 1.8 - 1.8 - org.apache.maven.plugins @@ -410,11 +415,6 @@ true - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - org.apache.maven.plugins maven-enforcer-plugin @@ -461,21 +461,6 @@ 8 - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - -DskipTests ${add.release.arguments} - clean install - false - true - @{project.version} - true - false - release - - org.apache.maven.plugins maven-resources-plugin @@ -522,6 +507,11 @@ + + org.codehaus.mojo + versions-maven-plugin + 2.16.2 + org.eclipse.m2e lifecycle-mapping @@ -663,6 +653,7 @@ maven-settings.xml readme.md CONTRIBUTING.md + NEXT_RELEASE_CHANGELOG.md .gitattributes .gitignore .factorypath @@ -791,12 +782,21 @@ + + org.codehaus.mojo + versions-maven-plugin + - release + publication + + + release + + @@ -823,18 +823,86 @@ + + + + + stage + + local::file:${maven.multiModuleProjectDirectory}/target/staging-deploy + + + deploy + + + + jreleaser + + - org.apache.maven.plugins - maven-gpg-plugin - - - sign-artifacts - verify - - sign - - - + org.jreleaser + jreleaser-maven-plugin + ${jreleaser.plugin.version} + + true + + + Mapstruct + + https://mapstruct.org/ + https://mapstruct.org/documentation/stable/reference/html/ + + + + ALWAYS + true + + + false + + + + + + ALWAYS + https://oss.sonatype.org/service/local + https://oss.sonatype.org/content/repositories/snapshots/ + true + true + ${maven.multiModuleProjectDirectory}/target/staging-deploy + + + org.mapstruct + mapstruct-jdk8 + false + false + + + + + + + + + {{projectVersion}} + {{projectVersion}} + + ${maven.multiModuleProjectDirectory}/NEXT_RELEASE_CHANGELOG.md + + + + + + + ${maven.multiModuleProjectDirectory}/distribution/target/mapstruct-{{projectVersion}}-dist.tar.gz + + + ${maven.multiModuleProjectDirectory}/distribution/target/mapstruct-{{projectVersion}}-dist.zip + + + + + diff --git a/pom.xml b/pom.xml index 25a5ead6bc..90402f0cae 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,6 @@ core core-jdk8 processor - integrationtest true @@ -71,5 +70,17 @@ distribution + + test + + + release + !true + + + + integrationtest + + From 8e53b4181fb76d992a5902d70afa5136768d4a41 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 11 May 2024 08:47:02 +0200 Subject: [PATCH 413/597] #3602 Fix setup-java action for release workflow --- .github/workflows/release.yml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e4cddbeed..6dfd75a6f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,10 +10,6 @@ on: description: 'Next version' required: false -env: - JAVA_VERSION: '11' - JAVA_DISTRO: 'zulu' - jobs: release: # This job has been inspired by the moditect release (https://github.com/moditect/moditect/blob/main/.github/workflows/release.yml) @@ -26,8 +22,8 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - java-version: ${{ vars.JAVA_VERSION }} - distribution: ${{ vars.JAVA_DISTRO }} + java-version: 11 + distribution: 'zulu' cache: maven - name: Set release version @@ -99,12 +95,6 @@ jobs: fetch-depth: 0 token: ${{ secrets.GIT_WEBSITE_ACCESS_TOKEN }} - - name: Setup Java - uses: actions/setup-java@v4 - with: - java-version: ${{ vars.JAVA_VERSION }} - distribution: ${{ vars.JAVA_DISTRO }} - - name: Download assets shell: bash run: | From 21a8b88a0f3cab99673d0bf94f11e5f731f00b90 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 07:01:58 +0000 Subject: [PATCH 414/597] Releasing version 1.6.0.Beta2 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0bdec734cb..95be5f0b90 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta2 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 160f963adf..8830420981 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta2 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index ac993c73d6..7c8059144a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta2 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index afa8e31002..5c4a656d75 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta2 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 0a62a3ef3c..be0d7efe59 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta2 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index f888e226ff..3e0fa3ec09 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta2 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index e4109325f7..7acf7d5952 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta2 pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - ${git.commit.author.time} + 2024-05-11T07:01:58Z 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index 90402f0cae..ab4476277d 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta2 parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 12fc615f5f..7ce9548edf 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.Beta2 ../parent/pom.xml From 8a679b325d677ef93f7bda65c5a3da13b6aff524 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 07:10:47 +0000 Subject: [PATCH 415/597] Next version 1.6.0-SNAPSHOT --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 95be5f0b90..0bdec734cb 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta2 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 8830420981..160f963adf 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta2 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 7c8059144a..ac993c73d6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta2 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 5c4a656d75..afa8e31002 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta2 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index be0d7efe59..0a62a3ef3c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta2 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 3e0fa3ec09..f888e226ff 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta2 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 7acf7d5952..e4109325f7 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta2 + 1.6.0-SNAPSHOT pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - 2024-05-11T07:01:58Z + ${git.commit.author.time} 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index ab4476277d..90402f0cae 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta2 + 1.6.0-SNAPSHOT parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 7ce9548edf..12fc615f5f 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.Beta2 + 1.6.0-SNAPSHOT ../parent/pom.xml From baa02bf37725c2be8ca6ec8534170172c84583d9 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 11 May 2024 09:32:27 +0200 Subject: [PATCH 416/597] #3602 Fix path for update-website.sh scrip in release workflow --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6dfd75a6f2..493fd2ad0c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,5 +109,5 @@ jobs: VERSION: ${{ github.event.inputs.version }} GH_BOT_EMAIL: ${{ vars.GH_BOT_EMAIL }} run: | - chmod +x update-website.sh - ./update-website.sh + chmod +x tmp/update-website.sh + tmp/update-website.sh From babb9dedd9cf3c9c75aa7e4c96a7bd24c40ed927 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 30 Jun 2024 14:47:27 +0200 Subject: [PATCH 417/597] #3602 Doing a release should reset NEXT_RELEASE_CHANGELOG.md --- .github/workflows/release.yml | 4 ++++ NEXT_RELEASE_CHANGELOG.md | 19 ------------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 493fd2ad0c..36cabf44f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,6 +72,10 @@ jobs: parent/target/jreleaser/trace.log parent/target/jreleaser/output.properties + - name: Reset NEXT_RELEASE_CHANGELOG.md + run: | + echo -e "### Features\n\n### Enhancements\n\n### Bugs\n\n### Documentation\n\n### Build\n" > NEXT_RELEASE_CHANGELOG.md + - name: Set next version run: | ./mvnw -ntp -B versions:set versions:commit -DnewVersion=${{ env.NEXT_VERSION }} -pl :mapstruct-parent -DgenerateBackupPoms=false diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index ed3f0e9c56..e0f4cd31f0 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,29 +1,10 @@ ### Features -* Support conditional mapping for source parameters (#2610, #3459, #3270) -* Add `@SourcePropertyName` to handle a property name of the source object (#3323) - Currently only applicable for `@Condition` methods - ### Enhancements -* Improve error message for mapping to `target = "."` using expression (#3485) -* Improve error messages for auto generated mappings (#2788) -* Remove unnecessary casts to long (#3400) - ### Bugs -* `@Condition` cannot be used only with `@Context` parameters (#3561) -* `@Condition` treated as ambiguous mapping for methods returning Boolean/boolean (#3565) -* Subclass mapping warns about unmapped property that is mapped in referenced mapper (#3360) -* Interface inherited build method is not found (#3463) -* Bean with getter returning Stream is treating the Stream as an alternative setter (#3462) -* Using `Mapping#expression` and `Mapping#conditionalQualifiedBy(Name)` should lead to compile error (#3413) -* Defined mappings for subclass mappings with runtime exception subclass exhaustive strategy not working if result type is abstract class (#3331) - ### Documentation -* Clarify that `Mapping#ignoreByDefault` is inherited in nested mappings in documentation (#3577) - ### Build -* Improve tests to show that Lombok `@SuperBuilder` is supported (#3524) -* Add Java 21 CI matrix build (#3473) From 69371708ee9371bdaf4267529f45b9122df80c62 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 6 Jul 2024 10:31:32 +0200 Subject: [PATCH 418/597] #3574 Respect only explicit mappings but fail on unmapped source fields * #3574 Respect only explicit mappings but fail on unmapped source fields This reverts #2560, because we've decided that `@BeanMapping(ignoreByDefault = true)` should only be applied to target properties and not to source properties. Source properties are anyway ignored, the `BeanMapping#unmappedSourcePolicy` should be used to control what should happen with unmapped source policy --- NEXT_RELEASE_CHANGELOG.md | 4 ++++ .../ap/internal/model/BeanMappingMethod.java | 4 ---- ...neousSourceTargetMapperWithIgnoreByDefault.java} | 5 +++-- .../IgnoreByDefaultSourcesTest.java | 13 +++++++++++-- 4 files changed, 18 insertions(+), 8 deletions(-) rename processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/{SourceTargetMapper.java => ErroneousSourceTargetMapperWithIgnoreByDefault.java} (74%) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index e0f4cd31f0..a6c424b08e 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -2,6 +2,10 @@ ### Enhancements +* Breaking change:g (#3574) - +This reverts #2560, because we've decided that `@BeanMapping(ignoreByDefault = true)` should only be applied to target properties and not to source properties. +Source properties are ignored anyway, the `BeanMapping#unmappedSourcePolicy` should be used to control what should happen with unmapped source policy + ### Bugs ### Documentation diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index cf5179f9cb..b437afadc0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1764,10 +1764,6 @@ private ReportingPolicyGem getUnmappedSourcePolicy() { if ( mappingReferences.isForForgedMethods() ) { return ReportingPolicyGem.IGNORE; } - // If we have ignoreByDefault = true, unprocessed source properties are not an issue. - if ( method.getOptions().getBeanMapping().isignoreByDefault() ) { - return ReportingPolicyGem.IGNORE; - } return method.getOptions().getBeanMapping().unmappedSourcePolicy(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/ErroneousSourceTargetMapperWithIgnoreByDefault.java similarity index 74% rename from processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/SourceTargetMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/ErroneousSourceTargetMapperWithIgnoreByDefault.java index e029bd7391..459e2f426a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/ErroneousSourceTargetMapperWithIgnoreByDefault.java @@ -14,8 +14,9 @@ @Mapper( unmappedTargetPolicy = ReportingPolicy.IGNORE, unmappedSourcePolicy = ReportingPolicy.ERROR) -public interface SourceTargetMapper { - SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); +public interface ErroneousSourceTargetMapperWithIgnoreByDefault { + ErroneousSourceTargetMapperWithIgnoreByDefault INSTANCE = Mappers.getMapper( + ErroneousSourceTargetMapperWithIgnoreByDefault.class ); @Mapping(source = "one", target = "one") @BeanMapping(ignoreByDefault = true) diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/IgnoreByDefaultSourcesTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/IgnoreByDefaultSourcesTest.java index 98f2cde4c0..fce6d49648 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/IgnoreByDefaultSourcesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/ignorebydefaultsource/IgnoreByDefaultSourcesTest.java @@ -18,8 +18,17 @@ public class IgnoreByDefaultSourcesTest { @ProcessorTest - @WithClasses({ SourceTargetMapper.class, Source.class, Target.class }) - public void shouldSucceed() { + @WithClasses({ ErroneousSourceTargetMapperWithIgnoreByDefault.class, Source.class, Target.class }) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousSourceTargetMapperWithIgnoreByDefault.class, + kind = Kind.ERROR, + line = 23, + message = "Unmapped source property: \"other\".") + } + ) + public void shouldRaiseErrorDueToNonIgnoredSourcePropertyWithBeanMappingIgnoreByDefault() { } @ProcessorTest From 037da5a1e1f450f670fa934640ee40ed67f0f781 Mon Sep 17 00:00:00 2001 From: Connor McGowan <53909969+cmcgowanprovidertrust@users.noreply.github.com> Date: Sun, 7 Jul 2024 14:19:38 -0500 Subject: [PATCH 419/597] #3635 Fix documentation of unmappedSourcePolicy default (#3637) --- documentation/src/main/asciidoc/chapter-2-set-up.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 666954c4b1..58759ff8e0 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -261,7 +261,7 @@ Supported values are: If a policy is given for a specific mapper via `@Mapper#unmappedSourcePolicy()`, the value from the annotation takes precedence. If a policy is given for a specific bean mapping via `@BeanMapping#ignoreUnmappedSourceProperties()`, it takes precedence over both `@Mapper#unmappedSourcePolicy()` and the option. -|`WARN` +|`IGNORE` |`mapstruct. disableBuilders` From 8fa2f40944b3a143a987a17448473eec2f78f076 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 15 Jul 2024 23:18:32 +0200 Subject: [PATCH 420/597] Enforce whitespaces around the for colon with CheckStyle (#3642) --- build-config/src/main/resources/build-config/checkstyle.xml | 4 +++- .../org/mapstruct/ap/internal/model/BeanMappingMethod.java | 2 +- .../ap/test/conversion/_enum/EnumToIntegerConversionTest.java | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build-config/src/main/resources/build-config/checkstyle.xml b/build-config/src/main/resources/build-config/checkstyle.xml index a1ff4af23a..71a7d35899 100644 --- a/build-config/src/main/resources/build-config/checkstyle.xml +++ b/build-config/src/main/resources/build-config/checkstyle.xml @@ -146,7 +146,9 @@ - + + + diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index b437afadc0..6535b07012 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -152,7 +152,7 @@ public Builder forgedMethod(ForgedMethod forgedMethod) { method( forgedMethod ); mappingReferences = forgedMethod.getMappingReferences(); Parameter sourceParameter = first( Parameter.getSourceParameters( forgedMethod.getParameters() ) ); - for ( MappingReference mappingReference: mappingReferences.getMappingReferences() ) { + for ( MappingReference mappingReference : mappingReferences.getMappingReferences() ) { SourceReference sourceReference = mappingReference.getSourceReference(); if ( sourceReference != null ) { mappingReference.setSourceReference( new SourceReference.BuilderFromSourceReference() diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerConversionTest.java index 1302c22bc9..67d99dfbc6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/EnumToIntegerConversionTest.java @@ -30,7 +30,7 @@ public class EnumToIntegerConversionTest { public void shouldApplyEnumToIntegerConversion() { EnumToIntegerSource source = new EnumToIntegerSource(); - for ( EnumToIntegerEnum value: EnumToIntegerEnum.values() ) { + for ( EnumToIntegerEnum value : EnumToIntegerEnum.values() ) { source.setEnumValue( value ); EnumToIntegerTarget target = EnumToIntegerMapper.INSTANCE.sourceToTarget( source ); From eef3bdfca4037735507ef3776568b49201ca4906 Mon Sep 17 00:00:00 2001 From: thunderhook <8238759+thunderhook@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:54:06 +0200 Subject: [PATCH 421/597] #3639 fix documentation link --- .../src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 2174822359..ed64580eb4 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -448,7 +448,7 @@ E.g. If an object factory exists for our `PersonBuilder` then this factory would [NOTE] ====== -Detected builders influence `@BeforeMapping` and `@AfterMapping` behavior. See chapter `Mapping customization with before-mapping and after-mapping methods` for more information. +Detected builders influence `@BeforeMapping` and `@AfterMapping` behavior. See <> for more information. ====== .Person with Builder example From 52877d36c2e227e639fbca43d268174f4fa68bc6 Mon Sep 17 00:00:00 2001 From: thunderhook <8238759+thunderhook@users.noreply.github.com> Date: Sun, 7 Jul 2024 13:49:52 +0200 Subject: [PATCH 422/597] #3634 fix typo in experimental note --- .../java/org/mapstruct/ap/spi/EnumMappingStrategy.java | 2 +- .../org/mapstruct/ap/spi/EnumTransformationStrategy.java | 2 +- .../org/mapstruct/ap/spi/MappingExclusionProvider.java | 9 ++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/spi/EnumMappingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/EnumMappingStrategy.java index 97088cd8c6..8ad6737c63 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/EnumMappingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/EnumMappingStrategy.java @@ -17,7 +17,7 @@ * * @since 1.4 */ -@Experimental("This SPI can have it's signature changed in subsequent releases") +@Experimental("This SPI can have its signature changed in subsequent releases") public interface EnumMappingStrategy { /** diff --git a/processor/src/main/java/org/mapstruct/ap/spi/EnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/EnumTransformationStrategy.java index 2c498fc3a8..796bdb4954 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/EnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/EnumTransformationStrategy.java @@ -13,7 +13,7 @@ * @author Filip Hrisafov * @since 1.4 */ -@Experimental("This SPI can have it's signature changed in subsequent releases") +@Experimental("This SPI can have its signature changed in subsequent releases") public interface EnumTransformationStrategy { /** diff --git a/processor/src/main/java/org/mapstruct/ap/spi/MappingExclusionProvider.java b/processor/src/main/java/org/mapstruct/ap/spi/MappingExclusionProvider.java index afd7e69511..9dae81cda2 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/MappingExclusionProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/MappingExclusionProvider.java @@ -12,10 +12,10 @@ /** * A service provider interface that is used to control if MapStruct is allowed to generate automatic sub-mapping for * a given {@link TypeElement}. - * + *

      * When generating the implementation of a mapping method, MapStruct will apply the following routine for each * attribute pair in the source and target object: - * + *

      *

        *
      • If source and target attribute have the same type, the value will be simply copied from source to target. * If the attribute is a collection (e.g. a `List`) a copy of the collection will be set into the target @@ -30,14 +30,14 @@ *
      • If MapStruct could not create a name based mapping method an error will be raised at build time, * indicating the non-mappable attribute and its path.
      • *
      - * + *

      * With this SPI the last step before raising an error can be controlled. i.e. A user can control whether MapStruct * is allowed to generate such automatic sub-mapping method (for the source or target type) or not. * * @author Filip Hrisafov * @since 1.2 */ -@Experimental("This SPI can have it's signature changed in subsequent releases") +@Experimental("This SPI can have its signature changed in subsequent releases") public interface MappingExclusionProvider { /** @@ -46,7 +46,6 @@ public interface MappingExclusionProvider { * The given {@code typeElement} will be excluded from the automatic sub-mapping generation * * @param typeElement that needs to be checked - * * @return {@code true} if MapStruct should exclude the provided {@link TypeElement} from an automatic sub-mapping */ boolean isExcluded(TypeElement typeElement); From 66f4288842460f27b88ba4580cd5384551127d61 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 20 Jul 2024 13:53:39 +0200 Subject: [PATCH 423/597] #3601 Always use SourceParameterCondition when checking source parameter This is a breaking change, with this change whenever a source parameter is used as a source for a target property the condition has to apply to source parameters and not properties --- NEXT_RELEASE_CHANGELOG.md | 68 +++++++++ .../source/selector/SelectionCriteria.java | 129 ++++++++++++------ .../ap/test/bugs/_3601/Issue3601Mapper.java | 50 +++++++ .../ap/test/bugs/_3601/Issue3601Test.java | 47 +++++++ ...itionalMethodWithSourceToTargetMapper.java | 6 +- 5 files changed, 252 insertions(+), 48 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3601/Issue3601Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3601/Issue3601Test.java diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index a6c424b08e..305bdda947 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -8,7 +8,75 @@ Source properties are ignored anyway, the `BeanMapping#unmappedSourcePolicy` sho ### Bugs +* Breaking change: Presence check method used only once when multiple source parameters are provided (#3601) + ### Documentation ### Build +## Breaking changes + +### Presence checks for source parameters + +In 1.6, support for presence checks on source parameters has been added. +This means that even if you want to map a source parameter directly to some target property the new `@SourceParameterCondition` or `@Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS)` should be used. + +e.g. + +If we had the following in 1.5: +```java +@Mapper +public interface OrderMapper { + + @Mapping(source = "dto", target = "customer", conditionQualifiedByName = "mapCustomerFromOrder") + Order map(OrderDTO dto); + + @Condition + @Named("mapCustomerFromOrder") + default boolean mapCustomerFromOrder(OrderDTO dto) { + return dto != null && dto.getCustomerName() != null; + } + +} +``` + +Them MapStruct would generate + +```java +public class OrderMapperImpl implements OrderMapper { + + @Override + public Order map(OrderDTO dto) { + if ( dto == null ) { + return null; + } + + Order order = new Order(); + + if ( mapCustomerFromOrder( dto ) ) { + order.setCustomer( orderDtoToCustomer( orderDTO ) ); + } + + return order; + } +} +``` + +In order for the same to be generated in 1.6, the mapper needs to look like this: + +```java +@Mapper +public interface OrderMapper { + + @Mapping(source = "dto", target = "customer", conditionQualifiedByName = "mapCustomerFromOrder") + Order map(OrderDTO dto); + + @SourceParameterCondition + @Named("mapCustomerFromOrder") + default boolean mapCustomerFromOrder(OrderDTO dto) { + return dto != null && dto.getCustomerName() != null; + } + +} +``` + diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java index fa5e1c29c0..ecc9ac9e01 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.internal.model.source.selector; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -22,50 +21,31 @@ */ public class SelectionCriteria { - private final List qualifiers = new ArrayList<>(); - private final List qualifiedByNames = new ArrayList<>(); + private final QualifyingInfo qualifyingInfo; private final String targetPropertyName; - private final TypeMirror qualifyingResultType; private final SourceRHS sourceRHS; private boolean ignoreQualifiers = false; private Type type; - private final boolean allowDirect; - private final boolean allowConversion; - private final boolean allowMappingMethod; - private final boolean allow2Steps; + private final MappingControl mappingControl; public SelectionCriteria(SelectionParameters selectionParameters, MappingControl mappingControl, String targetPropertyName, Type type) { - if ( selectionParameters != null ) { - if ( type == Type.PRESENCE_CHECK ) { - qualifiers.addAll( selectionParameters.getConditionQualifiers() ); - qualifiedByNames.addAll( selectionParameters.getConditionQualifyingNames() ); - } - else { - qualifiers.addAll( selectionParameters.getQualifiers() ); - qualifiedByNames.addAll( selectionParameters.getQualifyingNames() ); - } - qualifyingResultType = selectionParameters.getResultType(); - sourceRHS = selectionParameters.getSourceRHS(); - } - else { - this.qualifyingResultType = null; - sourceRHS = null; - } - if ( mappingControl != null ) { - this.allowDirect = mappingControl.allowDirect(); - this.allowConversion = mappingControl.allowTypeConversion(); - this.allowMappingMethod = mappingControl.allowMappingMethod(); - this.allow2Steps = mappingControl.allowBy2Steps(); - } - else { - this.allowDirect = true; - this.allowConversion = true; - this.allowMappingMethod = true; - this.allow2Steps = true; - } + this( + QualifyingInfo.fromSelectionParameters( selectionParameters ), + selectionParameters != null ? selectionParameters.getSourceRHS() : null, + mappingControl, + targetPropertyName, + type + ); + } + + private SelectionCriteria(QualifyingInfo qualifyingInfo, SourceRHS sourceRHS, MappingControl mappingControl, + String targetPropertyName, Type type) { + this.qualifyingInfo = qualifyingInfo; this.targetPropertyName = targetPropertyName; + this.sourceRHS = sourceRHS; this.type = type; + this.mappingControl = mappingControl; } /** @@ -109,11 +89,11 @@ public void setIgnoreQualifiers(boolean ignoreQualifiers) { } public List getQualifiers() { - return ignoreQualifiers ? Collections.emptyList() : qualifiers; + return ignoreQualifiers ? Collections.emptyList() : qualifyingInfo.qualifiers(); } public List getQualifiedByNames() { - return ignoreQualifiers ? Collections.emptyList() : qualifiedByNames; + return ignoreQualifiers ? Collections.emptyList() : qualifyingInfo.qualifiedByNames(); } public String getTargetPropertyName() { @@ -121,7 +101,7 @@ public String getTargetPropertyName() { } public TypeMirror getQualifyingResultType() { - return qualifyingResultType; + return qualifyingInfo.qualifyingResultType(); } public boolean isPreferUpdateMapping() { @@ -137,23 +117,23 @@ public void setPreferUpdateMapping(boolean preferUpdateMapping) { } public boolean hasQualfiers() { - return !qualifiedByNames.isEmpty() || !qualifiers.isEmpty(); + return !qualifyingInfo.qualifiedByNames().isEmpty() || !qualifyingInfo.qualifiers().isEmpty(); } public boolean isAllowDirect() { - return allowDirect; + return mappingControl == null || mappingControl.allowDirect(); } public boolean isAllowConversion() { - return allowConversion; + return mappingControl == null || mappingControl.allowTypeConversion(); } public boolean isAllowMappingMethod() { - return allowMappingMethod; + return mappingControl == null || mappingControl.allowMappingMethod(); } public boolean isAllow2Steps() { - return allow2Steps; + return mappingControl == null || mappingControl.allowBy2Steps(); } public boolean isSelfAllowed() { @@ -181,7 +161,22 @@ public static SelectionCriteria forLifecycleMethods(SelectionParameters selectio } public static SelectionCriteria forPresenceCheckMethods(SelectionParameters selectionParameters) { - return new SelectionCriteria( selectionParameters, null, null, Type.PRESENCE_CHECK ); + SourceRHS sourceRHS = selectionParameters.getSourceRHS(); + Type type; + QualifyingInfo qualifyingInfo = new QualifyingInfo( + selectionParameters.getConditionQualifiers(), + selectionParameters.getConditionQualifyingNames(), + selectionParameters.getResultType() + ); + if ( sourceRHS != null && sourceRHS.isSourceReferenceParameter() ) { + // If the source reference is for a source parameter, + // then the presence check should be for the source parameter + type = Type.SOURCE_PARAMETER_CHECK; + } + else { + type = Type.PRESENCE_CHECK; + } + return new SelectionCriteria( qualifyingInfo, sourceRHS, null, null, type ); } public static SelectionCriteria forSourceParameterCheckMethods(SelectionParameters selectionParameters) { @@ -193,6 +188,50 @@ public static SelectionCriteria forSubclassMappingMethods(SelectionParameters se return new SelectionCriteria( selectionParameters, mappingControl, null, Type.SELF_NOT_ALLOWED ); } + private static class QualifyingInfo { + + private static final QualifyingInfo EMPTY = new QualifyingInfo( + Collections.emptyList(), + Collections.emptyList(), + null + ); + + private final List qualifiers; + private final List qualifiedByNames; + private final TypeMirror qualifyingResultType; + + private QualifyingInfo(List qualifiers, List qualifiedByNames, + TypeMirror qualifyingResultType) { + this.qualifiers = qualifiers; + this.qualifiedByNames = qualifiedByNames; + this.qualifyingResultType = qualifyingResultType; + } + + public List qualifiers() { + return qualifiers; + } + + public List qualifiedByNames() { + return qualifiedByNames; + } + + public TypeMirror qualifyingResultType() { + return qualifyingResultType; + } + + private static QualifyingInfo fromSelectionParameters(SelectionParameters selectionParameters) { + if ( selectionParameters == null ) { + return EMPTY; + } + return new QualifyingInfo( + selectionParameters.getQualifiers(), + selectionParameters.getQualifyingNames(), + selectionParameters.getResultType() + ); + } + } + + public enum Type { PREFER_UPDATE_MAPPING, OBJECT_FACTORY, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3601/Issue3601Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3601/Issue3601Mapper.java new file mode 100644 index 0000000000..851e353de7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3601/Issue3601Mapper.java @@ -0,0 +1,50 @@ +/* + * 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._3601; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SourceParameterCondition; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3601Mapper { + + Issue3601Mapper INSTANCE = Mappers.getMapper( Issue3601Mapper.class ); + + @Mapping(target = "currentId", source = "source.uuid") + @Mapping(target = "targetIds", source = "sourceIds") + Target map(Source source, List sourceIds); + + @SourceParameterCondition + default boolean isNotEmpty(List elements) { + return elements != null && !elements.isEmpty(); + } + + class Source { + private final String uuid; + + public Source(String uuid) { + this.uuid = uuid; + } + + public String getUuid() { + return uuid; + } + } + + class Target { + //CHECKSTYLE:OFF + public String currentId; + public List targetIds; + //CHECKSTYLE:ON + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3601/Issue3601Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3601/Issue3601Test.java new file mode 100644 index 0000000000..b9b68fb583 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3601/Issue3601Test.java @@ -0,0 +1,47 @@ +/* + * 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._3601; + +import java.util.Collections; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@IssueKey("3601") +class Issue3601Test { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses( Issue3601Mapper.class ) + void shouldUseSourceParameterPresenceCheckCorrectly() { + Issue3601Mapper.Target target = Issue3601Mapper.INSTANCE.map( + new Issue3601Mapper.Source( "test1" ), + Collections.emptyList() + ); + + assertThat( target ).isNotNull(); + assertThat( target.currentId ).isEqualTo( "test1" ); + assertThat( target.targetIds ).isNull(); + + target = Issue3601Mapper.INSTANCE.map( + null, + Collections.emptyList() + ); + + assertThat( target ).isNull(); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceToTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceToTargetMapper.java index 35864fe62c..6577a6fd95 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceToTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/qualifier/ConditionalMethodWithSourceToTargetMapper.java @@ -5,10 +5,10 @@ */ package org.mapstruct.ap.test.conditional.qualifier; -import org.mapstruct.Condition; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named; +import org.mapstruct.SourceParameterCondition; import org.mapstruct.factory.Mappers; /** @@ -29,13 +29,13 @@ public interface ConditionalMethodWithSourceToTargetMapper { Address convertToAddress(OrderDTO orderDTO); - @Condition + @SourceParameterCondition @Named("mapCustomerFromOrder") default boolean mapCustomerFromOrder(OrderDTO orderDTO) { return orderDTO != null && ( orderDTO.getCustomerName() != null || mapAddressFromOrder( orderDTO ) ); } - @Condition + @SourceParameterCondition @Named("mapAddressFromOrder") default boolean mapAddressFromOrder(OrderDTO orderDTO) { return orderDTO != null && ( orderDTO.getLine1() != null || orderDTO.getLine2() != null ); From df49ce5ff93fce8c062263a8d56594e776dc0a3a Mon Sep 17 00:00:00 2001 From: Obolrom <65775868+Obolrom@users.noreply.github.com> Date: Sat, 20 Jul 2024 15:06:49 +0300 Subject: [PATCH 424/597] #3609 Pass bean mapping ignored unmapped source properties to subclass forged methods Co-authored-by: thunderhook <8238759+thunderhook@users.noreply.github.com> --- .../ap/internal/model/ForgedMethod.java | 21 ++++++++- .../model/source/BeanMappingOptions.java | 11 +++++ .../model/source/MappingMethodOptions.java | 13 ++++++ .../ap/test/bugs/_3609/Issue3609Mapper.java | 43 +++++++++++++++++++ .../ap/test/bugs/_3609/Issue3609Test.java | 23 ++++++++++ 5 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3609/Issue3609Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3609/Issue3609Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java index a33bc7520e..2aa12687d8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ForgedMethod.java @@ -146,13 +146,30 @@ public static ForgedMethod forSubclassMapping(String name, Type sourceType, Type basedOn, history, mappingReferences == null ? MappingReferences.empty() : mappingReferences, - forgedNameBased + forgedNameBased, + MappingMethodOptions.getSubclassForgedMethodInheritedOptions( basedOn.getOptions() ) ); } private ForgedMethod(String name, Type sourceType, Type returnType, List additionalParameters, Method basedOn, ForgedMethodHistory history, MappingReferences mappingReferences, boolean forgedNameBased) { + this( + name, + sourceType, + returnType, + additionalParameters, + basedOn, + history, + mappingReferences, + forgedNameBased, + MappingMethodOptions.getForgedMethodInheritedOptions( basedOn.getOptions() ) + ); + } + + private ForgedMethod(String name, Type sourceType, Type returnType, List additionalParameters, + Method basedOn, ForgedMethodHistory history, MappingReferences mappingReferences, + boolean forgedNameBased, MappingMethodOptions options) { // establish name String sourceParamSafeName; @@ -185,7 +202,7 @@ private ForgedMethod(String name, Type sourceType, Type returnType, List Date: Sat, 20 Jul 2024 16:19:59 +0200 Subject: [PATCH 425/597] #3591 Fix duplicate method generation with recursive auto mapping --- .../internal/model/AbstractBaseBuilder.java | 30 ++++--- .../model/ContainerMappingMethod.java | 3 +- .../ap/internal/model/PropertyMapping.java | 9 +- .../model/source/BeanMappingOptions.java | 4 +- .../model/source/IterableMappingOptions.java | 13 ++- .../model/source/MapMappingOptions.java | 4 +- .../internal/model/source/MappingOptions.java | 2 +- .../model/source/SelectionParameters.java | 15 ++++ .../mapstruct/ap/test/bugs/_3591/Bean.java | 36 ++++++++ .../mapstruct/ap/test/bugs/_3591/BeanDto.java | 30 +++++++ .../ap/test/bugs/_3591/BeanMapper.java | 21 +++++ .../ap/test/bugs/_3591/ContainerBean.java | 47 ++++++++++ .../ap/test/bugs/_3591/ContainerBeanDto.java | 40 +++++++++ .../test/bugs/_3591/ContainerBeanMapper.java | 22 +++++ .../ap/test/bugs/_3591/Issue3591Test.java | 79 +++++++++++++++++ .../bugs/_3591/ContainerBeanMapperImpl.java | 85 +++++++++++++++++++ 16 files changed, 416 insertions(+), 24 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/Bean.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/BeanDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/BeanMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBean.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBeanDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/Issue3591Test.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapperImpl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java index ba13854667..311cc00368 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.internal.model; +import java.util.function.Supplier; import javax.lang.model.element.AnnotationMirror; import org.mapstruct.ap.internal.model.common.Assignment; @@ -74,16 +75,9 @@ private boolean isDisableSubMappingMethodsGeneration() { */ Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, ForgedMethod forgedMethod) { - if ( ctx.getForgedMethodsUnderCreation().containsKey( forgedMethod ) ) { - return createAssignment( sourceRHS, ctx.getForgedMethodsUnderCreation().get( forgedMethod ) ); - } - else { - ctx.getForgedMethodsUnderCreation().put( forgedMethod, forgedMethod ); - } - - MappingMethod forgedMappingMethod; + Supplier forgedMappingMethodCreator; if ( MappingMethodUtils.isEnumMapping( forgedMethod ) ) { - forgedMappingMethod = new ValueMappingMethod.Builder() + forgedMappingMethodCreator = () -> new ValueMappingMethod.Builder() .method( forgedMethod ) .valueMappings( forgedMethod.getOptions().getValueMappings() ) .enumMapping( forgedMethod.getOptions().getEnumMappingOptions() ) @@ -91,15 +85,31 @@ Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, .build(); } else { - forgedMappingMethod = new BeanMappingMethod.Builder() + forgedMappingMethodCreator = () -> new BeanMappingMethod.Builder() .forgedMethod( forgedMethod ) .returnTypeBuilder( builderType ) .mappingContext( ctx ) .build(); } + return getOrCreateForgedAssignment( sourceRHS, forgedMethod, forgedMappingMethodCreator ); + } + + Assignment getOrCreateForgedAssignment(SourceRHS sourceRHS, ForgedMethod forgedMethod, + Supplier mappingMethodCreator) { + + if ( ctx.getForgedMethodsUnderCreation().containsKey( forgedMethod ) ) { + return createAssignment( sourceRHS, ctx.getForgedMethodsUnderCreation().get( forgedMethod ) ); + } + else { + ctx.getForgedMethodsUnderCreation().put( forgedMethod, forgedMethod ); + } + + MappingMethod forgedMappingMethod = mappingMethodCreator.get(); + Assignment forgedAssignment = createForgedAssignment( sourceRHS, forgedMethod, forgedMappingMethod ); ctx.getForgedMethodsUnderCreation().remove( forgedMethod ); + return forgedAssignment; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java index 7ecb7b0ed1..9e7e64fefa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java @@ -46,7 +46,8 @@ public abstract class ContainerMappingMethod extends NormalTypeMappingMethod { afterMappingReferences ); this.elementAssignment = parameterAssignment; this.loopVariableName = loopVariableName; - this.selectionParameters = selectionParameters; + this.selectionParameters = selectionParameters != null ? selectionParameters : SelectionParameters.empty(); + this.index1Name = Strings.getSafeVariableName( "i", existingVariables ); this.index2Name = Strings.getSafeVariableName( "j", existingVariables ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 2f30957962..db14ccbd8e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.function.Supplier; import javax.lang.model.element.AnnotationMirror; import org.mapstruct.ap.internal.gem.BuilderGem; @@ -746,7 +747,7 @@ private Assignment forgeWithElementMapping(Type sourceType, Type targetType, Sou targetType = targetType.withoutBounds(); ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, "[]" ); - ContainerMappingMethod iterableMappingMethod = builder + Supplier mappingMethodCreator = () -> builder .mappingContext( ctx ) .method( methodRef ) .selectionParameters( selectionParameters ) @@ -754,7 +755,7 @@ private Assignment forgeWithElementMapping(Type sourceType, Type targetType, Sou .positionHint( positionHint ) .build(); - return createForgedAssignment( source, methodRef, iterableMappingMethod ); + return getOrCreateForgedAssignment( source, methodRef, mappingMethodCreator ); } private ForgedMethod prepareForgedMethod(Type sourceType, Type targetType, SourceRHS source, String suffix) { @@ -772,12 +773,12 @@ private Assignment forgeMapMapping(Type sourceType, Type targetType, SourceRHS s ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, "{}" ); MapMappingMethod.Builder builder = new MapMappingMethod.Builder(); - MapMappingMethod mapMappingMethod = builder + Supplier mapMappingMethodCreator = () -> builder .mappingContext( ctx ) .method( methodRef ) .build(); - return createForgedAssignment( source, methodRef, mapMappingMethod ); + return getOrCreateForgedAssignment( source, methodRef, mapMappingMethodCreator ); } private Assignment forgeMapping(SourceRHS sourceRHS) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index a31000e8b4..bc19860bb5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -58,7 +58,7 @@ public static BeanMappingOptions forInheritance(BeanMappingOptions beanMapping, public static BeanMappingOptions forForgedMethods(BeanMappingOptions beanMapping) { BeanMappingOptions options = new BeanMappingOptions( beanMapping.selectionParameters != null ? - SelectionParameters.withoutResultType( beanMapping.selectionParameters ) : null, + SelectionParameters.withoutResultType( beanMapping.selectionParameters ) : SelectionParameters.empty(), Collections.emptyList(), beanMapping.beanMapping, beanMapping @@ -78,7 +78,7 @@ public static BeanMappingOptions forSubclassForgedMethods(BeanMappingOptions bea } public static BeanMappingOptions empty(DelegatingOptions delegatingOptions) { - return new BeanMappingOptions( null, Collections.emptyList(), null, delegatingOptions ); + return new BeanMappingOptions( SelectionParameters.empty(), Collections.emptyList(), null, delegatingOptions ); } public static BeanMappingOptions getInstanceOn(BeanMappingGem beanMapping, MapperOptions mapperOptions, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java index 4affcd93e2..c4770efde6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java @@ -8,14 +8,14 @@ import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.util.TypeUtils; -import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.gem.IterableMappingGem; import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.model.common.FormattingParameters; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.tools.gem.GemValue; /** @@ -34,7 +34,12 @@ public static IterableMappingOptions fromGem(IterableMappingGem iterableMapping, FormattingMessager messager, TypeUtils typeUtils) { if ( iterableMapping == null || !isConsistent( iterableMapping, method, messager ) ) { - IterableMappingOptions options = new IterableMappingOptions( null, null, null, mapperOptions ); + IterableMappingOptions options = new IterableMappingOptions( + null, + SelectionParameters.empty(), + null, + mapperOptions + ); return options; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java index ca8f1ea544..fd1758d8d2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java @@ -38,9 +38,9 @@ public static MapMappingOptions fromGem(MapMappingGem mapMapping, MapperOptions if ( mapMapping == null || !isConsistent( mapMapping, method, messager ) ) { MapMappingOptions options = new MapMappingOptions( null, + SelectionParameters.empty(), null, - null, - null, + SelectionParameters.empty(), null, mapperOptions ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index e0994d746f..24a94137f1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -182,7 +182,7 @@ public static MappingOptions forIgnore(String targetName) { null, true, null, - null, + SelectionParameters.empty(), Collections.emptySet(), null, null, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java index 7237668e2c..4706d219cb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java @@ -21,6 +21,16 @@ */ public class SelectionParameters { + private static final SelectionParameters EMPTY = new SelectionParameters( + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + null, + null, + null + ); + private final List qualifiers; private final List qualifyingNames; private final List conditionQualifiers; @@ -225,4 +235,9 @@ public static SelectionParameters forSourceRHS(SourceRHS sourceRHS) { sourceRHS ); } + + public static SelectionParameters empty() { + return EMPTY; + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/Bean.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/Bean.java new file mode 100644 index 0000000000..7da423c906 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/Bean.java @@ -0,0 +1,36 @@ +/* + * 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._3591; + +import java.util.List; + +public class Bean { + private List beans; + private String value; + + public Bean() { + } + + public Bean(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public List getBeans() { + return beans; + } + + public void setBeans(List beans) { + this.beans = beans; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/BeanDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/BeanDto.java new file mode 100644 index 0000000000..00e70ff228 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/BeanDto.java @@ -0,0 +1,30 @@ +/* + * 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._3591; + +import java.util.List; + +public class BeanDto { + + private List beans; + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public List getBeans() { + return beans; + } + + public void setBeans(List beans) { + this.beans = beans; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/BeanMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/BeanMapper.java new file mode 100644 index 0000000000..eeb54b4710 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/BeanMapper.java @@ -0,0 +1,21 @@ +/* + * 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._3591; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface BeanMapper { + + BeanMapper INSTANCE = Mappers.getMapper( BeanMapper.class ); + + @Mapping(source = "beans", target = "beans") + BeanDto map(Bean bean, @MappingTarget BeanDto beanDto); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBean.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBean.java new file mode 100644 index 0000000000..b0e68ecfcf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBean.java @@ -0,0 +1,47 @@ +/* + * 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._3591; + +import java.util.Map; +import java.util.stream.Stream; + +public class ContainerBean { + + private String value; + private Map beanMap; + private Stream beanStream; + + public ContainerBean() { + } + + public ContainerBean(String value) { + this.value = value; + } + + public Map getBeanMap() { + return beanMap; + } + + public void setBeanMap(Map beanMap) { + this.beanMap = beanMap; + } + + public Stream getBeanStream() { + return beanStream; + } + + public void setBeanStream(Stream beanStream) { + this.beanStream = beanStream; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBeanDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBeanDto.java new file mode 100644 index 0000000000..86bb0193ac --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBeanDto.java @@ -0,0 +1,40 @@ +/* + * 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._3591; + +import java.util.Map; +import java.util.stream.Stream; + +public class ContainerBeanDto { + + private String value; + private Map beanMap; + private Stream beanStream; + + public Map getBeanMap() { + return beanMap; + } + + public void setBeanMap(Map beanMap) { + this.beanMap = beanMap; + } + + public Stream getBeanStream() { + return beanStream; + } + + public void setBeanStream(Stream beanStream) { + this.beanStream = beanStream; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapper.java new file mode 100644 index 0000000000..0da338aadf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapper.java @@ -0,0 +1,22 @@ +/* + * 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._3591; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ContainerBeanMapper { + + ContainerBeanMapper INSTANCE = Mappers.getMapper( ContainerBeanMapper.class ); + + @Mapping(source = "beanMap", target = "beanMap") + @Mapping(source = "beanStream", target = "beanStream") + ContainerBeanDto mapWithMapMapping(ContainerBean containerBean, @MappingTarget ContainerBeanDto containerBeanDto); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/Issue3591Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/Issue3591Test.java new file mode 100644 index 0000000000..15bb191ffc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3591/Issue3591Test.java @@ -0,0 +1,79 @@ +/* + * 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._3591; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("3591") +class Issue3591Test { + + @RegisterExtension + GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses({ + BeanDto.class, + Bean.class, + BeanMapper.class + }) + void mapNestedBeansWithMappingAnnotation() { + Bean bean = new Bean( "parent" ); + Bean child = new Bean( "child" ); + bean.setBeans( Collections.singletonList( child ) ); + + BeanDto beanDto = BeanMapper.INSTANCE.map( bean, new BeanDto() ); + + assertThat( beanDto ).isNotNull(); + assertThat( beanDto.getValue() ).isEqualTo( "parent" ); + assertThat( beanDto.getBeans() ) + .extracting( BeanDto::getValue ) + .containsExactly( "child" ); + } + + @ProcessorTest + @WithClasses({ + ContainerBean.class, + ContainerBeanDto.class, + ContainerBeanMapper.class, + }) + void shouldMapNestedMapAndStream() { + generatedSource.addComparisonToFixtureFor( ContainerBeanMapper.class ); + + ContainerBean containerBean = new ContainerBean( "parent" ); + Map beanMap = new HashMap<>(); + beanMap.put( "child", new ContainerBean( "mapChild" ) ); + containerBean.setBeanMap( beanMap ); + + Stream streamChild = Stream.of( new ContainerBean( "streamChild" ) ); + containerBean.setBeanStream( streamChild ); + + ContainerBeanDto dto = ContainerBeanMapper.INSTANCE.mapWithMapMapping( containerBean, new ContainerBeanDto() ); + + assertThat( dto ).isNotNull(); + + assertThat( dto.getBeanMap() ) + .extractingByKey( "child" ) + .extracting( ContainerBeanDto::getValue ) + .isEqualTo( "mapChild" ); + + assertThat( dto.getBeanStream() ) + .singleElement() + .extracting( ContainerBeanDto::getValue ) + .isEqualTo( "streamChild" ); + } + +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapperImpl.java new file mode 100644 index 0000000000..47baa689c8 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapperImpl.java @@ -0,0 +1,85 @@ +/* + * 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._3591; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Stream; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-05-25T14:23:23+0200", + comments = "version: , compiler: javac, environment: Java 17.0.11 (N/A)" +) +public class ContainerBeanMapperImpl implements ContainerBeanMapper { + + @Override + public ContainerBeanDto mapWithMapMapping(ContainerBean containerBean, ContainerBeanDto containerBeanDto) { + if ( containerBean == null ) { + return containerBeanDto; + } + + if ( containerBeanDto.getBeanMap() != null ) { + Map map = stringContainerBeanMapToStringContainerBeanDtoMap( containerBean.getBeanMap() ); + if ( map != null ) { + containerBeanDto.getBeanMap().clear(); + containerBeanDto.getBeanMap().putAll( map ); + } + else { + containerBeanDto.setBeanMap( null ); + } + } + else { + Map map = stringContainerBeanMapToStringContainerBeanDtoMap( containerBean.getBeanMap() ); + if ( map != null ) { + containerBeanDto.setBeanMap( map ); + } + } + containerBeanDto.setBeanStream( containerBeanStreamToContainerBeanDtoStream( containerBean.getBeanStream() ) ); + containerBeanDto.setValue( containerBean.getValue() ); + + return containerBeanDto; + } + + protected Stream containerBeanStreamToContainerBeanDtoStream(Stream stream) { + if ( stream == null ) { + return null; + } + + return stream.map( containerBean -> containerBeanToContainerBeanDto( containerBean ) ); + } + + protected ContainerBeanDto containerBeanToContainerBeanDto(ContainerBean containerBean) { + if ( containerBean == null ) { + return null; + } + + ContainerBeanDto containerBeanDto = new ContainerBeanDto(); + + containerBeanDto.setBeanMap( stringContainerBeanMapToStringContainerBeanDtoMap( containerBean.getBeanMap() ) ); + containerBeanDto.setBeanStream( containerBeanStreamToContainerBeanDtoStream( containerBean.getBeanStream() ) ); + containerBeanDto.setValue( containerBean.getValue() ); + + return containerBeanDto; + } + + protected Map stringContainerBeanMapToStringContainerBeanDtoMap(Map map) { + if ( map == null ) { + return null; + } + + Map map1 = new LinkedHashMap( Math.max( (int) ( map.size() / .75f ) + 1, 16 ) ); + + for ( java.util.Map.Entry entry : map.entrySet() ) { + String key = entry.getKey(); + ContainerBeanDto value = containerBeanToContainerBeanDto( entry.getValue() ); + map1.put( key, value ); + } + + return map1; + } +} From e2edb1a0864b1a2b1a39341a7113a63f40650e92 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 20 Jul 2024 14:04:47 +0200 Subject: [PATCH 426/597] #3504 Add example classes for the passing target type documentation --- .../chapter-5-data-type-conversions.asciidoc | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 30430fe97e..bc406cf0b0 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -401,6 +401,39 @@ When having a custom mapper hooked into the generated mapper with `@Mapper#uses( For instance, the `CarDto` could have a property `owner` of type `Reference` that contains the primary key of a `Person` entity. You could now create a generic custom mapper that resolves any `Reference` objects to their corresponding managed JPA entity instances. +e.g. + +.Example classes for the passing target type example +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public class Car { + + private Person owner; + // ... +} + +public class Person extends BaseEntity { + + // ... +} + +public class Reference { + + private String pk; + // ... +} + +public class CarDto { + + private Reference owner; + // ... +} +---- +==== + + .Mapping method expecting mapping target type as parameter ==== [source, java, linenums] From 5ce9c537e963709c33f0fa830b6c1da8653bc885 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 20 Jul 2024 16:27:04 +0200 Subject: [PATCH 427/597] Add release notes --- NEXT_RELEASE_CHANGELOG.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 305bdda947..39f23af6fc 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,19 +1,26 @@ -### Features - ### Enhancements -* Breaking change:g (#3574) - +* Breaking change: (#3574) - This reverts #2560, because we've decided that `@BeanMapping(ignoreByDefault = true)` should only be applied to target properties and not to source properties. Source properties are ignored anyway, the `BeanMapping#unmappedSourcePolicy` should be used to control what should happen with unmapped source policy ### Bugs * Breaking change: Presence check method used only once when multiple source parameters are provided (#3601) +* Fix `@SubclassMapping` not working with `@BeanMapping#ignoreUnmappedSourceProperties` (#3609) +* Fix duplicate method generation with recursive auto mapping (#3591) ### Documentation +* Fix documentation of `unmappedSourcePolicy` default value (#3635) +* Fix documentation link of before and after mapping when using builders (#3639) +* Fix typo in experimental note (#3634) +* Add example classes for the passing target type documentation (#3504) + ### Build +* Enforce whitespaces around the for colon with CheckStyle (#3642) + ## Breaking changes ### Presence checks for source parameters From bbb9bb403c93e1c938907523ce9fcdeb67c2819c Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 20 Jul 2024 17:20:31 +0200 Subject: [PATCH 428/597] Fix typo in changelog --- NEXT_RELEASE_CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 39f23af6fc..f08c6a2fd9 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -47,7 +47,7 @@ public interface OrderMapper { } ``` -Them MapStruct would generate +Then MapStruct would generate ```java public class OrderMapperImpl implements OrderMapper { From 6ef64ea3aa8320c52a924375d3bc6edf60f0c86d Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 15:36:11 +0000 Subject: [PATCH 429/597] Releasing version 1.6.0.RC1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0bdec734cb..ff56bd7953 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.RC1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 160f963adf..d2806fa413 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.RC1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index ac993c73d6..478ef7308f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.RC1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index afa8e31002..4bd4bc5dc4 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.RC1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 0a62a3ef3c..d98933a414 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.RC1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index f888e226ff..10850bc821 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.RC1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index e4109325f7..2b3b8e65a6 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.RC1 pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - ${git.commit.author.time} + 2024-07-20T15:36:10Z 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index 90402f0cae..571623372b 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.RC1 parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 12fc615f5f..e33e84ba65 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0.RC1 ../parent/pom.xml From 6365a606c1b7f2c8d2dfb1583c74d46fb122eb1d Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 15:45:11 +0000 Subject: [PATCH 430/597] Next version 1.6.0-SNAPSHOT --- NEXT_RELEASE_CHANGELOG.md | 83 +-------------------------------------- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 +- pom.xml | 2 +- processor/pom.xml | 2 +- 10 files changed, 12 insertions(+), 91 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index f08c6a2fd9..e0f4cd31f0 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,89 +1,10 @@ -### Enhancements +### Features -* Breaking change: (#3574) - -This reverts #2560, because we've decided that `@BeanMapping(ignoreByDefault = true)` should only be applied to target properties and not to source properties. -Source properties are ignored anyway, the `BeanMapping#unmappedSourcePolicy` should be used to control what should happen with unmapped source policy +### Enhancements ### Bugs -* Breaking change: Presence check method used only once when multiple source parameters are provided (#3601) -* Fix `@SubclassMapping` not working with `@BeanMapping#ignoreUnmappedSourceProperties` (#3609) -* Fix duplicate method generation with recursive auto mapping (#3591) - ### Documentation -* Fix documentation of `unmappedSourcePolicy` default value (#3635) -* Fix documentation link of before and after mapping when using builders (#3639) -* Fix typo in experimental note (#3634) -* Add example classes for the passing target type documentation (#3504) - ### Build -* Enforce whitespaces around the for colon with CheckStyle (#3642) - -## Breaking changes - -### Presence checks for source parameters - -In 1.6, support for presence checks on source parameters has been added. -This means that even if you want to map a source parameter directly to some target property the new `@SourceParameterCondition` or `@Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS)` should be used. - -e.g. - -If we had the following in 1.5: -```java -@Mapper -public interface OrderMapper { - - @Mapping(source = "dto", target = "customer", conditionQualifiedByName = "mapCustomerFromOrder") - Order map(OrderDTO dto); - - @Condition - @Named("mapCustomerFromOrder") - default boolean mapCustomerFromOrder(OrderDTO dto) { - return dto != null && dto.getCustomerName() != null; - } - -} -``` - -Then MapStruct would generate - -```java -public class OrderMapperImpl implements OrderMapper { - - @Override - public Order map(OrderDTO dto) { - if ( dto == null ) { - return null; - } - - Order order = new Order(); - - if ( mapCustomerFromOrder( dto ) ) { - order.setCustomer( orderDtoToCustomer( orderDTO ) ); - } - - return order; - } -} -``` - -In order for the same to be generated in 1.6, the mapper needs to look like this: - -```java -@Mapper -public interface OrderMapper { - - @Mapping(source = "dto", target = "customer", conditionQualifiedByName = "mapCustomerFromOrder") - Order map(OrderDTO dto); - - @SourceParameterCondition - @Named("mapCustomerFromOrder") - default boolean mapCustomerFromOrder(OrderDTO dto) { - return dto != null && dto.getCustomerName() != null; - } - -} -``` - diff --git a/build-config/pom.xml b/build-config/pom.xml index ff56bd7953..0bdec734cb 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.RC1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index d2806fa413..160f963adf 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.RC1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 478ef7308f..ac993c73d6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.RC1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 4bd4bc5dc4..afa8e31002 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.RC1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index d98933a414..0a62a3ef3c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.RC1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 10850bc821..f888e226ff 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.RC1 + 1.6.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 2b3b8e65a6..e4109325f7 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0.RC1 + 1.6.0-SNAPSHOT pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - 2024-07-20T15:36:10Z + ${git.commit.author.time} 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index 571623372b..90402f0cae 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0.RC1 + 1.6.0-SNAPSHOT parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index e33e84ba65..12fc615f5f 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0.RC1 + 1.6.0-SNAPSHOT ../parent/pom.xml From 0f24633d04c0a568ab879e629a4720f37a51616b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 20 Jul 2024 17:56:48 +0200 Subject: [PATCH 431/597] Fix update website script to be able to run Linux --- .github/scripts/update-website.sh | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/scripts/update-website.sh b/.github/scripts/update-website.sh index 8fa989a1dd..7c92b8b43b 100644 --- a/.github/scripts/update-website.sh +++ b/.github/scripts/update-website.sh @@ -38,16 +38,22 @@ STABLE_VERSION=`grep stableVersion config.toml | sed 's/.*"\(.*\)"/\1/'` MAJOR_MINOR_STABLE_VERSION=$(computeMajorMinorVersion $STABLE_VERSION) echo "📝 Updating versions" -sed -i '' -e "s/^devVersion = \"\(.*\)\"/devVersion = \"${VERSION}\"/g" config.toml + +SEDOPTION="-i" +if [[ "$OSTYPE" == "darwin"* ]]; then + SEDOPTION="-i ''" +fi + +sed $SEDOPTION -e "s/^devVersion = \"\(.*\)\"/devVersion = \"${VERSION}\"/g" config.toml if [ "${STABLE}" == "yes" ]; then - sed -i '' -e "s/^stableVersion = \"\(.*\)\"/stableVersion = \"${VERSION}\"/g" config.toml + sed $SEDOPTION -e "s/^stableVersion = \"\(.*\)\"/stableVersion = \"${VERSION}\"/g" config.toml if [ "${MAJOR_MINOR_STABLE_VERSION}" != ${MAJOR_MINOR_VERSION} ]; then echo "📝 Updating new stable version" # This means that we have a new stable version and we need to change the order of the releases. - sed -i '' -e "s/^order = \(.*\)/order = 500/g" data/releases/${MAJOR_MINOR_VERSION}.toml + sed $SEDOPTION -e "s/^order = \(.*\)/order = 500/g" data/releases/${MAJOR_MINOR_VERSION}.toml NEXT_STABLE_ORDER=$((`ls -1 data/releases | wc -l` - 2)) - sed -i '' -e "s/^order = \(.*\)/order = ${NEXT_STABLE_ORDER}/g" data/releases/${MAJOR_MINOR_STABLE_VERSION}.toml + sed $SEDOPTION -e "s/^order = \(.*\)/order = ${NEXT_STABLE_ORDER}/g" data/releases/${MAJOR_MINOR_STABLE_VERSION}.toml git add data/releases/${MAJOR_MINOR_STABLE_VERSION}.toml fi elif [ "${MAJOR_MINOR_DEV_VERSION}" != "${MAJOR_MINOR_VERSION}" ]; then @@ -55,11 +61,11 @@ elif [ "${MAJOR_MINOR_DEV_VERSION}" != "${MAJOR_MINOR_VERSION}" ]; then # This means that we are updating for a new dev version, but the last dev version is not the one that we are doing. # Therefore, we need to update add the new data configuration cp data/releases/${MAJOR_MINOR_DEV_VERSION}.toml data/releases/${MAJOR_MINOR_VERSION}.toml - sed -i '' -e "s/^order = \(.*\)/order = 1000/g" data/releases/${MAJOR_MINOR_VERSION}.toml + sed $SEDOPTION -e "s/^order = \(.*\)/order = 1000/g" data/releases/${MAJOR_MINOR_VERSION}.toml fi -sed -i '' -e "s/^name = \"\(.*\)\"/name = \"${VERSION}\"/g" data/releases/${MAJOR_MINOR_VERSION}.toml -sed -i '' -e "s/^releaseDate = \(.*\)/releaseDate = $(date +%F)/g" data/releases/${MAJOR_MINOR_VERSION}.toml +sed $SEDOPTION -e "s/^name = \"\(.*\)\"/name = \"${VERSION}\"/g" data/releases/${MAJOR_MINOR_VERSION}.toml +sed $SEDOPTION -e "s/^releaseDate = \(.*\)/releaseDate = $(date +%F)/g" data/releases/${MAJOR_MINOR_VERSION}.toml git add data/releases/${MAJOR_MINOR_VERSION}.toml git add config.toml From 81ca739040022ab06991cf3d1d4d12fb8fa68e5b Mon Sep 17 00:00:00 2001 From: thunderhook <8238759+thunderhook@users.noreply.github.com> Date: Sun, 21 Jul 2024 22:59:41 +0200 Subject: [PATCH 432/597] #3638 Remove deprecation note of enum mapping via @Mapping --- core/src/main/java/org/mapstruct/Mapping.java | 5 +---- .../src/main/asciidoc/chapter-8-mapping-values.asciidoc | 6 ------ 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 42384152d0..8b0c4adb0c 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -20,7 +20,7 @@ import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; /** - * Configures the mapping of one bean attribute or enum constant. + * Configures the mapping of one bean attribute. *

      * The name of the mapped attribute or constant is to be specified via {@link #target()}. For mapped bean attributes it * is assumed by default that the attribute has the same name in the source bean. Alternatively, one of @@ -136,9 +136,6 @@ * } *

      * - * IMPORTANT NOTE: the enum mapping capability is deprecated and replaced by {@link ValueMapping} it - * will be removed in subsequent versions. - * * @author Gunnar Morling */ diff --git a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc index 965479541e..fcb353010d 100644 --- a/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc +++ b/documentation/src/main/asciidoc/chapter-8-mapping-values.asciidoc @@ -189,12 +189,6 @@ public class SpecialOrderMapperImpl implements SpecialOrderMapper { ---- ==== - -[WARNING] -==== -The mapping of enum to enum via the `@Mapping` annotation is *DEPRECATED*. It will be removed from future versions of MapStruct. Please adapt existing enum mapping methods to make use of `@ValueMapping` instead. -==== - === Mapping enum-to-String or String-to-enum MapStruct supports enum to a String mapping along the same lines as is described in <>. There are similarities and differences: From 38ec5c53350905fb8902935306b42fd1845c40c0 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 20:59:31 +0000 Subject: [PATCH 433/597] Releasing version 1.6.0 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0bdec734cb..3848c6f465 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 160f963adf..bc88fdbb71 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index ac993c73d6..f002c4dd28 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index afa8e31002..a44578dc47 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 0a62a3ef3c..f82f10d339 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index f888e226ff..4ee4319a23 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index e4109325f7..a47b1fad15 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0 pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - ${git.commit.author.time} + 2024-08-12T20:59:31Z 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index 90402f0cae..361be72a33 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0 parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 12fc615f5f..589e37007e 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0-SNAPSHOT + 1.6.0 ../parent/pom.xml From 96d06984171f56ac6a910c144fd8a30cd42f1e3d Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:08:07 +0000 Subject: [PATCH 434/597] Next version 1.7.0-SNAPSHOT --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 3848c6f465..0b3ca1c113 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index bc88fdbb71..c73676192b 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index f002c4dd28..e62d5e4682 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index a44578dc47..f826421092 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index f82f10d339..21a2b151bd 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 4ee4319a23..7c91837e79 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index a47b1fad15..94c84fb4d0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.0 + 1.7.0-SNAPSHOT pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - 2024-08-12T20:59:31Z + ${git.commit.author.time} 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index 361be72a33..f55f0955d3 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.0 + 1.7.0-SNAPSHOT parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 589e37007e..ed2df7718b 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.0 + 1.7.0-SNAPSHOT ../parent/pom.xml From b452d7f2c8cc48c384d61b87ad530d2f9c882fa1 Mon Sep 17 00:00:00 2001 From: Stefan Simon <35351956+Hypnagokali@users.noreply.github.com> Date: Sun, 18 Aug 2024 17:46:35 +0200 Subject: [PATCH 435/597] #3652 Inverse Inheritance should be possible for ignore-mappings without source --- NEXT_RELEASE_CHANGELOG.md | 2 ++ .../internal/model/source/MappingOptions.java | 5 ++- .../org/mapstruct/ap/test/bugs/_3652/Bar.java | 30 ++++++++++++++++ .../org/mapstruct/ap/test/bugs/_3652/Foo.java | 21 +++++++++++ .../ap/test/bugs/_3652/FooBarConfig.java | 24 +++++++++++++ .../ap/test/bugs/_3652/FooBarMapper.java | 21 +++++++++++ .../ap/test/bugs/_3652/Issue3652Test.java | 35 +++++++++++++++++++ 7 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Bar.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Foo.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/FooBarConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/FooBarMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Issue3652Test.java diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index e0f4cd31f0..b4a6b4a75b 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -4,6 +4,8 @@ ### Bugs +* Inverse Inheritance Strategy not working for ignored mappings only with target (#3652) + ### Documentation ### Build diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 24a94137f1..22f9ccdc2f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -479,13 +479,12 @@ public MappingControl getMappingControl(ElementUtils elementUtils) { } /** - * mapping can only be inversed if the source was not a constant nor an expression nor a nested property - * and the mapping is not a 'target-source-ignore' mapping + * Mapping can only be inversed if the source was not a constant nor an expression * * @return true when the above applies */ public boolean canInverse() { - return constant == null && javaExpression == null && !( isIgnored && sourceName == null ); + return constant == null && javaExpression == null; } public MappingOptions copyForInverseInheritance(SourceMethod templateMethod, diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Bar.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Bar.java new file mode 100644 index 0000000000..3ac8b595ee --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Bar.java @@ -0,0 +1,30 @@ +/* + * 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._3652; + +public class Bar { + + private int secret; + private int doesNotExistInFoo; + + public int getSecret() { + return secret; + } + + public void setSecret(int secret) { + this.secret = secret; + } + + public int getDoesNotExistInFoo() { + return doesNotExistInFoo; + } + + public void setDoesNotExistInFoo(int doesNotExistInFoo) { + this.doesNotExistInFoo = doesNotExistInFoo; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Foo.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Foo.java new file mode 100644 index 0000000000..02b5b6e8b0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Foo.java @@ -0,0 +1,21 @@ +/* + * 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._3652; + +public class Foo { + + private int secret; + + public int getSecret() { + return secret; + } + + public void setSecret(int secret) { + this.secret = secret; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/FooBarConfig.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/FooBarConfig.java new file mode 100644 index 0000000000..3cf19dfbfb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/FooBarConfig.java @@ -0,0 +1,24 @@ +/* + * 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._3652; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.MapperConfig; +import org.mapstruct.Mapping; +import org.mapstruct.MappingInheritanceStrategy; + +@MapperConfig(mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_ALL_FROM_CONFIG) +public interface FooBarConfig { + + @Mapping(target = "doesNotExistInFoo", ignore = true) + @Mapping(target = "secret", ignore = true) + Bar toBar(Foo foo); + + @InheritInverseConfiguration(name = "toBar") + Foo toFoo(Bar bar); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/FooBarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/FooBarMapper.java new file mode 100644 index 0000000000..d58be74f1e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/FooBarMapper.java @@ -0,0 +1,21 @@ +/* + * 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._3652; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper(config = FooBarConfig.class) +public interface FooBarMapper { + + FooBarMapper INSTANCE = Mappers.getMapper( FooBarMapper.class ); + + Bar toBar(Foo foo); + + Foo toFoo(Bar bar); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Issue3652Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Issue3652Test.java new file mode 100644 index 0000000000..aa8a64ada8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3652/Issue3652Test.java @@ -0,0 +1,35 @@ +/* + * 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._3652; + +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; + +@IssueKey("3652") +public class Issue3652Test { + + @WithClasses({ + Bar.class, + Foo.class, + FooBarConfig.class, + FooBarMapper.class, + }) + @ProcessorTest + void ignoreMappingsWithoutSourceShouldBeInvertible() { + Bar bar = new Bar(); + bar.setSecret( 123 ); + bar.setDoesNotExistInFoo( 6 ); + + Foo foo = FooBarMapper.INSTANCE.toFoo( bar ); + + assertThat( foo.getSecret() ).isEqualTo( 0 ); + } + +} From 60cd0a442026a3385385dbfb0fc8cd43289992fa Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 24 Aug 2024 11:27:52 +0200 Subject: [PATCH 436/597] #3670 Fix regression when using `InheritInverseConfiguration` with nested target properties and reversing `target = "."` --- NEXT_RELEASE_CHANGELOG.md | 1 + .../NestedTargetPropertyMappingHolder.java | 8 +- .../ap/test/bugs/_3670/Issue3670Mapper.java | 74 +++++++++++++++++++ .../ap/test/bugs/_3670/Issue3670Test.java | 22 ++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3670/Issue3670Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3670/Issue3670Test.java diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index b4a6b4a75b..a3ea4e5026 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -5,6 +5,7 @@ ### Bugs * Inverse Inheritance Strategy not working for ignored mappings only with target (#3652) +* Fix regression when using `InheritInverseConfiguration` with nested target properties and reversing `target = "."` (#3670) ### Documentation diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index 20825c2dd9..92da1f4509 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -362,7 +362,13 @@ private GroupedTargetReferences groupByTargetReferences( ) { Map> singleTargetReferences = new LinkedHashMap<>(); for ( MappingReference mapping : mappingReferences.getMappingReferences() ) { TargetReference targetReference = mapping.getTargetReference(); - String property = first( targetReference.getPropertyEntries() ); + List propertyEntries = targetReference.getPropertyEntries(); + if ( propertyEntries.isEmpty() ) { + // This can happen if the target property is target = ".", + // this usually happens when doing a reverse mapping + continue; + } + String property = first( propertyEntries ); MappingReference newMapping = mapping.popTargetReference(); if ( newMapping != null ) { // group properties on current name. diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3670/Issue3670Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3670/Issue3670Mapper.java new file mode 100644 index 0000000000..a014a7d52d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3670/Issue3670Mapper.java @@ -0,0 +1,74 @@ +/* + * 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._3670; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3670Mapper { + + @Mapping(target = "name", source = ".", qualifiedByName = "nestedName") + Target map(Source source); + + @InheritInverseConfiguration + @Mapping(target = "nested.nestedName", source = "name") + Source map(Target target); + + @Named("nestedName") + default String mapNestedName(Source source) { + if ( source == null ) { + return null; + } + + Nested nested = source.getNested(); + + return nested != null ? nested.getNestedName() : null; + } + + class Target { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + class Nested { + private String nestedName; + + public String getNestedName() { + return nestedName; + } + + public void setNestedName(String nestedName) { + this.nestedName = nestedName; + } + } + + class Source { + + private Nested nested; + + public Nested getNested() { + return nested; + } + + public void setNested(Nested nested) { + this.nested = nested; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3670/Issue3670Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3670/Issue3670Test.java new file mode 100644 index 0000000000..fc3929b685 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3670/Issue3670Test.java @@ -0,0 +1,22 @@ +/* + * 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._3670; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Filip Hrisafov + */ +@IssueKey("3670") +@WithClasses(Issue3670Mapper.class) +class Issue3670Test { + + @ProcessorTest + void shouldCompile() { + } +} From 6c8a2e184bd0254d8fac8bc1e04382da56750a60 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 18 Aug 2024 23:47:35 +0200 Subject: [PATCH 437/597] #3667, #3673 MappingReference should custom MappingOption equality instead of the default only target name based one --- .../model/beanmapping/MappingReference.java | 32 ++++++++- .../ap/test/bugs/_3667/Issue3667Mapper.java | 22 ++++++ .../ap/test/bugs/_3667/Issue3667Test.java | 43 ++++++++++++ .../mapstruct/ap/test/bugs/_3667/Source.java | 51 ++++++++++++++ .../mapstruct/ap/test/bugs/_3667/Target.java | 32 +++++++++ .../mapstruct/ap/test/bugs/_3673/Animal.java | 45 ++++++++++++ .../org/mapstruct/ap/test/bugs/_3673/Cat.java | 19 ++++++ .../mapstruct/ap/test/bugs/_3673/Details.java | 19 ++++++ .../org/mapstruct/ap/test/bugs/_3673/Dog.java | 19 ++++++ .../bugs/_3673/Issue3673ConstantMapper.java | 25 +++++++ .../bugs/_3673/Issue3673ExpressionMapper.java | 25 +++++++ .../ap/test/bugs/_3673/Issue3673Test.java | 68 +++++++++++++++++++ 12 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Issue3667Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Issue3667Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Animal.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Cat.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Details.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Dog.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673ConstantMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673ExpressionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java index e2501ea952..d013a77a7a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java @@ -71,7 +71,37 @@ public boolean equals(Object o) { return false; } MappingReference that = (MappingReference) o; - return mapping.equals( that.mapping ); + if ( ".".equals( that.mapping.getTargetName() ) ) { + // target this will never be equal to any other target this or any other. + return false; + } + + if (!Objects.equals( mapping.getTargetName(), that.mapping.getTargetName() ) ) { + return false; + } + + if ( !Objects.equals( mapping.getConstant(), that.mapping.getConstant() ) ) { + return false; + } + + if ( !Objects.equals( mapping.getJavaExpression(), that.mapping.getJavaExpression() ) ) { + return false; + } + + if ( sourceReference == null ) { + return that.sourceReference == null; + } + + if ( that.sourceReference == null ) { + return false; + } + + + if (!Objects.equals( sourceReference.getPropertyEntries(), that.sourceReference.getPropertyEntries() ) ) { + return false; + } + + return true; } @Override diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Issue3667Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Issue3667Mapper.java new file mode 100644 index 0000000000..d4f2dff0af --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Issue3667Mapper.java @@ -0,0 +1,22 @@ +/* + * 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._3667; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue3667Mapper { + + Issue3667Mapper INSTANCE = Mappers.getMapper( Issue3667Mapper.class ); + + @Mapping(target = "nested.value", source = "nested.nested1.value") + Target mapFirst(Source source); + + @Mapping(target = "nested.value", source = "nested.nested2.value") + Target mapSecond(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Issue3667Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Issue3667Test.java new file mode 100644 index 0000000000..1e91de00d7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Issue3667Test.java @@ -0,0 +1,43 @@ +/* + * 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._3667; + +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; + +@IssueKey("3667") +@WithClasses({ + Issue3667Mapper.class, + Source.class, + Target.class +}) +class Issue3667Test { + + @ProcessorTest + void shouldCorrectlyMapNestedProperty() { + Source source = new Source( + new Source.Nested( + new Source.NestedNested( "value1" ), + new Source.NestedNested( "value2" ) + ) + ); + + Target target1 = Issue3667Mapper.INSTANCE.mapFirst( source ); + Target target2 = Issue3667Mapper.INSTANCE.mapSecond( source ); + + assertThat( target1 ).isNotNull(); + assertThat( target1.getNested() ).isNotNull(); + assertThat( target1.getNested().getValue() ).isEqualTo( "value1" ); + + assertThat( target2 ).isNotNull(); + assertThat( target2.getNested() ).isNotNull(); + assertThat( target2.getNested().getValue() ).isEqualTo( "value2" ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Source.java new file mode 100644 index 0000000000..ede78edf0d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Source.java @@ -0,0 +1,51 @@ +/* + * 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._3667; + +public class Source { + + private final Nested nested; + + public Source(Nested nested) { + this.nested = nested; + } + + public Nested getNested() { + return nested; + } + + public static class Nested { + + private final NestedNested nested1; + private final NestedNested nested2; + + public Nested(NestedNested nested1, NestedNested nested2) { + this.nested1 = nested1; + this.nested2 = nested2; + } + + public NestedNested getNested1() { + return nested1; + } + + public NestedNested getNested2() { + return nested2; + } + } + + public static class NestedNested { + + private final String value; + + public NestedNested(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Target.java new file mode 100644 index 0000000000..e0c8d23296 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3667/Target.java @@ -0,0 +1,32 @@ +/* + * 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._3667; + +public class Target { + + private Nested nested; + + public Nested getNested() { + return nested; + } + + public void setNested(Nested nested) { + this.nested = nested; + } + + public static class Nested { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Animal.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Animal.java new file mode 100644 index 0000000000..9fe97e0e79 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Animal.java @@ -0,0 +1,45 @@ +/* + * 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._3673; + +public class Animal { + + private AnimalDetails details; + + public AnimalDetails getDetails() { + return details; + } + + public void setDetails(AnimalDetails details) { + this.details = details; + } + + public enum Type { + CAT, + DOG + } + + public static class AnimalDetails { + private Type type; + private String name; + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Cat.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Cat.java new file mode 100644 index 0000000000..63e5391690 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Cat.java @@ -0,0 +1,19 @@ +/* + * 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._3673; + +public class Cat { + + private final Details details; + + public Cat(Details details) { + this.details = details; + } + + public Details getDetails() { + return details; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Details.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Details.java new file mode 100644 index 0000000000..8526793015 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Details.java @@ -0,0 +1,19 @@ +/* + * 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._3673; + +public class Details { + + private final String name; + + public Details(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Dog.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Dog.java new file mode 100644 index 0000000000..a021a5d579 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Dog.java @@ -0,0 +1,19 @@ +/* + * 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._3673; + +public class Dog { + + private final Details details; + + public Dog(Details details) { + this.details = details; + } + + public Details getDetails() { + return details; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673ConstantMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673ConstantMapper.java new file mode 100644 index 0000000000..d74a954f9f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673ConstantMapper.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.ap.test.bugs._3673; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue3673ConstantMapper { + + Issue3673ConstantMapper INSTANCE = Mappers.getMapper( Issue3673ConstantMapper.class ); + + @Mapping(target = "details.name", source = "details.name") + @Mapping(target = "details.type", constant = "DOG") + Animal map(Dog dog); + + @Mapping(target = "details.name", source = "details.name") + @Mapping(target = "details.type", constant = "CAT") + Animal map(Cat cat); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673ExpressionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673ExpressionMapper.java new file mode 100644 index 0000000000..3aca966807 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673ExpressionMapper.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.ap.test.bugs._3673; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue3673ExpressionMapper { + + Issue3673ExpressionMapper INSTANCE = Mappers.getMapper( Issue3673ExpressionMapper.class ); + + @Mapping(target = "details.name", source = "details.name") + @Mapping(target = "details.type", expression = "java(Animal.Type.DOG)") + Animal map(Dog dog); + + @Mapping(target = "details.name", source = "details.name") + @Mapping(target = "details.type", expression = "java(Animal.Type.CAT)") + Animal map(Cat cat); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673Test.java new file mode 100644 index 0000000000..2d796b4670 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3673/Issue3673Test.java @@ -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._3673; + +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; + +@IssueKey("3673") +@WithClasses({ + Cat.class, + Dog.class, + Details.class, + Animal.class +}) +class Issue3673Test { + + @ProcessorTest + @WithClasses(Issue3673ConstantMapper.class) + void shouldCorrectlyMapNestedPropertyConstant() { + + Animal cat = Issue3673ConstantMapper.INSTANCE.map( + new Cat( new Details( "cat" ) ) + ); + + Animal dog = Issue3673ConstantMapper.INSTANCE.map( + new Dog( new Details( "dog" ) ) + ); + + assertThat( cat ).isNotNull(); + assertThat( cat.getDetails() ).isNotNull(); + assertThat( cat.getDetails().getName() ).isEqualTo( "cat" ); + assertThat( cat.getDetails().getType() ).isEqualTo( Animal.Type.CAT ); + + assertThat( dog ).isNotNull(); + assertThat( dog.getDetails() ).isNotNull(); + assertThat( dog.getDetails().getName() ).isEqualTo( "dog" ); + assertThat( dog.getDetails().getType() ).isEqualTo( Animal.Type.DOG ); + } + + @ProcessorTest + @WithClasses(Issue3673ExpressionMapper.class) + void shouldCorrectlyMapNestedPropertyExpression() { + + Animal cat = Issue3673ExpressionMapper.INSTANCE.map( + new Cat( new Details( "cat" ) ) + ); + + Animal dog = Issue3673ExpressionMapper.INSTANCE.map( + new Dog( new Details( "dog" ) ) + ); + + assertThat( cat ).isNotNull(); + assertThat( cat.getDetails() ).isNotNull(); + assertThat( cat.getDetails().getName() ).isEqualTo( "cat" ); + assertThat( cat.getDetails().getType() ).isEqualTo( Animal.Type.CAT ); + + assertThat( dog ).isNotNull(); + assertThat( dog.getDetails() ).isNotNull(); + assertThat( dog.getDetails().getName() ).isEqualTo( "dog" ); + assertThat( dog.getDetails().getType() ).isEqualTo( Animal.Type.DOG ); + } +} From c89b616f8c213f3210f3b5d5a6f7560d665b145b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 24 Aug 2024 12:22:37 +0200 Subject: [PATCH 438/597] #3668 Do not apply implicit mappings when using `SubclassExhaustiveStrategy#RUNTIME_EXCEPTION` and return type is abstract --- NEXT_RELEASE_CHANGELOG.md | 1 + .../ap/internal/model/BeanMappingMethod.java | 4 ++- .../mapstruct/ap/test/bugs/_3668/Child.java | 23 +++++++++++++ .../ap/test/bugs/_3668/ChildDto.java | 23 +++++++++++++ .../ap/test/bugs/_3668/ChildMapper.java | 31 +++++++++++++++++ .../ap/test/bugs/_3668/Issue3668Test.java | 30 +++++++++++++++++ .../mapstruct/ap/test/bugs/_3668/Parent.java | 33 +++++++++++++++++++ .../ap/test/bugs/_3668/ParentDto.java | 33 +++++++++++++++++++ .../ap/test/bugs/_3668/ParentMapper.java | 31 +++++++++++++++++ 9 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Child.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ChildDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ChildMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Issue3668Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Parent.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ParentDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ParentMapper.java diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index a3ea4e5026..438f4e3a6f 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -5,6 +5,7 @@ ### Bugs * Inverse Inheritance Strategy not working for ignored mappings only with target (#3652) +* Inconsistent ambiguous mapping method error when using `SubclassMapping`: generic vs raw types (#3668) * Fix regression when using `InheritInverseConfiguration` with nested target properties and reversing `target = "."` (#3670) ### Documentation diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 6535b07012..1bec5038c4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -295,7 +295,9 @@ else if ( !method.isUpdateMethod() ) { } } - boolean applyImplicitMappings = !mappingReferences.isRestrictToDefinedMappings(); + // If defined mappings should not be handled then we should not apply implicit mappings + boolean applyImplicitMappings = + shouldHandledDefinedMappings && !mappingReferences.isRestrictToDefinedMappings(); if ( applyImplicitMappings ) { applyImplicitMappings = beanMapping == null || !beanMapping.isignoreByDefault(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Child.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Child.java new file mode 100644 index 0000000000..3c10c4d470 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Child.java @@ -0,0 +1,23 @@ +/* + * 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._3668; + +public abstract class Child { + + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public static class ChildA extends Child { } + + public static class ChildB extends Child { } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ChildDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ChildDto.java new file mode 100644 index 0000000000..458cc57a9a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ChildDto.java @@ -0,0 +1,23 @@ +/* + * 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._3668; + +public abstract class ChildDto { + + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public static class ChildDtoA extends ChildDto { } + + public static class ChildDtoB extends ChildDto { } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ChildMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ChildMapper.java new file mode 100644 index 0000000000..c381a99fcd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ChildMapper.java @@ -0,0 +1,31 @@ +/* + * 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._3668; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; + +@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION) +public interface ChildMapper { + + @SubclassMapping(target = Child.ChildA.class, source = ChildDto.ChildDtoA.class) + @SubclassMapping(target = Child.ChildB.class, source = ChildDto.ChildDtoB.class) + Child toEntity(ChildDto childDto); + + @SubclassMapping(target = ChildDto.ChildDtoA.class, source = Child.ChildA.class) + @SubclassMapping(target = ChildDto.ChildDtoB.class, source = Child.ChildB.class) + ChildDto toDto(Child child); + + Child.ChildA toEntity(ChildDto.ChildDtoA childDto); + + ChildDto.ChildDtoA toDto(Child.ChildA child); + + Child.ChildB toEntity(ChildDto.ChildDtoB childDto); + + ChildDto.ChildDtoB toDto(Child.ChildB child); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Issue3668Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Issue3668Test.java new file mode 100644 index 0000000000..a35ac70dce --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Issue3668Test.java @@ -0,0 +1,30 @@ +/* + * 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._3668; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Filip Hrisafov + */ +@IssueKey("3668") +@WithClasses({ + Child.class, + ChildDto.class, + ChildMapper.class, + Parent.class, + ParentDto.class, + ParentMapper.class, +}) +class Issue3668Test { + + @ProcessorTest + void shouldCompile() { + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Parent.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Parent.java new file mode 100644 index 0000000000..900a7fa68b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/Parent.java @@ -0,0 +1,33 @@ +/* + * 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._3668; + +public abstract class Parent { + + private Long id; + + private T child; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public T getChild() { + return child; + } + + public void setChild(T child) { + this.child = child; + } + + public static class ParentA extends Parent { } + + public static class ParentB extends Parent { } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ParentDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ParentDto.java new file mode 100644 index 0000000000..f4736ceef0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ParentDto.java @@ -0,0 +1,33 @@ +/* + * 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._3668; + +public abstract class ParentDto { + + private Long id; + + private T child; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public T getChild() { + return child; + } + + public void setChild(T child) { + this.child = child; + } + + public static class ParentDtoA extends ParentDto { } + + public static class ParentDtoB extends ParentDto { } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ParentMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ParentMapper.java new file mode 100644 index 0000000000..484ddbc930 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3668/ParentMapper.java @@ -0,0 +1,31 @@ +/* + * 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._3668; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; + +@Mapper(uses = { ChildMapper.class }, subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION) +public interface ParentMapper { + + @SubclassMapping(target = Parent.ParentA.class, source = ParentDto.ParentDtoA.class) + @SubclassMapping(target = Parent.ParentB.class, source = ParentDto.ParentDtoB.class) + Parent toEntity(ParentDto parentDto); + + @SubclassMapping(target = ParentDto.ParentDtoA.class, source = Parent.ParentA.class) + @SubclassMapping(target = ParentDto.ParentDtoB.class, source = Parent.ParentB.class) + ParentDto toDto(Parent parent); + + Parent.ParentA toEntity(ParentDto.ParentDtoA parentDto); + + ParentDto.ParentDtoA toDto(Parent.ParentA parent); + + Parent.ParentB toEntity(ParentDto.ParentDtoB parentDto); + + ParentDto.ParentDtoB toDto(Parent.ParentB parent); + +} From 58dcb9d81387963caa2f74a8c4b836afaef471b9 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 28 Aug 2024 11:56:21 +0200 Subject: [PATCH 439/597] Update latest version and remove some obsolete badges --- readme.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/readme.md b/readme.md index 21d820dc58..6e5debbb78 100644 --- a/readme.md +++ b/readme.md @@ -1,14 +1,11 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.5.5.Final-blue.svg)](https://search.maven.org/search?q=g:org.mapstruct%20AND%20v:1.*.Final) -[![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](https://search.maven.org/search?q=g:org.mapstruct) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.6.0-blue.svg)](https://central.sonatype.com/search?q=g:org.mapstruct%20v:1.6.0) +[![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](https://central.sonatype.com/search?q=g:org.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/main/LICENSE.txt) [![Build Status](https://github.com/mapstruct/mapstruct/workflows/CI/badge.svg?branch=main)](https://github.com/mapstruct/mapstruct/actions?query=branch%3Amain+workflow%3ACI) [![Coverage Status](https://img.shields.io/codecov/c/github/mapstruct/mapstruct.svg)](https://codecov.io/gh/mapstruct/mapstruct/tree/main) -[![Gitter](https://img.shields.io/gitter/room/mapstruct/mapstruct.svg)](https://gitter.im/mapstruct/mapstruct-users) -[![Code Quality: Java](https://img.shields.io/lgtm/grade/java/g/mapstruct/mapstruct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/mapstruct/mapstruct/context:java) -[![Total Alerts](https://img.shields.io/lgtm/alerts/g/mapstruct/mapstruct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/mapstruct/mapstruct/alerts) * [What is MapStruct?](#what-is-mapstruct) * [Requirements](#requirements) @@ -68,7 +65,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.5.5.Final + 1.6.0 ... @@ -84,10 +81,10 @@ For Maven-based projects, add the following to your POM file in order to use Map org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.13.0 - 1.8 - 1.8 + 17 + 1<7/target> org.mapstruct @@ -114,10 +111,10 @@ plugins { dependencies { ... - implementation 'org.mapstruct:mapstruct:1.5.5.Final' + implementation 'org.mapstruct:mapstruct:1.6.0' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.0' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.6.0' // if you are using mapstruct in test code } ... ``` From c6010c917a6ee239c5c0d949b7460a6a7451df1f Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Sat, 31 Aug 2024 16:31:51 +0200 Subject: [PATCH 440/597] Fix typo in readme Maven plugin config --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 6e5debbb78..c0f798af83 100644 --- a/readme.md +++ b/readme.md @@ -84,7 +84,7 @@ For Maven-based projects, add the following to your POM file in order to use Map 3.13.0 17 - 1<7/target> + 17 org.mapstruct From 1e89d7497b1327c91cf782b80cb484adfdf87192 Mon Sep 17 00:00:00 2001 From: Obolrom <65775868+Obolrom@users.noreply.github.com> Date: Mon, 2 Sep 2024 09:44:17 +0300 Subject: [PATCH 441/597] Fix method name typo (#3622) --- .../org/mapstruct/ap/internal/model/BeanMappingMethod.java | 4 ++-- .../ap/internal/model/source/BeanMappingOptions.java | 2 +- .../ap/internal/processor/MapperCreationProcessor.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 1bec5038c4..4ccf2bb49a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -299,7 +299,7 @@ else if ( !method.isUpdateMethod() ) { boolean applyImplicitMappings = shouldHandledDefinedMappings && !mappingReferences.isRestrictToDefinedMappings(); if ( applyImplicitMappings ) { - applyImplicitMappings = beanMapping == null || !beanMapping.isignoreByDefault(); + applyImplicitMappings = beanMapping == null || !beanMapping.isIgnoredByDefault(); } if ( applyImplicitMappings ) { @@ -1699,7 +1699,7 @@ private ReportingPolicyGem getUnmappedTargetPolicy() { return ReportingPolicyGem.IGNORE; } // If we have ignoreByDefault = true, unprocessed target properties are not an issue. - if ( method.getOptions().getBeanMapping().isignoreByDefault() ) { + if ( method.getOptions().getBeanMapping().isIgnoredByDefault() ) { return ReportingPolicyGem.IGNORE; } if ( method.getOptions().getBeanMapping() != null ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index bc19860bb5..ac27dfff00 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -223,7 +223,7 @@ public SelectionParameters getSelectionParameters() { return selectionParameters; } - public boolean isignoreByDefault() { + public boolean isIgnoredByDefault() { return Optional.ofNullable( beanMapping ).map( BeanMappingGem::ignoreByDefault ) .map( GemValue::get ) .orElse( false ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 24dd528f6b..5fc1b07823 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -565,7 +565,7 @@ else if ( applicableReversePrototypeMethods.size() > 1 ) { } // @BeanMapping( ignoreByDefault = true ) - if ( mappingOptions.getBeanMapping() != null && mappingOptions.getBeanMapping().isignoreByDefault() ) { + if ( mappingOptions.getBeanMapping() != null && mappingOptions.getBeanMapping().isIgnoredByDefault() ) { mappingOptions.applyIgnoreAll( method, typeFactory, mappingContext.getMessager() ); } From 23f4802374ae8e4703e8f127d85e332bf2e682fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B8=B0=EC=84=9C?= <81108344+rlarltj@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:05:01 +0900 Subject: [PATCH 442/597] Fix method name typo (#3691) --- .../java/org/mapstruct/ap/internal/util/NativeTypesTest.java | 4 ++-- .../mapstruct/ap/test/accessibility/AccessibilityTest.java | 2 +- .../ap/test/accessibility/DefaultSourceTargetMapperIfc.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java b/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java index 9c68a1de45..cd96592839 100644 --- a/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/util/NativeTypesTest.java @@ -122,7 +122,7 @@ public void testIntegerLiteralFromJLS() { .isNotNull(); // most negative int: dec / octal / int / binary - // NOTE parseInt should be changed to parseUnsignedInt in Java, than the - sign can disssapear (java8) + // NOTE parseInt should be changed to parseUnsignedInt in Java, than the - sign can dissapear (java8) // and the function will be true to what the compiler shows. assertThat( getLiteral( int.class.getCanonicalName(), "-2147483648" ) ).isNotNull(); assertThat( getLiteral( int.class.getCanonicalName(), "0x8000_0000" ) ).isNotNull(); @@ -177,7 +177,7 @@ public void testIntegerLiteralFromJLS() { * The following example shows other ways you can use the underscore in numeric literals: */ @Test - public void testFloatingPoingLiteralFromJLS() { + public void testFloatingPointLiteralFromJLS() { // The largest positive finite literal of type float is 3.4028235e38f. assertThat( getLiteral( float.class.getCanonicalName(), "3.4028235e38f" ) ).isNotNull(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/AccessibilityTest.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/AccessibilityTest.java index d338cadedd..bab9ca9b71 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/AccessibilityTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/AccessibilityTest.java @@ -41,7 +41,7 @@ public void testGeneratedModifiersFromInterfaceAreCorrect() throws Exception { assertTrue( isDefault( defaultFromIfc.getModifiers() ) ); - assertTrue( isPublic( modifiersFor( defaultFromIfc, "implicitlyPublicSoureToTarget" ) ) ); + assertTrue( isPublic( modifiersFor( defaultFromIfc, "implicitlyPublicSourceToTarget" ) ) ); } private static Class loadForMapper(Class mapper) throws ClassNotFoundException { diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/DefaultSourceTargetMapperIfc.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/DefaultSourceTargetMapperIfc.java index 84e9aebbbc..027d60b9ec 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/DefaultSourceTargetMapperIfc.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/DefaultSourceTargetMapperIfc.java @@ -12,5 +12,5 @@ */ @Mapper interface DefaultSourceTargetMapperIfc { - Target implicitlyPublicSoureToTarget(Source source); + Target implicitlyPublicSourceToTarget(Source source); } From 4d9894ba25ba4e17c76211409f951f4cce956b3e Mon Sep 17 00:00:00 2001 From: Obolrom <65775868+Obolrom@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:26:48 +0300 Subject: [PATCH 443/597] #3113 Use LinkedHashSet, LinkedHashSet new factory methods for java >= 19 --- .../model/common/ImplementationType.java | 25 +- .../ap/internal/model/common/TypeFactory.java | 26 +- .../DefaultModelElementProcessorContext.java | 3 +- .../processor/DefaultVersionInformation.java | 7 + .../internal/version/VersionInformation.java | 2 + .../ap/internal/model/IterableCreation.ftl | 12 +- .../testutil/runner/CompilationRequest.java | 4 + .../ap/testutil/runner/GeneratedSource.java | 21 +- .../test/bugs/_1453/Issue1453MapperImpl.java | 139 ++++++++++ .../bugs/_3591/ContainerBeanMapperImpl.java | 85 ++++++ .../DomainDtoWithNcvsAlwaysMapperImpl.java | 214 +++++++++++++++ .../DomainDtoWithNvmsDefaultMapperImpl.java | 245 +++++++++++++++++ .../_913/DomainDtoWithNvmsNullMapperImpl.java | 248 +++++++++++++++++ .../DomainDtoWithPresenceCheckMapperImpl.java | 214 +++++++++++++++ .../SourceTargetMapperImpl.java | 259 ++++++++++++++++++ .../updatemethods/CompanyMapper1Impl.java | 120 ++++++++ 16 files changed, 1605 insertions(+), 19 deletions(-) create mode 100644 processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java create mode 100644 processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/updatemethods/CompanyMapper1Impl.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ImplementationType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ImplementationType.java index 45b73c492f..af8c7ecfb3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ImplementationType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ImplementationType.java @@ -16,23 +16,34 @@ public class ImplementationType { private final Type type; private final boolean initialCapacityConstructor; private final boolean loadFactorAdjustment; + private final String factoryMethodName; - private ImplementationType(Type type, boolean initialCapacityConstructor, boolean loadFactorAdjustment) { + private ImplementationType( + Type type, + boolean initialCapacityConstructor, + boolean loadFactorAdjustment, + String factoryMethodName + ) { this.type = type; this.initialCapacityConstructor = initialCapacityConstructor; this.loadFactorAdjustment = loadFactorAdjustment; + this.factoryMethodName = factoryMethodName; } public static ImplementationType withDefaultConstructor(Type type) { - return new ImplementationType( type, false, false ); + return new ImplementationType( type, false, false, null ); } public static ImplementationType withInitialCapacity(Type type) { - return new ImplementationType( type, true, false ); + return new ImplementationType( type, true, false, null ); } public static ImplementationType withLoadFactorAdjustment(Type type) { - return new ImplementationType( type, true, true ); + return new ImplementationType( type, true, true, null ); + } + + public static ImplementationType withFactoryMethod(Type type, String factoryMethodName) { + return new ImplementationType( type, true, false, factoryMethodName ); } /** @@ -44,7 +55,7 @@ public static ImplementationType withLoadFactorAdjustment(Type type) { * @return a new implementation type with the given {@code type} */ public ImplementationType createNew(Type type) { - return new ImplementationType( type, initialCapacityConstructor, loadFactorAdjustment ); + return new ImplementationType( type, initialCapacityConstructor, loadFactorAdjustment, factoryMethodName ); } /** @@ -71,4 +82,8 @@ public boolean hasInitialCapacityConstructor() { public boolean isLoadFactorAdjustment() { return loadFactorAdjustment; } + + public String getFactoryMethodName() { + return factoryMethodName; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index f28e0c687e..d65070426b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -38,12 +38,11 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.util.AnnotationProcessingException; import org.mapstruct.ap.internal.util.Collections; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.Extractor; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.JavaStreamConstants; @@ -51,13 +50,16 @@ import org.mapstruct.ap.internal.util.NativeTypes; import org.mapstruct.ap.internal.util.RoundContext; import org.mapstruct.ap.internal.util.Strings; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.util.accessor.Accessor; +import org.mapstruct.ap.internal.version.VersionInformation; import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; import org.mapstruct.ap.spi.BuilderInfo; import org.mapstruct.ap.spi.MoreThanOneBuilderCreationMethodException; import org.mapstruct.ap.spi.TypeHierarchyErroneousException; import static org.mapstruct.ap.internal.model.common.ImplementationType.withDefaultConstructor; +import static org.mapstruct.ap.internal.model.common.ImplementationType.withFactoryMethod; import static org.mapstruct.ap.internal.model.common.ImplementationType.withInitialCapacity; import static org.mapstruct.ap.internal.model.common.ImplementationType.withLoadFactorAdjustment; @@ -82,6 +84,8 @@ public class TypeFactory { sb.append( ')' ); return sb.toString(); }; + private static final String LINKED_HASH_SET_FACTORY_METHOD_NAME = "newLinkedHashSet"; + private static final String LINKED_HASH_MAP_FACTORY_METHOD_NAME = "newLinkedHashMap"; private final ElementUtils elementUtils; private final TypeUtils typeUtils; @@ -100,7 +104,8 @@ public class TypeFactory { private final boolean loggingVerbose; public TypeFactory(ElementUtils elementUtils, TypeUtils typeUtils, FormattingMessager messager, - RoundContext roundContext, Map notToBeImportedTypes, boolean loggingVerbose) { + RoundContext roundContext, Map notToBeImportedTypes, boolean loggingVerbose, + VersionInformation versionInformation) { this.elementUtils = elementUtils; this.typeUtils = typeUtils; this.messager = messager; @@ -118,11 +123,22 @@ public TypeFactory(ElementUtils elementUtils, TypeUtils typeUtils, FormattingMes implementationTypes.put( Collection.class.getName(), withInitialCapacity( getType( ArrayList.class ) ) ); implementationTypes.put( List.class.getName(), withInitialCapacity( getType( ArrayList.class ) ) ); - implementationTypes.put( Set.class.getName(), withLoadFactorAdjustment( getType( LinkedHashSet.class ) ) ); + boolean sourceVersionAtLeast19 = versionInformation.isSourceVersionAtLeast19(); + implementationTypes.put( + Set.class.getName(), + sourceVersionAtLeast19 ? + withFactoryMethod( getType( LinkedHashSet.class ), LINKED_HASH_SET_FACTORY_METHOD_NAME ) : + withLoadFactorAdjustment( getType( LinkedHashSet.class ) ) + ); implementationTypes.put( SortedSet.class.getName(), withDefaultConstructor( getType( TreeSet.class ) ) ); implementationTypes.put( NavigableSet.class.getName(), withDefaultConstructor( getType( TreeSet.class ) ) ); - implementationTypes.put( Map.class.getName(), withLoadFactorAdjustment( getType( LinkedHashMap.class ) ) ); + implementationTypes.put( + Map.class.getName(), + sourceVersionAtLeast19 ? + withFactoryMethod( getType( LinkedHashMap.class ), LINKED_HASH_MAP_FACTORY_METHOD_NAME ) : + withLoadFactorAdjustment( getType( LinkedHashMap.class ) ) + ); implementationTypes.put( SortedMap.class.getName(), withDefaultConstructor( getType( TreeMap.class ) ) ); implementationTypes.put( NavigableMap.class.getName(), withDefaultConstructor( getType( TreeMap.class ) ) ); implementationTypes.put( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java index 51ea5dd786..8ac1cc067e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java @@ -62,7 +62,8 @@ public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvir messager, roundContext, notToBeImported, - options.isVerbose() + options.isVerbose(), + versionInformation ); this.options = options; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java index c4baf1bf5f..055bfe6095 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java @@ -41,6 +41,7 @@ public class DefaultVersionInformation implements VersionInformation { private final String runtimeVendor; private final String compiler; private final boolean sourceVersionAtLeast9; + private final boolean sourceVersionAtLeast19; private final boolean eclipseJDT; private final boolean javac; @@ -53,6 +54,7 @@ public class DefaultVersionInformation implements VersionInformation { this.javac = compiler.startsWith( COMPILER_NAME_JAVAC ); // If the difference between the source version and RELEASE_6 is more that 2 than we are at least on 9 this.sourceVersionAtLeast9 = sourceVersion.compareTo( SourceVersion.RELEASE_6 ) > 2; + this.sourceVersionAtLeast19 = sourceVersion.compareTo( SourceVersion.RELEASE_6 ) > 12; } @Override @@ -80,6 +82,11 @@ public boolean isSourceVersionAtLeast9() { return sourceVersionAtLeast9; } + @Override + public boolean isSourceVersionAtLeast19() { + return sourceVersionAtLeast19; + } + @Override public boolean isEclipseJDTCompiler() { return eclipseJDT; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java b/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java index 94e3520ad0..5e1972fcae 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java @@ -21,6 +21,8 @@ public interface VersionInformation { boolean isSourceVersionAtLeast9(); + boolean isSourceVersionAtLeast19(); + boolean isEclipseJDTCompiler(); boolean isJavacCompiler(); diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableCreation.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableCreation.ftl index d49397a985..d083bd113d 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableCreation.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableCreation.ftl @@ -11,13 +11,15 @@ <@includeModel object=factoryMethod targetType=resultType/> <#elseif enumSet> EnumSet.noneOf( <@includeModel object=enumSetElementType raw=true/>.class ) - <#else> - new - <#if resultType.implementationType??> - <@includeModel object=resultType.implementationType/><#if ext.useSizeIfPossible?? && ext.useSizeIfPossible && canUseSize>( <@sizeForCreation /> )<#else>() + <#elseif resultType.implementation??> + <#if resultType.implementation.factoryMethodName?? && ext.useSizeIfPossible?? && ext.useSizeIfPossible && canUseSize> + <@includeModel object=resultType.implementationType raw=true />.${resultType.implementation.factoryMethodName}( <@sizeForCreation /> ) <#else> - <@includeModel object=resultType/>() + new <@includeModel object=resultType.implementationType/><#if ext.useSizeIfPossible?? && ext.useSizeIfPossible && canUseSize>( <@sizeForCreation /> )<#else>() + <#else> + new <@includeModel object=resultType/>() + <#macro sizeForCreation> <@compress single_line=true> diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java index 60e9a30072..9f1b78caa0 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java @@ -76,4 +76,8 @@ public Map, Class> getServices() { public Collection getTestDependencies() { return testDependencies; } + + public Compiler getCompiler() { + return compiler; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java index c0993ea098..18a53b4971 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java @@ -44,12 +44,15 @@ public class GeneratedSource implements BeforeTestExecutionCallback, AfterTestEx */ private ThreadLocal sourceOutputDir = new ThreadLocal<>(); + private Compiler compiler; + private List> fixturesFor = new ArrayList<>(); @Override public void beforeTestExecution(ExtensionContext context) throws Exception { CompilationRequest compilationRequest = context.getStore( NAMESPACE ) .get( context.getUniqueId() + "-compilationRequest", CompilationRequest.class ); + this.compiler = compilationRequest.getCompiler(); setSourceOutputDir( context.getStore( NAMESPACE ) .get( compilationRequest, CompilationCache.class ) .getLastSourceOutputDir() ); @@ -118,13 +121,13 @@ public JavaFileAssert forJavaFile(String path) { private void handleFixtureComparison() throws UnsupportedEncodingException { for ( Class fixture : fixturesFor ) { - String expectedFixture = FIXTURES_ROOT + getMapperName( fixture ); - URL expectedFile = getClass().getClassLoader().getResource( expectedFixture ); + String fixtureName = getMapperName( fixture ); + URL expectedFile = getExpectedResource( fixtureName ); if ( expectedFile == null ) { fail( String.format( "No reference file could be found for Mapper %s. You should create a file %s", fixture.getName(), - expectedFixture + FIXTURES_ROOT + fixtureName ) ); } else { @@ -135,4 +138,16 @@ private void handleFixtureComparison() throws UnsupportedEncodingException { } } + + private URL getExpectedResource( String fixtureName ) { + ClassLoader classLoader = getClass().getClassLoader(); + for ( int version = Runtime.version().feature(); version >= 11 && compiler != Compiler.ECLIPSE; version-- ) { + URL resource = classLoader.getResource( FIXTURES_ROOT + "/" + version + "/" + fixtureName ); + if ( resource != null ) { + return resource; + } + } + + return classLoader.getResource( FIXTURES_ROOT + fixtureName ); + } } diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java new file mode 100644 index 0000000000..78c3bea0d7 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_1453/Issue1453MapperImpl.java @@ -0,0 +1,139 @@ +/* + * 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._1453; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-06-18T14:48:39+0200", + comments = "version: , compiler: javac, environment: Java 21.0.2 (Eclipse Adoptium)" +) +public class Issue1453MapperImpl implements Issue1453Mapper { + + @Override + public AuctionDto map(Auction auction) { + if ( auction == null ) { + return null; + } + + AuctionDto auctionDto = new AuctionDto(); + + auctionDto.setPayments( paymentListToPaymentDtoList( auction.getPayments() ) ); + auctionDto.setOtherPayments( paymentListToPaymentDtoList( auction.getOtherPayments() ) ); + auctionDto.setMapPayments( paymentPaymentMapToPaymentDtoPaymentDtoMap( auction.getMapPayments() ) ); + auctionDto.setMapSuperPayments( paymentPaymentMapToPaymentDtoPaymentDtoMap( auction.getMapSuperPayments() ) ); + + return auctionDto; + } + + @Override + public List mapExtend(List auctions) { + if ( auctions == null ) { + return null; + } + + List list = new ArrayList( auctions.size() ); + for ( Auction auction : auctions ) { + list.add( map( auction ) ); + } + + return list; + } + + @Override + public List mapSuper(List auctions) { + if ( auctions == null ) { + return null; + } + + List list = new ArrayList( auctions.size() ); + for ( Auction auction : auctions ) { + list.add( map( auction ) ); + } + + return list; + } + + @Override + public Map mapExtend(Map auctions) { + if ( auctions == null ) { + return null; + } + + Map map = LinkedHashMap.newLinkedHashMap( auctions.size() ); + + for ( java.util.Map.Entry entry : auctions.entrySet() ) { + AuctionDto key = map( entry.getKey() ); + AuctionDto value = map( entry.getValue() ); + map.put( key, value ); + } + + return map; + } + + @Override + public Map mapSuper(Map auctions) { + if ( auctions == null ) { + return null; + } + + Map map = LinkedHashMap.newLinkedHashMap( auctions.size() ); + + for ( java.util.Map.Entry entry : auctions.entrySet() ) { + AuctionDto key = map( entry.getKey() ); + AuctionDto value = map( entry.getValue() ); + map.put( key, value ); + } + + return map; + } + + protected PaymentDto paymentToPaymentDto(Payment payment) { + if ( payment == null ) { + return null; + } + + PaymentDto paymentDto = new PaymentDto(); + + paymentDto.setPrice( payment.getPrice() ); + + return paymentDto; + } + + protected List paymentListToPaymentDtoList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( Payment payment : list ) { + list1.add( paymentToPaymentDto( payment ) ); + } + + return list1; + } + + protected Map paymentPaymentMapToPaymentDtoPaymentDtoMap(Map map) { + if ( map == null ) { + return null; + } + + Map map1 = LinkedHashMap.newLinkedHashMap( map.size() ); + + for ( java.util.Map.Entry entry : map.entrySet() ) { + PaymentDto key = paymentToPaymentDto( entry.getKey() ); + PaymentDto value = paymentToPaymentDto( entry.getValue() ); + map1.put( key, value ); + } + + return map1; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapperImpl.java new file mode 100644 index 0000000000..af3d293f7d --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_3591/ContainerBeanMapperImpl.java @@ -0,0 +1,85 @@ +/* + * 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._3591; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Stream; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-05-25T14:23:23+0200", + comments = "version: , compiler: javac, environment: Java 21.0.2 (Eclipse Adoptium)" +) +public class ContainerBeanMapperImpl implements ContainerBeanMapper { + + @Override + public ContainerBeanDto mapWithMapMapping(ContainerBean containerBean, ContainerBeanDto containerBeanDto) { + if ( containerBean == null ) { + return containerBeanDto; + } + + if ( containerBeanDto.getBeanMap() != null ) { + Map map = stringContainerBeanMapToStringContainerBeanDtoMap( containerBean.getBeanMap() ); + if ( map != null ) { + containerBeanDto.getBeanMap().clear(); + containerBeanDto.getBeanMap().putAll( map ); + } + else { + containerBeanDto.setBeanMap( null ); + } + } + else { + Map map = stringContainerBeanMapToStringContainerBeanDtoMap( containerBean.getBeanMap() ); + if ( map != null ) { + containerBeanDto.setBeanMap( map ); + } + } + containerBeanDto.setBeanStream( containerBeanStreamToContainerBeanDtoStream( containerBean.getBeanStream() ) ); + containerBeanDto.setValue( containerBean.getValue() ); + + return containerBeanDto; + } + + protected Stream containerBeanStreamToContainerBeanDtoStream(Stream stream) { + if ( stream == null ) { + return null; + } + + return stream.map( containerBean -> containerBeanToContainerBeanDto( containerBean ) ); + } + + protected ContainerBeanDto containerBeanToContainerBeanDto(ContainerBean containerBean) { + if ( containerBean == null ) { + return null; + } + + ContainerBeanDto containerBeanDto = new ContainerBeanDto(); + + containerBeanDto.setBeanMap( stringContainerBeanMapToStringContainerBeanDtoMap( containerBean.getBeanMap() ) ); + containerBeanDto.setBeanStream( containerBeanStreamToContainerBeanDtoStream( containerBean.getBeanStream() ) ); + containerBeanDto.setValue( containerBean.getValue() ); + + return containerBeanDto; + } + + protected Map stringContainerBeanMapToStringContainerBeanDtoMap(Map map) { + if ( map == null ) { + return null; + } + + Map map1 = LinkedHashMap.newLinkedHashMap( map.size() ); + + for ( java.util.Map.Entry entry : map.entrySet() ) { + String key = entry.getKey(); + ContainerBeanDto value = containerBeanToContainerBeanDto( entry.getValue() ); + map1.put( key, value ); + } + + return map1; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java new file mode 100644 index 0000000000..d5cfd24723 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java @@ -0,0 +1,214 @@ +/* + * 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._913; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-06-19T11:20:01+0300", + comments = "version: , compiler: javac, environment: Java 21.0.2 (Amazon.com Inc.)" +) +public class DomainDtoWithNcvsAlwaysMapperImpl implements DomainDtoWithNcvsAlwaysMapper { + + private final Helper helper = new Helper(); + + @Override + public Domain create(DtoWithPresenceCheck source) { + if ( source == null ) { + return null; + } + + Domain domain = createNullDomain(); + + if ( source.hasStrings() ) { + List list = source.getStrings(); + domain.setStrings( new LinkedHashSet( list ) ); + } + if ( source.hasStrings() ) { + domain.setLongs( stringListToLongSet( source.getStrings() ) ); + } + if ( source.hasStringsInitialized() ) { + List list1 = source.getStringsInitialized(); + domain.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + if ( source.hasStringsInitialized() ) { + domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + } + if ( source.hasStringsWithDefault() ) { + List list2 = source.getStringsWithDefault(); + domain.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + domain.setStringsWithDefault( helper.toList( "3" ) ); + } + + return domain; + } + + @Override + public void update(DtoWithPresenceCheck source, Domain target) { + if ( source == null ) { + return; + } + + if ( target.getStrings() != null ) { + if ( source.hasStrings() ) { + target.getStrings().clear(); + target.getStrings().addAll( source.getStrings() ); + } + } + else { + if ( source.hasStrings() ) { + List list = source.getStrings(); + target.setStrings( new LinkedHashSet( list ) ); + } + } + if ( target.getLongs() != null ) { + if ( source.hasStrings() ) { + target.getLongs().clear(); + target.getLongs().addAll( stringListToLongSet( source.getStrings() ) ); + } + } + else { + if ( source.hasStrings() ) { + target.setLongs( stringListToLongSet( source.getStrings() ) ); + } + } + if ( target.getStringsInitialized() != null ) { + if ( source.hasStringsInitialized() ) { + target.getStringsInitialized().clear(); + target.getStringsInitialized().addAll( source.getStringsInitialized() ); + } + } + else { + if ( source.hasStringsInitialized() ) { + List list1 = source.getStringsInitialized(); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + } + if ( target.getLongsInitialized() != null ) { + if ( source.hasStringsInitialized() ) { + target.getLongsInitialized().clear(); + target.getLongsInitialized().addAll( stringListToLongSet( source.getStringsInitialized() ) ); + } + } + else { + if ( source.hasStringsInitialized() ) { + target.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + } + } + if ( target.getStringsWithDefault() != null ) { + if ( source.hasStringsWithDefault() ) { + target.getStringsWithDefault().clear(); + target.getStringsWithDefault().addAll( source.getStringsWithDefault() ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + else { + if ( source.hasStringsWithDefault() ) { + List list2 = source.getStringsWithDefault(); + target.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + } + + @Override + public Domain updateWithReturn(DtoWithPresenceCheck source, Domain target) { + if ( source == null ) { + return target; + } + + if ( target.getStrings() != null ) { + if ( source.hasStrings() ) { + target.getStrings().clear(); + target.getStrings().addAll( source.getStrings() ); + } + } + else { + if ( source.hasStrings() ) { + List list = source.getStrings(); + target.setStrings( new LinkedHashSet( list ) ); + } + } + if ( target.getLongs() != null ) { + if ( source.hasStrings() ) { + target.getLongs().clear(); + target.getLongs().addAll( stringListToLongSet( source.getStrings() ) ); + } + } + else { + if ( source.hasStrings() ) { + target.setLongs( stringListToLongSet( source.getStrings() ) ); + } + } + if ( target.getStringsInitialized() != null ) { + if ( source.hasStringsInitialized() ) { + target.getStringsInitialized().clear(); + target.getStringsInitialized().addAll( source.getStringsInitialized() ); + } + } + else { + if ( source.hasStringsInitialized() ) { + List list1 = source.getStringsInitialized(); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + } + if ( target.getLongsInitialized() != null ) { + if ( source.hasStringsInitialized() ) { + target.getLongsInitialized().clear(); + target.getLongsInitialized().addAll( stringListToLongSet( source.getStringsInitialized() ) ); + } + } + else { + if ( source.hasStringsInitialized() ) { + target.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + } + } + if ( target.getStringsWithDefault() != null ) { + if ( source.hasStringsWithDefault() ) { + target.getStringsWithDefault().clear(); + target.getStringsWithDefault().addAll( source.getStringsWithDefault() ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + else { + if ( source.hasStringsWithDefault() ) { + List list2 = source.getStringsWithDefault(); + target.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + + return target; + } + + protected Set stringListToLongSet(List list) { + if ( list == null ) { + return null; + } + + Set set = LinkedHashSet.newLinkedHashSet( list.size() ); + for ( String string : list ) { + set.add( Long.parseLong( string ) ); + } + + return set; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java new file mode 100644 index 0000000000..76de94378b --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java @@ -0,0 +1,245 @@ +/* + * 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._913; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-06-18T14:48:39+0200", + comments = "version: , compiler: javac, environment: Java 21.0.2 (Eclipse Adoptium)" +) +public class DomainDtoWithNvmsDefaultMapperImpl implements DomainDtoWithNvmsDefaultMapper { + + private final Helper helper = new Helper(); + + @Override + public Domain create(Dto source) { + + Domain domain = new Domain(); + + if ( source != null ) { + List list = source.getStrings(); + if ( list != null ) { + domain.setStrings( new LinkedHashSet( list ) ); + } + domain.setLongs( stringListToLongSet( source.getStrings() ) ); + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + domain.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + domain.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + domain.setStringsWithDefault( helper.toList( "3" ) ); + } + } + + return domain; + } + + @Override + public void update(Dto source, Domain target) { + + if ( source != null ) { + if ( target.getStrings() != null ) { + List list = source.getStrings(); + if ( list != null ) { + target.getStrings().clear(); + target.getStrings().addAll( list ); + } + else { + target.setStrings( new LinkedHashSet() ); + } + } + else { + List list = source.getStrings(); + if ( list != null ) { + target.setStrings( new LinkedHashSet( list ) ); + } + } + if ( target.getLongs() != null ) { + Set set = stringListToLongSet( source.getStrings() ); + if ( set != null ) { + target.getLongs().clear(); + target.getLongs().addAll( set ); + } + else { + target.setLongs( new LinkedHashSet() ); + } + } + else { + Set set = stringListToLongSet( source.getStrings() ); + if ( set != null ) { + target.setLongs( set ); + } + } + if ( target.getStringsInitialized() != null ) { + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + target.getStringsInitialized().clear(); + target.getStringsInitialized().addAll( list1 ); + } + else { + target.setStringsInitialized( new LinkedHashSet() ); + } + } + else { + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + target.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + } + if ( target.getLongsInitialized() != null ) { + Set set1 = stringListToLongSet( source.getStringsInitialized() ); + if ( set1 != null ) { + target.getLongsInitialized().clear(); + target.getLongsInitialized().addAll( set1 ); + } + else { + target.setLongsInitialized( new LinkedHashSet() ); + } + } + else { + Set set1 = stringListToLongSet( source.getStringsInitialized() ); + if ( set1 != null ) { + target.setLongsInitialized( set1 ); + } + } + if ( target.getStringsWithDefault() != null ) { + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + target.getStringsWithDefault().clear(); + target.getStringsWithDefault().addAll( list2 ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + else { + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + target.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + } + } + + @Override + public Domain updateWithReturn(Dto source, Domain target) { + + if ( source != null ) { + if ( target.getStrings() != null ) { + List list = source.getStrings(); + if ( list != null ) { + target.getStrings().clear(); + target.getStrings().addAll( list ); + } + else { + target.setStrings( new LinkedHashSet() ); + } + } + else { + List list = source.getStrings(); + if ( list != null ) { + target.setStrings( new LinkedHashSet( list ) ); + } + } + if ( target.getLongs() != null ) { + Set set = stringListToLongSet( source.getStrings() ); + if ( set != null ) { + target.getLongs().clear(); + target.getLongs().addAll( set ); + } + else { + target.setLongs( new LinkedHashSet() ); + } + } + else { + Set set = stringListToLongSet( source.getStrings() ); + if ( set != null ) { + target.setLongs( set ); + } + } + if ( target.getStringsInitialized() != null ) { + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + target.getStringsInitialized().clear(); + target.getStringsInitialized().addAll( list1 ); + } + else { + target.setStringsInitialized( new LinkedHashSet() ); + } + } + else { + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + target.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + } + if ( target.getLongsInitialized() != null ) { + Set set1 = stringListToLongSet( source.getStringsInitialized() ); + if ( set1 != null ) { + target.getLongsInitialized().clear(); + target.getLongsInitialized().addAll( set1 ); + } + else { + target.setLongsInitialized( new LinkedHashSet() ); + } + } + else { + Set set1 = stringListToLongSet( source.getStringsInitialized() ); + if ( set1 != null ) { + target.setLongsInitialized( set1 ); + } + } + if ( target.getStringsWithDefault() != null ) { + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + target.getStringsWithDefault().clear(); + target.getStringsWithDefault().addAll( list2 ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + else { + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + target.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + } + + return target; + } + + protected Set stringListToLongSet(List list) { + if ( list == null ) { + return new LinkedHashSet(); + } + + Set set = LinkedHashSet.newLinkedHashSet( list.size() ); + for ( String string : list ) { + set.add( Long.parseLong( string ) ); + } + + return set; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java new file mode 100644 index 0000000000..c61c2c68e8 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java @@ -0,0 +1,248 @@ +/* + * 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._913; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-06-18T14:48:39+0200", + comments = "version: , compiler: javac, environment: Java 21.0.2 (Eclipse Adoptium)" +) +public class DomainDtoWithNvmsNullMapperImpl implements DomainDtoWithNvmsNullMapper { + + private final Helper helper = new Helper(); + + @Override + public Domain create(Dto source) { + if ( source == null ) { + return null; + } + + Domain domain = createNullDomain(); + + List list = source.getStrings(); + if ( list != null ) { + domain.setStrings( new LinkedHashSet( list ) ); + } + domain.setLongs( stringListToLongSet( source.getStrings() ) ); + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + domain.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + domain.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + domain.setStringsWithDefault( helper.toList( "3" ) ); + } + + return domain; + } + + @Override + public void update(Dto source, Domain target) { + if ( source == null ) { + return; + } + + if ( target.getStrings() != null ) { + List list = source.getStrings(); + if ( list != null ) { + target.getStrings().clear(); + target.getStrings().addAll( list ); + } + else { + target.setStrings( null ); + } + } + else { + List list = source.getStrings(); + if ( list != null ) { + target.setStrings( new LinkedHashSet( list ) ); + } + } + if ( target.getLongs() != null ) { + Set set = stringListToLongSet( source.getStrings() ); + if ( set != null ) { + target.getLongs().clear(); + target.getLongs().addAll( set ); + } + else { + target.setLongs( null ); + } + } + else { + Set set = stringListToLongSet( source.getStrings() ); + if ( set != null ) { + target.setLongs( set ); + } + } + if ( target.getStringsInitialized() != null ) { + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + target.getStringsInitialized().clear(); + target.getStringsInitialized().addAll( list1 ); + } + else { + target.setStringsInitialized( null ); + } + } + else { + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + target.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + } + if ( target.getLongsInitialized() != null ) { + Set set1 = stringListToLongSet( source.getStringsInitialized() ); + if ( set1 != null ) { + target.getLongsInitialized().clear(); + target.getLongsInitialized().addAll( set1 ); + } + else { + target.setLongsInitialized( null ); + } + } + else { + Set set1 = stringListToLongSet( source.getStringsInitialized() ); + if ( set1 != null ) { + target.setLongsInitialized( set1 ); + } + } + if ( target.getStringsWithDefault() != null ) { + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + target.getStringsWithDefault().clear(); + target.getStringsWithDefault().addAll( list2 ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + else { + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + target.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + } + + @Override + public Domain updateWithReturn(Dto source, Domain target) { + if ( source == null ) { + return target; + } + + if ( target.getStrings() != null ) { + List list = source.getStrings(); + if ( list != null ) { + target.getStrings().clear(); + target.getStrings().addAll( list ); + } + else { + target.setStrings( null ); + } + } + else { + List list = source.getStrings(); + if ( list != null ) { + target.setStrings( new LinkedHashSet( list ) ); + } + } + if ( target.getLongs() != null ) { + Set set = stringListToLongSet( source.getStrings() ); + if ( set != null ) { + target.getLongs().clear(); + target.getLongs().addAll( set ); + } + else { + target.setLongs( null ); + } + } + else { + Set set = stringListToLongSet( source.getStrings() ); + if ( set != null ) { + target.setLongs( set ); + } + } + if ( target.getStringsInitialized() != null ) { + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + target.getStringsInitialized().clear(); + target.getStringsInitialized().addAll( list1 ); + } + else { + target.setStringsInitialized( null ); + } + } + else { + List list1 = source.getStringsInitialized(); + if ( list1 != null ) { + target.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + } + if ( target.getLongsInitialized() != null ) { + Set set1 = stringListToLongSet( source.getStringsInitialized() ); + if ( set1 != null ) { + target.getLongsInitialized().clear(); + target.getLongsInitialized().addAll( set1 ); + } + else { + target.setLongsInitialized( null ); + } + } + else { + Set set1 = stringListToLongSet( source.getStringsInitialized() ); + if ( set1 != null ) { + target.setLongsInitialized( set1 ); + } + } + if ( target.getStringsWithDefault() != null ) { + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + target.getStringsWithDefault().clear(); + target.getStringsWithDefault().addAll( list2 ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + else { + List list2 = source.getStringsWithDefault(); + if ( list2 != null ) { + target.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + + return target; + } + + protected Set stringListToLongSet(List list) { + if ( list == null ) { + return null; + } + + Set set = LinkedHashSet.newLinkedHashSet( list.size() ); + for ( String string : list ) { + set.add( Long.parseLong( string ) ); + } + + return set; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java new file mode 100644 index 0000000000..6d01a91ec5 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java @@ -0,0 +1,214 @@ +/* + * 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._913; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-06-19T11:03:59+0300", + comments = "version: , compiler: javac, environment: Java 21.0.2 (Amazon.com Inc.)" +) +public class DomainDtoWithPresenceCheckMapperImpl implements DomainDtoWithPresenceCheckMapper { + + private final Helper helper = new Helper(); + + @Override + public Domain create(DtoWithPresenceCheck source) { + if ( source == null ) { + return null; + } + + Domain domain = createNullDomain(); + + if ( source.hasStrings() ) { + List list = source.getStrings(); + domain.setStrings( new LinkedHashSet( list ) ); + } + if ( source.hasStrings() ) { + domain.setLongs( stringListToLongSet( source.getStrings() ) ); + } + if ( source.hasStringsInitialized() ) { + List list1 = source.getStringsInitialized(); + domain.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + if ( source.hasStringsInitialized() ) { + domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + } + if ( source.hasStringsWithDefault() ) { + List list2 = source.getStringsWithDefault(); + domain.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + domain.setStringsWithDefault( helper.toList( "3" ) ); + } + + return domain; + } + + @Override + public void update(DtoWithPresenceCheck source, Domain target) { + if ( source == null ) { + return; + } + + if ( target.getStrings() != null ) { + if ( source.hasStrings() ) { + target.getStrings().clear(); + target.getStrings().addAll( source.getStrings() ); + } + } + else { + if ( source.hasStrings() ) { + List list = source.getStrings(); + target.setStrings( new LinkedHashSet( list ) ); + } + } + if ( target.getLongs() != null ) { + if ( source.hasStrings() ) { + target.getLongs().clear(); + target.getLongs().addAll( stringListToLongSet( source.getStrings() ) ); + } + } + else { + if ( source.hasStrings() ) { + target.setLongs( stringListToLongSet( source.getStrings() ) ); + } + } + if ( target.getStringsInitialized() != null ) { + if ( source.hasStringsInitialized() ) { + target.getStringsInitialized().clear(); + target.getStringsInitialized().addAll( source.getStringsInitialized() ); + } + } + else { + if ( source.hasStringsInitialized() ) { + List list1 = source.getStringsInitialized(); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + } + if ( target.getLongsInitialized() != null ) { + if ( source.hasStringsInitialized() ) { + target.getLongsInitialized().clear(); + target.getLongsInitialized().addAll( stringListToLongSet( source.getStringsInitialized() ) ); + } + } + else { + if ( source.hasStringsInitialized() ) { + target.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + } + } + if ( target.getStringsWithDefault() != null ) { + if ( source.hasStringsWithDefault() ) { + target.getStringsWithDefault().clear(); + target.getStringsWithDefault().addAll( source.getStringsWithDefault() ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + else { + if ( source.hasStringsWithDefault() ) { + List list2 = source.getStringsWithDefault(); + target.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + } + + @Override + public Domain updateWithReturn(DtoWithPresenceCheck source, Domain target) { + if ( source == null ) { + return target; + } + + if ( target.getStrings() != null ) { + if ( source.hasStrings() ) { + target.getStrings().clear(); + target.getStrings().addAll( source.getStrings() ); + } + } + else { + if ( source.hasStrings() ) { + List list = source.getStrings(); + target.setStrings( new LinkedHashSet( list ) ); + } + } + if ( target.getLongs() != null ) { + if ( source.hasStrings() ) { + target.getLongs().clear(); + target.getLongs().addAll( stringListToLongSet( source.getStrings() ) ); + } + } + else { + if ( source.hasStrings() ) { + target.setLongs( stringListToLongSet( source.getStrings() ) ); + } + } + if ( target.getStringsInitialized() != null ) { + if ( source.hasStringsInitialized() ) { + target.getStringsInitialized().clear(); + target.getStringsInitialized().addAll( source.getStringsInitialized() ); + } + } + else { + if ( source.hasStringsInitialized() ) { + List list1 = source.getStringsInitialized(); + target.setStringsInitialized( new LinkedHashSet( list1 ) ); + } + } + if ( target.getLongsInitialized() != null ) { + if ( source.hasStringsInitialized() ) { + target.getLongsInitialized().clear(); + target.getLongsInitialized().addAll( stringListToLongSet( source.getStringsInitialized() ) ); + } + } + else { + if ( source.hasStringsInitialized() ) { + target.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) ); + } + } + if ( target.getStringsWithDefault() != null ) { + if ( source.hasStringsWithDefault() ) { + target.getStringsWithDefault().clear(); + target.getStringsWithDefault().addAll( source.getStringsWithDefault() ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + else { + if ( source.hasStringsWithDefault() ) { + List list2 = source.getStringsWithDefault(); + target.setStringsWithDefault( new ArrayList( list2 ) ); + } + else { + target.setStringsWithDefault( helper.toList( "3" ) ); + } + } + + return target; + } + + protected Set stringListToLongSet(List list) { + if ( list == null ) { + return null; + } + + Set set = LinkedHashSet.newLinkedHashSet( list.size() ); + for ( String string : list ) { + set.add( Long.parseLong( string ) ); + } + + return set; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java new file mode 100644 index 0000000000..3f0bee535a --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapperImpl.java @@ -0,0 +1,259 @@ +/* + * 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.collection.defaultimplementation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentNavigableMap; +import java.util.concurrent.ConcurrentSkipListMap; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-06-18T14:48:39+0200", + comments = "version: , compiler: javac, environment: Java 21.0.2 (Eclipse Adoptium)" +) +public class SourceTargetMapperImpl implements SourceTargetMapper { + + @Override + public Target sourceToTarget(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + if ( target.getFooListNoSetter() != null ) { + List list = sourceFoosToTargetFoos( source.getFooList() ); + if ( list != null ) { + target.getFooListNoSetter().addAll( list ); + } + } + + return target; + } + + @Override + public TargetFoo sourceFooToTargetFoo(SourceFoo sourceFoo) { + if ( sourceFoo == null ) { + return null; + } + + TargetFoo targetFoo = new TargetFoo(); + + targetFoo.setName( sourceFoo.getName() ); + + return targetFoo; + } + + @Override + public List sourceFoosToTargetFoos(List foos) { + if ( foos == null ) { + return null; + } + + List list = new ArrayList( foos.size() ); + for ( SourceFoo sourceFoo : foos ) { + list.add( sourceFooToTargetFoo( sourceFoo ) ); + } + + return list; + } + + @Override + public Set sourceFoosToTargetFoos(Set foos) { + if ( foos == null ) { + return null; + } + + Set set = LinkedHashSet.newLinkedHashSet( foos.size() ); + for ( SourceFoo sourceFoo : foos ) { + set.add( sourceFooToTargetFoo( sourceFoo ) ); + } + + return set; + } + + @Override + public Collection sourceFoosToTargetFoos(Collection foos) { + if ( foos == null ) { + return null; + } + + Collection collection = new ArrayList( foos.size() ); + for ( SourceFoo sourceFoo : foos ) { + collection.add( sourceFooToTargetFoo( sourceFoo ) ); + } + + return collection; + } + + @Override + public Iterable sourceFoosToTargetFoos(Iterable foos) { + if ( foos == null ) { + return null; + } + + ArrayList iterable = new ArrayList(); + for ( SourceFoo sourceFoo : foos ) { + iterable.add( sourceFooToTargetFoo( sourceFoo ) ); + } + + return iterable; + } + + @Override + public void sourceFoosToTargetFoosUsingTargetParameter(List targetFoos, Iterable sourceFoos) { + if ( sourceFoos == null ) { + return; + } + + targetFoos.clear(); + for ( SourceFoo sourceFoo : sourceFoos ) { + targetFoos.add( sourceFooToTargetFoo( sourceFoo ) ); + } + } + + @Override + public Iterable sourceFoosToTargetFoosUsingTargetParameterAndReturn(Iterable sourceFoos, List targetFoos) { + if ( sourceFoos == null ) { + return targetFoos; + } + + targetFoos.clear(); + for ( SourceFoo sourceFoo : sourceFoos ) { + targetFoos.add( sourceFooToTargetFoo( sourceFoo ) ); + } + + return targetFoos; + } + + @Override + public SortedSet sourceFoosToTargetFooSortedSet(Collection foos) { + if ( foos == null ) { + return null; + } + + SortedSet sortedSet = new TreeSet(); + for ( SourceFoo sourceFoo : foos ) { + sortedSet.add( sourceFooToTargetFoo( sourceFoo ) ); + } + + return sortedSet; + } + + @Override + public NavigableSet sourceFoosToTargetFooNavigableSet(Collection foos) { + if ( foos == null ) { + return null; + } + + NavigableSet navigableSet = new TreeSet(); + for ( SourceFoo sourceFoo : foos ) { + navigableSet.add( sourceFooToTargetFoo( sourceFoo ) ); + } + + return navigableSet; + } + + @Override + public Map sourceFooMapToTargetFooMap(Map foos) { + if ( foos == null ) { + return null; + } + + Map map = LinkedHashMap.newLinkedHashMap( foos.size() ); + + for ( java.util.Map.Entry entry : foos.entrySet() ) { + String key = String.valueOf( entry.getKey() ); + TargetFoo value = sourceFooToTargetFoo( entry.getValue() ); + map.put( key, value ); + } + + return map; + } + + @Override + public SortedMap sourceFooMapToTargetFooSortedMap(Map foos) { + if ( foos == null ) { + return null; + } + + SortedMap sortedMap = new TreeMap(); + + for ( java.util.Map.Entry entry : foos.entrySet() ) { + String key = String.valueOf( entry.getKey() ); + TargetFoo value = sourceFooToTargetFoo( entry.getValue() ); + sortedMap.put( key, value ); + } + + return sortedMap; + } + + @Override + public NavigableMap sourceFooMapToTargetFooNavigableMap(Map foos) { + if ( foos == null ) { + return null; + } + + NavigableMap navigableMap = new TreeMap(); + + for ( java.util.Map.Entry entry : foos.entrySet() ) { + String key = String.valueOf( entry.getKey() ); + TargetFoo value = sourceFooToTargetFoo( entry.getValue() ); + navigableMap.put( key, value ); + } + + return navigableMap; + } + + @Override + public ConcurrentMap sourceFooMapToTargetFooConcurrentMap(Map foos) { + if ( foos == null ) { + return null; + } + + ConcurrentMap concurrentMap = new ConcurrentHashMap( Math.max( (int) ( foos.size() / .75f ) + 1, 16 ) ); + + for ( java.util.Map.Entry entry : foos.entrySet() ) { + String key = String.valueOf( entry.getKey() ); + TargetFoo value = sourceFooToTargetFoo( entry.getValue() ); + concurrentMap.put( key, value ); + } + + return concurrentMap; + } + + @Override + public ConcurrentNavigableMap sourceFooMapToTargetFooConcurrentNavigableMap(Map foos) { + if ( foos == null ) { + return null; + } + + ConcurrentNavigableMap concurrentNavigableMap = new ConcurrentSkipListMap(); + + for ( java.util.Map.Entry entry : foos.entrySet() ) { + String key = String.valueOf( entry.getKey() ); + TargetFoo value = sourceFooToTargetFoo( entry.getValue() ); + concurrentNavigableMap.put( key, value ); + } + + return concurrentNavigableMap; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/updatemethods/CompanyMapper1Impl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/updatemethods/CompanyMapper1Impl.java new file mode 100644 index 0000000000..f34495cb28 --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/updatemethods/CompanyMapper1Impl.java @@ -0,0 +1,120 @@ +/* + * 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.updatemethods; + +import java.util.LinkedHashMap; +import java.util.Map; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-06-18T14:48:39+0200", + comments = "version: , compiler: javac, environment: Java 21.0.2 (Eclipse Adoptium)" +) +public class CompanyMapper1Impl implements CompanyMapper1 { + + private final DepartmentEntityFactory departmentEntityFactory = new DepartmentEntityFactory(); + + @Override + public void toCompanyEntity(UnmappableCompanyDto dto, CompanyEntity entity) { + if ( dto == null ) { + return; + } + + entity.setName( dto.getName() ); + if ( dto.getDepartment() != null ) { + if ( entity.getDepartment() == null ) { + entity.setDepartment( departmentEntityFactory.createDepartmentEntity() ); + } + unmappableDepartmentDtoToDepartmentEntity( dto.getDepartment(), entity.getDepartment() ); + } + else { + entity.setDepartment( null ); + } + } + + @Override + public void toInBetween(UnmappableDepartmentDto dto, DepartmentInBetween entity) { + if ( dto == null ) { + return; + } + + entity.setName( dto.getName() ); + } + + @Override + public void toDepartmentEntity(DepartmentInBetween dto, DepartmentEntity entity) { + if ( dto == null ) { + return; + } + + entity.setName( dto.getName() ); + } + + protected SecretaryEntity secretaryDtoToSecretaryEntity(SecretaryDto secretaryDto) { + if ( secretaryDto == null ) { + return null; + } + + SecretaryEntity secretaryEntity = new SecretaryEntity(); + + secretaryEntity.setName( secretaryDto.getName() ); + + return secretaryEntity; + } + + protected EmployeeEntity employeeDtoToEmployeeEntity(EmployeeDto employeeDto) { + if ( employeeDto == null ) { + return null; + } + + EmployeeEntity employeeEntity = new EmployeeEntity(); + + employeeEntity.setName( employeeDto.getName() ); + + return employeeEntity; + } + + protected Map secretaryDtoEmployeeDtoMapToSecretaryEntityEmployeeEntityMap(Map map) { + if ( map == null ) { + return null; + } + + Map map1 = LinkedHashMap.newLinkedHashMap( map.size() ); + + for ( java.util.Map.Entry entry : map.entrySet() ) { + SecretaryEntity key = secretaryDtoToSecretaryEntity( entry.getKey() ); + EmployeeEntity value = employeeDtoToEmployeeEntity( entry.getValue() ); + map1.put( key, value ); + } + + return map1; + } + + protected void unmappableDepartmentDtoToDepartmentEntity(UnmappableDepartmentDto unmappableDepartmentDto, DepartmentEntity mappingTarget) { + if ( unmappableDepartmentDto == null ) { + return; + } + + mappingTarget.setName( unmappableDepartmentDto.getName() ); + if ( mappingTarget.getSecretaryToEmployee() != null ) { + Map map = secretaryDtoEmployeeDtoMapToSecretaryEntityEmployeeEntityMap( unmappableDepartmentDto.getSecretaryToEmployee() ); + if ( map != null ) { + mappingTarget.getSecretaryToEmployee().clear(); + mappingTarget.getSecretaryToEmployee().putAll( map ); + } + else { + mappingTarget.setSecretaryToEmployee( null ); + } + } + else { + Map map = secretaryDtoEmployeeDtoMapToSecretaryEntityEmployeeEntityMap( unmappableDepartmentDto.getSecretaryToEmployee() ); + if ( map != null ) { + mappingTarget.setSecretaryToEmployee( map ); + } + } + } +} From 5232df2707b2c396f6d8bf6453a9f292684ef0f3 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 2 Sep 2024 15:06:36 +0200 Subject: [PATCH 444/597] Try to stabilise MapMappingTest and CarMapperTest --- .../org/mapstruct/ap/test/collection/map/MapMappingTest.java | 2 ++ .../test/java/org/mapstruct/ap/test/complex/CarMapperTest.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java index d0dd66d22b..1261525d4f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java @@ -11,6 +11,7 @@ import java.util.HashMap; import java.util.Map; +import org.junitpioneer.jupiter.DefaultTimeZone; import org.mapstruct.ap.test.collection.map.other.ImportedType; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; @@ -26,6 +27,7 @@ */ @WithClasses({ SourceTargetMapper.class, CustomNumberMapper.class, Source.class, Target.class, ImportedType.class }) @IssueKey("44") +@DefaultTimeZone("UTC") public class MapMappingTest { @ProcessorTest diff --git a/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapperTest.java index 5eaf44765d..a1881a7613 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/complex/CarMapperTest.java @@ -11,6 +11,7 @@ import java.util.GregorianCalendar; import java.util.List; +import org.junitpioneer.jupiter.DefaultTimeZone; import org.mapstruct.ap.test.complex._target.CarDto; import org.mapstruct.ap.test.complex._target.PersonDto; import org.mapstruct.ap.test.complex.other.DateMapper; @@ -31,6 +32,7 @@ Category.class, DateMapper.class }) +@DefaultTimeZone("UTC") public class CarMapperTest { @ProcessorTest From 796dd9467486ec6ec91bdd919f663bde098896df Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 2 Sep 2024 15:33:08 +0200 Subject: [PATCH 445/597] Update next release changelog with latest changes --- NEXT_RELEASE_CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 438f4e3a6f..19571780cd 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -2,11 +2,16 @@ ### Enhancements +* Use Java `LinkedHashSet` and `LinkedHashMap` new factory method with known capacity when on Java 19 or later (#3113) + ### Bugs * Inverse Inheritance Strategy not working for ignored mappings only with target (#3652) * Inconsistent ambiguous mapping method error when using `SubclassMapping`: generic vs raw types (#3668) * Fix regression when using `InheritInverseConfiguration` with nested target properties and reversing `target = "."` (#3670) +* Deep mapping with multiple mappings broken in 1.6.0 (#3667) +* Two different constants are ignored in 1.6.0 (#3673) +* Inconsistent ambiguous mapping method error: generic vs raw types in 1.6.0 (#3668) ### Documentation From 12c9c6c1f067fde585a5f1aa4e7327d2770c7ef8 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 6 Sep 2024 16:35:37 +0300 Subject: [PATCH 446/597] Use email variable for GitHub Bot git email --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 36cabf44f8..f18a12b813 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,7 +38,7 @@ jobs: NEXT_VERSION=$COMPUTED_NEXT_VERSION fi ./mvnw -ntp -B versions:set versions:commit -DnewVersion=$RELEASE_VERSION -pl :mapstruct-parent -DgenerateBackupPoms=false - git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --global user.email "${{ vars.GH_BOT_EMAIL }}" git config --global user.name "GitHub Action" git commit -a -m "Releasing version $RELEASE_VERSION" git push From 2686e852b6bd85295f723f705450d401e99f560d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 14 Sep 2024 01:17:45 +0200 Subject: [PATCH 447/597] #3661 Use correct type for the Record component read accessors --- .../itest/tests/MavenIntegrationTest.java | 7 +++ .../module-1/pom.xml | 22 +++++++++ .../records/module1/NestedInterface.java | 10 +++++ .../itest/records/module1/RootInterface.java | 10 +++++ .../records/module1/SourceNestedRecord.java | 11 +++++ .../records/module1/SourceRootRecord.java | 11 +++++ .../module-2/pom.xml | 30 +++++++++++++ .../module2/RecordInterfaceIssueMapper.java | 18 ++++++++ .../records/module2/TargetNestedRecord.java | 11 +++++ .../records/module2/TargetRootRecord.java | 11 +++++ .../itest/records/module2/RecordsTest.java | 26 +++++++++++ .../recordsCrossModuleInterfaceTest/pom.xml | 26 +++++++++++ .../itest/records/mapper/RecordsTest.java | 0 .../ap/internal/model/common/Type.java | 4 +- .../mapstruct/ap/internal/util/Filters.java | 33 +++++--------- .../util/accessor/ElementAccessor.java | 45 +++++++++++++++++++ .../internal/util/accessor/ReadAccessor.java | 12 ++++- ...cessor.java => RecordElementAccessor.java} | 7 +-- 18 files changed, 265 insertions(+), 29 deletions(-) create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/pom.xml create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/NestedInterface.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/RootInterface.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceNestedRecord.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceRootRecord.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/pom.xml create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/RecordInterfaceIssueMapper.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetNestedRecord.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetRootRecord.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java create mode 100644 integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/pom.xml rename integrationtest/src/test/resources/recordsCrossModuleTest/mapper/{ => src}/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java (100%) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ElementAccessor.java rename processor/src/main/java/org/mapstruct/ap/internal/util/accessor/{FieldElementAccessor.java => RecordElementAccessor.java} (77%) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 7e9175dd92..0bef2994f6 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -131,6 +131,13 @@ void recordsTest() { void recordsCrossModuleTest() { } + @ProcessorTest(baseDir = "recordsCrossModuleInterfaceTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC + }) + @EnabledForJreRange(min = JRE.JAVA_17) + void recordsCrossModuleInterfaceTest() { + } + @ProcessorTest(baseDir = "expressionTextBlocksTest", processorTypes = { ProcessorTest.ProcessorType.JAVAC }) diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/pom.xml b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/pom.xml new file mode 100644 index 0000000000..72df10f62c --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/pom.xml @@ -0,0 +1,22 @@ + + + + 4.0.0 + + + recordsCrossModuleInterfaceTest + org.mapstruct + 1.0.0 + + + records-cross-module-1 + + diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/NestedInterface.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/NestedInterface.java new file mode 100644 index 0000000000..ffa53f88b1 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/NestedInterface.java @@ -0,0 +1,10 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.module1; + +public interface NestedInterface { + String field(); +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/RootInterface.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/RootInterface.java new file mode 100644 index 0000000000..fb23ffe157 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/RootInterface.java @@ -0,0 +1,10 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.module1; + +public interface RootInterface { + NestedInterface nested(); +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceNestedRecord.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceNestedRecord.java new file mode 100644 index 0000000000..6a0ddb86af --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceNestedRecord.java @@ -0,0 +1,11 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.module1; + +public record SourceNestedRecord( + String field +) implements NestedInterface { +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceRootRecord.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceRootRecord.java new file mode 100644 index 0000000000..151ad5208d --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-1/src/main/java/org/mapstruct/itest/records/module1/SourceRootRecord.java @@ -0,0 +1,11 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.module1; + +public record SourceRootRecord( + SourceNestedRecord nested +) implements RootInterface { +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/pom.xml b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/pom.xml new file mode 100644 index 0000000000..5f42efd18e --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/pom.xml @@ -0,0 +1,30 @@ + + + + 4.0.0 + + + recordsCrossModuleInterfaceTest + org.mapstruct + 1.0.0 + + + records-cross-module-2 + + + + + org.mapstruct + records-cross-module-1 + 1.0.0 + + + diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/RecordInterfaceIssueMapper.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/RecordInterfaceIssueMapper.java new file mode 100644 index 0000000000..a763359a98 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/RecordInterfaceIssueMapper.java @@ -0,0 +1,18 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.module2; + +import org.mapstruct.itest.records.module1.SourceRootRecord; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface RecordInterfaceIssueMapper { + + RecordInterfaceIssueMapper INSTANCE = Mappers.getMapper(RecordInterfaceIssueMapper.class); + + TargetRootRecord map(SourceRootRecord source); +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetNestedRecord.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetNestedRecord.java new file mode 100644 index 0000000000..d02a4b58e0 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetNestedRecord.java @@ -0,0 +1,11 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.module2; + +public record TargetNestedRecord( + String field +) { +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetRootRecord.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetRootRecord.java new file mode 100644 index 0000000000..09a69f1bf1 --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/main/java/org/mapstruct/itest/records/module2/TargetRootRecord.java @@ -0,0 +1,11 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.module2; + +public record TargetRootRecord( + TargetNestedRecord nested +) { +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java new file mode 100644 index 0000000000..5f7a99273a --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java @@ -0,0 +1,26 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.module2; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.mapstruct.itest.records.module1.SourceRootRecord; +import org.mapstruct.itest.records.module1.SourceNestedRecord; + +public class RecordsTest { + + @Test + public void shouldMap() { + SourceRootRecord source = new SourceRootRecord( new SourceNestedRecord( "test" ) ); + TargetRootRecord target = RecordInterfaceIssueMapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.nested() ).isNotNull(); + assertThat( target.nested().field() ).isEqualTo( "test" ); + } + +} diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/pom.xml b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/pom.xml new file mode 100644 index 0000000000..120c849dca --- /dev/null +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/pom.xml @@ -0,0 +1,26 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + recordsCrossModuleInterfaceTest + pom + + + module-1 + module-2 + + diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java similarity index 100% rename from integrationtest/src/test/resources/recordsCrossModuleTest/mapper/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java rename to integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 54305cffc1..6520834066 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -50,7 +50,7 @@ import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; -import org.mapstruct.ap.internal.util.accessor.FieldElementAccessor; +import org.mapstruct.ap.internal.util.accessor.ElementAccessor; import org.mapstruct.ap.internal.util.accessor.MapValueAccessor; import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; import org.mapstruct.ap.internal.util.accessor.ReadAccessor; @@ -1047,7 +1047,7 @@ private List getAlternativeTargetAccessors() { List setterMethods = getSetters(); List readAccessors = new ArrayList<>( getPropertyReadAccessors().values() ); // All the fields are also alternative accessors - readAccessors.addAll( filters.fieldsIn( getAllFields(), FieldElementAccessor::new ) ); + readAccessors.addAll( filters.fieldsIn( getAllFields(), ElementAccessor::new ) ); // there could be a read accessor (field or method) for a list/map that is not present as setter. // an accessor could substitute the setter in that case and act as setter. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java index 123f72832b..5f7fe74bf2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java @@ -39,22 +39,16 @@ public class Filters { private static final Method RECORD_COMPONENTS_METHOD; - private static final Method RECORD_COMPONENT_ACCESSOR_METHOD; static { Method recordComponentsMethod; - Method recordComponentAccessorMethod; try { recordComponentsMethod = TypeElement.class.getMethod( "getRecordComponents" ); - recordComponentAccessorMethod = Class.forName( "javax.lang.model.element.RecordComponentElement" ) - .getMethod( "getAccessor" ); } - catch ( NoSuchMethodException | ClassNotFoundException e ) { + catch ( NoSuchMethodException e ) { recordComponentsMethod = null; - recordComponentAccessorMethod = null; } RECORD_COMPONENTS_METHOD = recordComponentsMethod; - RECORD_COMPONENT_ACCESSOR_METHOD = recordComponentAccessorMethod; } private final AccessorNamingUtils accessorNaming; @@ -89,25 +83,18 @@ public List recordComponentsIn(TypeElement typeElement) { } public Map recordAccessorsIn(Collection recordComponents) { - if ( RECORD_COMPONENT_ACCESSOR_METHOD == null ) { + if ( recordComponents.isEmpty() ) { return java.util.Collections.emptyMap(); } - try { - Map recordAccessors = new LinkedHashMap<>(); - for ( Element recordComponent : recordComponents ) { - ExecutableElement recordExecutableElement = - (ExecutableElement) RECORD_COMPONENT_ACCESSOR_METHOD.invoke( recordComponent ); - recordAccessors.put( - recordComponent.getSimpleName().toString(), - ReadAccessor.fromGetter( recordExecutableElement, getReturnType( recordExecutableElement ) ) - ); - } - - return recordAccessors; - } - catch ( IllegalAccessException | InvocationTargetException e ) { - return java.util.Collections.emptyMap(); + Map recordAccessors = new LinkedHashMap<>(); + for ( Element recordComponent : recordComponents ) { + recordAccessors.put( + recordComponent.getSimpleName().toString(), + ReadAccessor.fromRecordComponent( recordComponent ) + ); } + + return recordAccessors; } private TypeMirror getReturnType(ExecutableElement executableElement) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ElementAccessor.java new file mode 100644 index 0000000000..24e71cc85f --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ElementAccessor.java @@ -0,0 +1,45 @@ +/* + * 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.internal.util.accessor; + +import javax.lang.model.element.Element; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; + +/** + * An {@link Accessor} that wraps a {@link VariableElement}. + * + * @author Filip Hrisafov + */ +public class ElementAccessor extends AbstractAccessor { + + private final AccessorType accessorType; + + public ElementAccessor(VariableElement variableElement) { + this( variableElement, AccessorType.FIELD ); + } + + public ElementAccessor(Element element, AccessorType accessorType) { + super( element ); + this.accessorType = accessorType; + } + + @Override + public TypeMirror getAccessedType() { + return element.asType(); + } + + @Override + public String toString() { + return element.toString(); + } + + @Override + public AccessorType getAccessorType() { + return accessorType; + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java index a790a3361b..5177bfc75b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.internal.util.accessor; +import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; @@ -17,7 +18,7 @@ public interface ReadAccessor extends Accessor { String getReadValueSource(); static ReadAccessor fromField(VariableElement variableElement) { - return new ReadDelegateAccessor( new FieldElementAccessor( variableElement ) ) { + return new ReadDelegateAccessor( new ElementAccessor( variableElement ) ) { @Override public String getReadValueSource() { return getSimpleName(); @@ -25,6 +26,15 @@ public String getReadValueSource() { }; } + static ReadAccessor fromRecordComponent(Element element) { + return new ReadDelegateAccessor( new ElementAccessor( element, AccessorType.GETTER ) ) { + @Override + public String getReadValueSource() { + return getSimpleName() + "()"; + } + }; + } + static ReadAccessor fromGetter(ExecutableElement element, TypeMirror accessedType) { return new ReadDelegateAccessor( new ExecutableElementAccessor( element, accessedType, AccessorType.GETTER ) ) { @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/FieldElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/RecordElementAccessor.java similarity index 77% rename from processor/src/main/java/org/mapstruct/ap/internal/util/accessor/FieldElementAccessor.java rename to processor/src/main/java/org/mapstruct/ap/internal/util/accessor/RecordElementAccessor.java index 5174900532..d163f462f9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/FieldElementAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/RecordElementAccessor.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.internal.util.accessor; +import javax.lang.model.element.Element; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; @@ -13,9 +14,9 @@ * * @author Filip Hrisafov */ -public class FieldElementAccessor extends AbstractAccessor { +public class RecordElementAccessor extends AbstractAccessor { - public FieldElementAccessor(VariableElement element) { + public RecordElementAccessor(Element element) { super( element ); } @@ -31,7 +32,7 @@ public String toString() { @Override public AccessorType getAccessorType() { - return AccessorType.FIELD; + return AccessorType.GETTER; } } From 4c1df35ba676814d06336ddf036f4408d9e0526d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 14 Sep 2024 23:06:56 +0200 Subject: [PATCH 448/597] #3703 Use include model instead of manually writing the type name for return type for afterMappingReferencesWithFinalizedReturnType --- .../ap/internal/model/BeanMappingMethod.ftl | 2 +- .../ap/test/bugs/_3703/Issue3703Mapper.java | 37 +++++++++++++++++++ .../ap/test/bugs/_3703/Issue3703Test.java | 24 ++++++++++++ .../ap/test/bugs/_3703/dto/Contact.java | 37 +++++++++++++++++++ .../ap/test/bugs/_3703/entity/Contact.java | 37 +++++++++++++++++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/Issue3703Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/Issue3703Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/dto/Contact.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/entity/Contact.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 8fb5bfdef0..61d9cc1837 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -136,7 +136,7 @@ <#if finalizerMethod??> <#if (afterMappingReferencesWithFinalizedReturnType?size > 0)> - ${returnType.name} ${finalizedResultName} = ${resultName}.<@includeModel object=finalizerMethod />; + <@includeModel object=returnType /> ${finalizedResultName} = ${resultName}.<@includeModel object=finalizerMethod />; <#list afterMappingReferencesWithFinalizedReturnType as callback> <#if callback_index = 0> diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/Issue3703Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/Issue3703Mapper.java new file mode 100644 index 0000000000..287e7655fb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/Issue3703Mapper.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.bugs._3703; + +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.ap.test.bugs._3703.dto.Contact; + +@Mapper +public interface Issue3703Mapper { + + Contact map(org.mapstruct.ap.test.bugs._3703.entity.Contact contact); + + org.mapstruct.ap.test.bugs._3703.entity.Contact map(Contact contact); + + @AfterMapping + default void afterMapping(@MappingTarget Contact target, org.mapstruct.ap.test.bugs._3703.entity.Contact contact) { + } + + @AfterMapping + default void afterMapping(@MappingTarget Contact.Builder targetBuilder, + org.mapstruct.ap.test.bugs._3703.entity.Contact contact) { + } + + @AfterMapping + default void afterMapping(@MappingTarget org.mapstruct.ap.test.bugs._3703.entity.Contact target, Contact contact) { + } + + @AfterMapping + default void afterMapping(@MappingTarget org.mapstruct.ap.test.bugs._3703.entity.Contact.Builder targetBuilder, + Contact contact) { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/Issue3703Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/Issue3703Test.java new file mode 100644 index 0000000000..ac5cae3959 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/Issue3703Test.java @@ -0,0 +1,24 @@ +/* + * 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._3703; + +import org.mapstruct.ap.test.bugs._3703.dto.Contact; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +@IssueKey("3703") +@WithClasses({ + Contact.class, + org.mapstruct.ap.test.bugs._3703.entity.Contact.class, + Issue3703Mapper.class +}) +public class Issue3703Test { + + @ProcessorTest + void shouldCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/dto/Contact.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/dto/Contact.java new file mode 100644 index 0000000000..a949864791 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/dto/Contact.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.bugs._3703.dto; + +public class Contact { + + private final String name; + + private Contact(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + + private String name; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Contact build() { + return new Contact( name ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/entity/Contact.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/entity/Contact.java new file mode 100644 index 0000000000..31fde373fe --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3703/entity/Contact.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.bugs._3703.entity; + +public class Contact { + + private final String name; + + private Contact(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + + private String name; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Contact build() { + return new Contact( name ); + } + } +} From 3011dd77d7ee2de7a7a5d93d3e8e965bad971a43 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 15 Sep 2024 10:28:20 +0200 Subject: [PATCH 449/597] #3678 before / after mapping for type using builder should only be kept if they are using the actual type in `@TargetType` or `@MappingTarget` --- .../ap/internal/model/BeanMappingMethod.java | 33 ++++- .../ap/test/bugs/_3678/Issue3678Mapper.java | 128 ++++++++++++++++++ .../ap/test/bugs/_3678/Issue3678Test.java | 50 +++++++ 3 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3678/Issue3678Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3678/Issue3678Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 4ccf2bb49a..e6ac5b5bcf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -408,9 +408,8 @@ else if ( !method.isUpdateMethod() ) { existingVariableNames ) ); - // remove methods without parameters as they are already being invoked - removeMappingReferencesWithoutSourceParameters( beforeMappingReferencesWithFinalizedReturnType ); - removeMappingReferencesWithoutSourceParameters( afterMappingReferencesWithFinalizedReturnType ); + keepMappingReferencesUsingTarget( beforeMappingReferencesWithFinalizedReturnType, actualReturnType ); + keepMappingReferencesUsingTarget( afterMappingReferencesWithFinalizedReturnType, actualReturnType ); } Map presenceChecksByParameter = new LinkedHashMap<>(); @@ -453,8 +452,32 @@ else if ( !sourceParameter.getType().isPrimitive() ) { ); } - private void removeMappingReferencesWithoutSourceParameters(List references) { - references.removeIf( r -> r.getSourceParameters().isEmpty() && r.getReturnType().isVoid() ); + private void keepMappingReferencesUsingTarget(List references, Type type) { + references.removeIf( reference -> { + List bindings = reference.getParameterBindings(); + if ( bindings.isEmpty() ) { + return true; + } + for ( ParameterBinding binding : bindings ) { + if ( binding.isMappingTarget() ) { + if ( type.isAssignableTo( binding.getType() ) ) { + // If the mapping target matches the type then we need to keep this + return false; + } + } + else if ( binding.isTargetType() ) { + Type targetType = binding.getType(); + List targetTypeTypeParameters = targetType.getTypeParameters(); + if ( targetTypeTypeParameters.size() == 1 ) { + if ( type.isAssignableTo( targetTypeTypeParameters.get( 0 ) ) ) { + return false; + } + } + } + } + + return true; + } ); } private boolean doesNotAllowAbstractReturnTypeAndCanBeConstructed(Type returnTypeImpl) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3678/Issue3678Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3678/Issue3678Mapper.java new file mode 100644 index 0000000000..374fbf931a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3678/Issue3678Mapper.java @@ -0,0 +1,128 @@ +/* + * 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._3678; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue3678Mapper { + + Issue3678Mapper INSTANCE = Mappers.getMapper( Issue3678Mapper.class ); + + @Mapping(target = "name", source = "sourceA.name") + @Mapping(target = "description", source = "sourceB.description") + Target map(SourceA sourceA, SourceB sourceB, @Context MappingContext context); + + @Mapping(target = "description", constant = "some description") + Target map(SourceA sourceA, @Context MappingContext context); + + class MappingContext { + + private final List invokedMethods = new ArrayList<>(); + + @BeforeMapping + public void beforeMappingSourceA(SourceA sourceA) { + invokedMethods.add( "beforeMappingSourceA" ); + } + + @AfterMapping + public void afterMappingSourceB(SourceA sourceA) { + invokedMethods.add( "afterMappingSourceA" ); + } + + @BeforeMapping + public void beforeMappingSourceB(SourceB sourceB) { + invokedMethods.add( "beforeMappingSourceB" ); + } + + @AfterMapping + public void afterMappingSourceB(SourceB sourceB) { + invokedMethods.add( "afterMappingSourceB" ); + } + + public List getInvokedMethods() { + return invokedMethods; + } + } + + class SourceA { + + private final String name; + + public SourceA(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + class SourceB { + + private final String description; + + public SourceB(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + } + + final class Target { + + private final String name; + private final String description; + + private Target(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String name; + private String description; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Target build() { + return new Target( this.name, this.description ); + } + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3678/Issue3678Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3678/Issue3678Test.java new file mode 100644 index 0000000000..25e7e21622 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3678/Issue3678Test.java @@ -0,0 +1,50 @@ +/* + * 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._3678; + +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; + +@IssueKey("3678") +@WithClasses(Issue3678Mapper.class) +public class Issue3678Test { + + @ProcessorTest + void beforeAndAfterMappingOnlyCalledOnceForTwoSources() { + + Issue3678Mapper.MappingContext mappingContext = new Issue3678Mapper.MappingContext(); + Issue3678Mapper.SourceA sourceA = new Issue3678Mapper.SourceA( "name" ); + Issue3678Mapper.SourceB sourceB = new Issue3678Mapper.SourceB( "description" ); + Issue3678Mapper.INSTANCE.map( sourceA, sourceB, mappingContext ); + + assertThat( mappingContext.getInvokedMethods() ) + .containsExactly( + "beforeMappingSourceA", + "beforeMappingSourceB", + "afterMappingSourceA", + "afterMappingSourceB" + ); + } + + @ProcessorTest + void beforeAndAfterMappingOnlyCalledOnceForSingleSource() { + + Issue3678Mapper.MappingContext mappingContext = new Issue3678Mapper.MappingContext(); + Issue3678Mapper.SourceA sourceA = new Issue3678Mapper.SourceA( "name" ); + Issue3678Mapper.INSTANCE.map( sourceA, mappingContext ); + + assertThat( mappingContext.getInvokedMethods() ) + .containsExactly( + "beforeMappingSourceA", + "afterMappingSourceA" + ); + } + +} From c36f9ae5d10baa1fbae63df8a51e937db0569c07 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 15 Sep 2024 17:11:24 +0200 Subject: [PATCH 450/597] Prepare release notes for 1.6.1 --- NEXT_RELEASE_CHANGELOG.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 19571780cd..5337e70daf 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,5 +1,3 @@ -### Features - ### Enhancements * Use Java `LinkedHashSet` and `LinkedHashMap` new factory method with known capacity when on Java 19 or later (#3113) @@ -12,8 +10,6 @@ * Deep mapping with multiple mappings broken in 1.6.0 (#3667) * Two different constants are ignored in 1.6.0 (#3673) * Inconsistent ambiguous mapping method error: generic vs raw types in 1.6.0 (#3668) - -### Documentation - -### Build - +* Fix cross module records with interfaces not recognizing accessors (#3661) +* `@AfterMapping` methods are called twice when using target with builder (#3678) +* Compile error when using `@AfterMapping` method with Builder and TargetObject (#3703) From 10d69878a197c1bff1e8743a3d056e36eda856d3 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 15 Sep 2024 15:52:17 +0000 Subject: [PATCH 451/597] Releasing version 1.6.1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0b3ca1c113..3e347b5c93 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index c73676192b..ca326ef6f0 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index e62d5e4682..da17fa59dc 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index f826421092..405f489865 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 21a2b151bd..5044ed7689 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 7c91837e79..96f86c6f0e 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 94c84fb4d0..8c36b4b6ea 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.1 pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - ${git.commit.author.time} + 2024-09-15T15:52:17Z 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index f55f0955d3..a0e55059da 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.1 parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index ed2df7718b..e605c32560 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.1 ../parent/pom.xml From c74e62a94c4c420ec5aa52fc177c56ea0483c50b Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 15 Sep 2024 16:01:43 +0000 Subject: [PATCH 452/597] Next version 1.7.0-SNAPSHOT --- NEXT_RELEASE_CHANGELOG.md | 17 ++++++----------- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 10 files changed, 16 insertions(+), 21 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 5337e70daf..e0f4cd31f0 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,15 +1,10 @@ -### Enhancements +### Features -* Use Java `LinkedHashSet` and `LinkedHashMap` new factory method with known capacity when on Java 19 or later (#3113) +### Enhancements ### Bugs -* Inverse Inheritance Strategy not working for ignored mappings only with target (#3652) -* Inconsistent ambiguous mapping method error when using `SubclassMapping`: generic vs raw types (#3668) -* Fix regression when using `InheritInverseConfiguration` with nested target properties and reversing `target = "."` (#3670) -* Deep mapping with multiple mappings broken in 1.6.0 (#3667) -* Two different constants are ignored in 1.6.0 (#3673) -* Inconsistent ambiguous mapping method error: generic vs raw types in 1.6.0 (#3668) -* Fix cross module records with interfaces not recognizing accessors (#3661) -* `@AfterMapping` methods are called twice when using target with builder (#3678) -* Compile error when using `@AfterMapping` method with Builder and TargetObject (#3703) +### Documentation + +### Build + diff --git a/build-config/pom.xml b/build-config/pom.xml index 3e347b5c93..0b3ca1c113 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index ca326ef6f0..c73676192b 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index da17fa59dc..e62d5e4682 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 405f489865..f826421092 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 5044ed7689..21a2b151bd 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 96f86c6f0e..7c91837e79 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 8c36b4b6ea..94c84fb4d0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.1 + 1.7.0-SNAPSHOT pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - 2024-09-15T15:52:17Z + ${git.commit.author.time} 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index a0e55059da..f55f0955d3 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.1 + 1.7.0-SNAPSHOT parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index e605c32560..ed2df7718b 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.1 + 1.7.0-SNAPSHOT ../parent/pom.xml From a3b4139070a3111872c3d17e0cb4dbcbaf8f4bf1 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 16 Sep 2024 09:19:39 +0200 Subject: [PATCH 453/597] #3717 Fix ClassCastException when getting thrown types for a record accessor --- .../itest/records/nested/Address.java | 13 ++++++++++ .../itest/records/nested/CareProvider.java | 13 ++++++++++ .../itest/records/nested/CareProviderDto.java | 13 ++++++++++ .../records/nested/CareProviderMapper.java | 25 ++++++++++++++++++ .../records/nested/NestedRecordsTest.java | 26 +++++++++++++++++++ .../ap/internal/model/common/TypeFactory.java | 6 ++++- 6 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/Address.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProvider.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderDto.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderMapper.java create mode 100644 integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/Address.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/Address.java new file mode 100644 index 0000000000..fb857e90ca --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/Address.java @@ -0,0 +1,13 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.nested; + +/** + * @author Filip Hrisafov + */ +public record Address(String street, String city) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProvider.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProvider.java new file mode 100644 index 0000000000..a0ce13c0ba --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProvider.java @@ -0,0 +1,13 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.nested; + +/** + * @author Filip Hrisafov + */ +public record CareProvider(String externalId, Address address) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderDto.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderDto.java new file mode 100644 index 0000000000..d7ce7229e9 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderDto.java @@ -0,0 +1,13 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.nested; + +/** + * @author Filip Hrisafov + */ +public record CareProviderDto(String id, String street, String city) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderMapper.java new file mode 100644 index 0000000000..89ed688976 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/nested/CareProviderMapper.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.itest.records.nested; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface CareProviderMapper { + + CareProviderMapper INSTANCE = Mappers.getMapper( CareProviderMapper.class ); + + @Mapping(target = "id", source = "externalId") + @Mapping(target = "street", source = "address.street") + @Mapping(target = "city", source = "address.city") + CareProviderDto map(CareProvider source); +} diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java new file mode 100644 index 0000000000..c8ccaf1a65 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java @@ -0,0 +1,26 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records.nested; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +public class NestedRecordsTest { + + @Test + public void shouldMapRecord() { + CareProvider source = new CareProvider( "kermit", new Address( "Sesame Street", "New York" ) ); + CareProviderDto target = CareProviderMapper.INSTANCE.map( source ); + + assertThat( target ).isNotNull(); + assertThat( target.id() ).isEqualTo( "kermit" ); + assertThat( target.street() ).isEqualTo( "Sesame Street" ); + assertThat( target.city() ).isEqualTo( "New York" ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index d65070426b..ceb190a332 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -491,7 +491,11 @@ public List getThrownTypes(Accessor accessor) { if (accessor.getAccessorType().isFieldAssignment()) { return new ArrayList<>(); } - return extractTypes( ( (ExecutableElement) accessor.getElement() ).getThrownTypes() ); + Element element = accessor.getElement(); + if ( element instanceof ExecutableElement ) { + return extractTypes( ( (ExecutableElement) element ).getThrownTypes() ); + } + return new ArrayList<>(); } private List extractTypes(List typeMirrors) { From 4fd22d6b267f845038786364f46f16c5b0903d0a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 16 Sep 2024 09:54:30 +0200 Subject: [PATCH 454/597] Prepare release notes for 1.6.2 --- NEXT_RELEASE_CHANGELOG.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index e0f4cd31f0..dd5c9b2349 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,10 +1,3 @@ -### Features - -### Enhancements - ### Bugs -### Documentation - -### Build - +* Regression from 1.6.1: ClassCastException when using records (#3717) From 212607b4470c9e0deb8b6ad1fed56d016d58aa08 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 07:55:31 +0000 Subject: [PATCH 455/597] Releasing version 1.6.2 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0b3ca1c113..fa50066316 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.2 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index c73676192b..648c193e39 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.2 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index e62d5e4682..37ea12be4d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.2 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index f826421092..4539528a91 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.2 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 21a2b151bd..dfac83cca8 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.2 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 7c91837e79..ab8dcb26b7 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.2 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 94c84fb4d0..94f614ecbb 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.2 pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - ${git.commit.author.time} + 2024-09-16T07:55:31Z 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index f55f0955d3..919e2d6460 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.2 parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index ed2df7718b..e0052bf5d4 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.2 ../parent/pom.xml From 4e0d73db1d17c40d251d39812550206607a16824 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 08:06:43 +0000 Subject: [PATCH 456/597] Next version 1.7.0-SNAPSHOT --- NEXT_RELEASE_CHANGELOG.md | 9 ++++++++- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 10 files changed, 18 insertions(+), 11 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index dd5c9b2349..e0f4cd31f0 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,3 +1,10 @@ +### Features + +### Enhancements + ### Bugs -* Regression from 1.6.1: ClassCastException when using records (#3717) +### Documentation + +### Build + diff --git a/build-config/pom.xml b/build-config/pom.xml index fa50066316..0b3ca1c113 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.2 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 648c193e39..c73676192b 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.2 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index 37ea12be4d..e62d5e4682 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.2 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 4539528a91..f826421092 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.2 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index dfac83cca8..21a2b151bd 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.2 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index ab8dcb26b7..7c91837e79 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.2 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 94f614ecbb..94c84fb4d0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.2 + 1.7.0-SNAPSHOT pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - 2024-09-16T07:55:31Z + ${git.commit.author.time} 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index 919e2d6460..f55f0955d3 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.2 + 1.7.0-SNAPSHOT parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index e0052bf5d4..ed2df7718b 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.2 + 1.7.0-SNAPSHOT ../parent/pom.xml From 26c5bcd923c7206ad161e93237f841a79ca85efd Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 27 Sep 2024 09:15:17 +0200 Subject: [PATCH 457/597] Update readme with 1.6.2 --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index c0f798af83..7903ddeb84 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.6.0-blue.svg)](https://central.sonatype.com/search?q=g:org.mapstruct%20v:1.6.0) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.6.2-blue.svg)](https://central.sonatype.com/search?q=g:org.mapstruct%20v:1.6.2) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](https://central.sonatype.com/search?q=g:org.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/main/LICENSE.txt) @@ -65,7 +65,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.6.0 + 1.6.2 ... @@ -111,10 +111,10 @@ plugins { dependencies { ... - implementation 'org.mapstruct:mapstruct:1.6.0' + implementation 'org.mapstruct:mapstruct:1.6.2' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.0' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.6.0' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.2' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.6.2' // if you are using mapstruct in test code } ... ``` From 32f1fea7b50ab583b87e3634e25ac92752380682 Mon Sep 17 00:00:00 2001 From: Srimathi-S <75327301+Srimathi-S@users.noreply.github.com> Date: Sun, 3 Nov 2024 17:22:52 +0530 Subject: [PATCH 458/597] #3370 Prevent stack overflow error for Immutables with custom builder --- .../ap/spi/DefaultBuilderProvider.java | 9 +- .../ap/spi/ImmutablesBuilderProvider.java | 27 +- .../bugs/_3370/Issue3370BuilderProvider.java | 25 ++ .../ap/test/bugs/_3370/Issue3370Test.java | 55 +++ .../ap/test/bugs/_3370/ItemMapper.java | 19 ++ .../test/bugs/_3370/domain/ImmutableItem.java | 316 ++++++++++++++++++ .../ap/test/bugs/_3370/domain/Item.java | 27 ++ .../ap/test/bugs/_3370/dto/ItemDTO.java | 26 ++ 8 files changed, 496 insertions(+), 8 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/Issue3370BuilderProvider.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/Issue3370Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/ItemMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/domain/ImmutableItem.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/domain/Item.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/dto/ItemDTO.java diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java index 6395364a35..92cd1ed80d 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java @@ -176,6 +176,10 @@ public DeclaredType visitDeclared(DeclaredType t, Void p) { * @throws MoreThanOneBuilderCreationMethodException if there are multiple builder creation methods */ protected BuilderInfo findBuilderInfo(TypeElement typeElement) { + return findBuilderInfo( typeElement, true ); + } + + protected BuilderInfo findBuilderInfo(TypeElement typeElement, boolean checkParent) { if ( shouldIgnore( typeElement ) ) { return null; } @@ -203,7 +207,10 @@ else if ( builderInfo.size() > 1 ) { throw new MoreThanOneBuilderCreationMethodException( typeElement.asType(), builderInfo ); } - return findBuilderInfo( typeElement.getSuperclass() ); + if ( checkParent ) { + return findBuilderInfo( typeElement.getSuperclass() ); + } + return null; } /** diff --git a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesBuilderProvider.java b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesBuilderProvider.java index d4ccd029ef..3431808418 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesBuilderProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesBuilderProvider.java @@ -35,18 +35,31 @@ protected BuilderInfo findBuilderInfo(TypeElement typeElement) { if ( name.length() == 0 || JAVA_JAVAX_PACKAGE.matcher( name ).matches() ) { return null; } + + // First look if there is a builder defined in my own type + BuilderInfo info = findBuilderInfo( typeElement, false ); + if ( info != null ) { + return info; + } + + // Check for a builder in the generated immutable type + BuilderInfo immutableInfo = findBuilderInfoForImmutables( typeElement ); + if ( immutableInfo != null ) { + return immutableInfo; + } + + return super.findBuilderInfo( typeElement.getSuperclass() ); + } + + protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement) { TypeElement immutableAnnotation = elementUtils.getTypeElement( IMMUTABLE_FQN ); if ( immutableAnnotation != null ) { - BuilderInfo info = findBuilderInfoForImmutables( + return findBuilderInfoForImmutables( typeElement, immutableAnnotation ); - if ( info != null ) { - return info; - } } - - return super.findBuilderInfo( typeElement ); + return null; } protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement, @@ -55,7 +68,7 @@ protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement, if ( typeUtils.isSameType( annotationMirror.getAnnotationType(), immutableAnnotation.asType() ) ) { TypeElement immutableElement = asImmutableElement( typeElement ); if ( immutableElement != null ) { - return super.findBuilderInfo( immutableElement ); + return super.findBuilderInfo( immutableElement, false ); } else { // Immutables processor has not run yet. Trigger a postpone to the next round for MapStruct diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/Issue3370BuilderProvider.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/Issue3370BuilderProvider.java new file mode 100644 index 0000000000..55a3f783c3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/Issue3370BuilderProvider.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.ap.test.bugs._3370; + +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; + +import org.mapstruct.ap.spi.BuilderInfo; +import org.mapstruct.ap.spi.BuilderProvider; +import org.mapstruct.ap.spi.ImmutablesBuilderProvider; + +public class Issue3370BuilderProvider extends ImmutablesBuilderProvider implements BuilderProvider { + + @Override + protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement) { + Name name = typeElement.getQualifiedName(); + if ( name.toString().endsWith( ".Item" ) ) { + return super.findBuilderInfo( asImmutableElement( typeElement ) ); + } + return super.findBuilderInfo( typeElement ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/Issue3370Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/Issue3370Test.java new file mode 100644 index 0000000000..0389de5b46 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/Issue3370Test.java @@ -0,0 +1,55 @@ +/* + * 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._3370; + +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.ap.spi.AccessorNamingStrategy; +import org.mapstruct.ap.spi.BuilderProvider; +import org.mapstruct.ap.spi.ImmutablesAccessorNamingStrategy; +import org.mapstruct.ap.test.bugs._3370.domain.ImmutableItem; +import org.mapstruct.ap.test.bugs._3370.domain.Item; +import org.mapstruct.ap.test.bugs._3370.dto.ItemDTO; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithServiceImplementation; +import org.mapstruct.ap.testutil.WithServiceImplementations; + +import static org.assertj.core.api.Assertions.assertThat; + +@WithClasses({ + ItemMapper.class, + Item.class, + ImmutableItem.class, + ItemDTO.class, +}) +@IssueKey("3370") +@WithServiceImplementations({ + @WithServiceImplementation(provides = BuilderProvider.class, value = Issue3370BuilderProvider.class), + @WithServiceImplementation(provides = AccessorNamingStrategy.class, + value = ImmutablesAccessorNamingStrategy.class), +}) +public class Issue3370Test { + + @ProcessorTest + public void shouldUseBuilderOfImmutableSuperClass() { + + Map attributesMap = new HashMap<>(); + attributesMap.put( "a", "b" ); + attributesMap.put( "c", "d" ); + + ItemDTO item = new ItemDTO( "test", attributesMap ); + + Item target = ItemMapper.INSTANCE.map( item ); + + assertThat( target ).isNotNull(); + assertThat( target.getId() ).isEqualTo( "test" ); + assertThat( target.getAttributes() ).isEqualTo( attributesMap ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/ItemMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/ItemMapper.java new file mode 100644 index 0000000000..5583e191b3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/ItemMapper.java @@ -0,0 +1,19 @@ +/* + * 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._3370; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.bugs._3370.domain.Item; +import org.mapstruct.ap.test.bugs._3370.dto.ItemDTO; +import org.mapstruct.factory.Mappers; + +@Mapper +public abstract class ItemMapper { + + public static final ItemMapper INSTANCE = Mappers.getMapper( ItemMapper.class ); + + public abstract Item map(ItemDTO itemDTO); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/domain/ImmutableItem.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/domain/ImmutableItem.java new file mode 100644 index 0000000000..9258914c0b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/domain/ImmutableItem.java @@ -0,0 +1,316 @@ +/* + * 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._3370.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Immutable implementation of {@link Item}. + *

      + * Use the builder to create immutable instances: + * {@code new Item.Builder()}. + */ +@SuppressWarnings("all") +public final class ImmutableItem extends Item { + private final String id; + private final Map attributes; + + private ImmutableItem(String id, Map attributes) { + this.id = id; + this.attributes = attributes; + } + + /** + * @return The value of the {@code id} attribute + */ + @Override + public String getId() { + return id; + } + + /** + * @return The value of the {@code attributes} attribute + */ + @Override + public Map getAttributes() { + return attributes; + } + + /** + * Copy the current immutable object by setting a value for the {@link Item#getId() id} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * + * @param id A new value for id + * @return A modified copy of the {@code this} object + */ + public final ImmutableItem withId(String id) { + if ( this.id.equals( id ) ) { + return this; + } + String newValue = Objects.requireNonNull( id, "id" ); + return new ImmutableItem( newValue, this.attributes ); + } + + /** + * Copy the current immutable object by replacing the {@link Item#getAttributes() attributes} map with the specified map. + * Nulls are not permitted as keys or values. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param attributes The entries to be added to the attributes map + * @return A modified copy of {@code this} object + */ + public final ImmutableItem withAttributes(Map attributes) { + if ( this.attributes == attributes ) { + return this; + } + Map newValue = createUnmodifiableMap( true, false, attributes ); + return new ImmutableItem( this.id, newValue ); + } + + /** + * This instance is equal to all instances of {@code ImmutableItem} that have equal attribute values. + * + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if ( this == another ) { + return true; + } + return another instanceof ImmutableItem + && equalTo( (ImmutableItem) another ); + } + + private boolean equalTo(ImmutableItem another) { + return id.equals( another.id ) + && attributes.equals( another.attributes ); + } + + /** + * Computes a hash code from attributes: {@code id}, {@code attributes}. + * + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 31; + h = h * 17 + id.hashCode(); + h = h * 17 + attributes.hashCode(); + return h; + } + + /** + * Prints the immutable value {@code Item} with attribute values. + * + * @return A string representation of the value + */ + @Override + public String toString() { + return "Item{" + + "id=" + id + + ", attributes=" + attributes + + "}"; + } + + /** + * Creates an immutable copy of a {@link Item} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * + * @param instance The instance to copy + * @return A copied immutable Item instance + */ + public static ImmutableItem copyOf(Item instance) { + if ( instance instanceof ImmutableItem ) { + return (ImmutableItem) instance; + } + return new Item.Builder() + .from( instance ) + .build(); + } + + /** + * Builds instances of type {@link ImmutableItem ImmutableItem}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

      {@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static class Builder { + private static final long INIT_BIT_ID = 0x1L; + private long initBits = 0x1L; + + private String id; + private Map attributes = new LinkedHashMap(); + + /** + * Creates a builder for {@link ImmutableItem ImmutableItem} instances. + */ + public Builder() { + if ( !( this instanceof Item.Builder ) ) { + throw new UnsupportedOperationException( "Use: new Item.Builder()" ); + } + } + + /** + * Fill a builder with attribute values from the provided {@code Item} instance. + * Regular attribute values will be replaced with those from the given instance. + * Absent optional values will not replace present values. + * Collection elements and entries will be added, not replaced. + * + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Item.Builder from(Item instance) { + Objects.requireNonNull( instance, "instance" ); + id( instance.getId() ); + putAllAttributes( instance.getAttributes() ); + return (Item.Builder) this; + } + + /** + * Initializes the value for the {@link Item#getId() id} attribute. + * + * @param id The value for id + * @return {@code this} builder for use in a chained invocation + */ + public final Item.Builder id(String id) { + this.id = Objects.requireNonNull( id, "id" ); + initBits &= ~INIT_BIT_ID; + return (Item.Builder) this; + } + + /** + * Put one entry to the {@link Item#getAttributes() attributes} map. + * + * @param key The key in the attributes map + * @param value The associated value in the attributes map + * @return {@code this} builder for use in a chained invocation + */ + public final Item.Builder putAttributes(String key, String value) { + this.attributes.put( + Objects.requireNonNull( key, "attributes key" ), + Objects.requireNonNull( value, "attributes value" ) + ); + return (Item.Builder) this; + } + + /** + * Put one entry to the {@link Item#getAttributes() attributes} map. Nulls are not permitted + * + * @param entry The key and value entry + * @return {@code this} builder for use in a chained invocation + */ + public final Item.Builder putAttributes(Map.Entry entry) { + String k = entry.getKey(); + String v = entry.getValue(); + this.attributes.put( + Objects.requireNonNull( k, "attributes key" ), + Objects.requireNonNull( v, "attributes value" ) + ); + return (Item.Builder) this; + } + + /** + * Sets or replaces all mappings from the specified map as entries for the {@link Item#getAttributes() attributes} map. Nulls are not permitted + * + * @param attributes The entries that will be added to the attributes map + * @return {@code this} builder for use in a chained invocation + */ + public final Item.Builder attributes(Map attributes) { + this.attributes.clear(); + return putAllAttributes( attributes ); + } + + /** + * Put all mappings from the specified map as entries to {@link Item#getAttributes() attributes} map. Nulls are not permitted + * + * @param attributes The entries that will be added to the attributes map + * @return {@code this} builder for use in a chained invocation + */ + public final Item.Builder putAllAttributes(Map attributes) { + for ( Map.Entry entry : attributes.entrySet() ) { + String k = entry.getKey(); + String v = entry.getValue(); + this.attributes.put( + Objects.requireNonNull( k, "attributes key" ), + Objects.requireNonNull( v, "attributes value" ) + ); + } + return (Item.Builder) this; + } + + /** + * Builds a new {@link ImmutableItem ImmutableItem}. + * + * @return An immutable instance of Item + * @throws java.lang.IllegalStateException if any required attributes are missing + */ + public ImmutableItem build() { + if ( initBits != 0 ) { + throw new IllegalStateException( formatRequiredAttributesMessage() ); + } + return new ImmutableItem( id, createUnmodifiableMap( false, false, attributes ) ); + } + + private String formatRequiredAttributesMessage() { + List attributes = new ArrayList(); + if ( ( initBits & INIT_BIT_ID ) != 0 ) { + attributes.add( "id" ); + } + return "Cannot build Item, some of required attributes are not set " + attributes; + } + } + + private static Map createUnmodifiableMap(boolean checkNulls, boolean skipNulls, + Map map) { + switch ( map.size() ) { + case 0: + return Collections.emptyMap(); + case 1: { + Map.Entry e = map.entrySet().iterator().next(); + K k = e.getKey(); + V v = e.getValue(); + if ( checkNulls ) { + Objects.requireNonNull( k, "key" ); + Objects.requireNonNull( v, "value" ); + } + if ( skipNulls && ( k == null || v == null ) ) { + return Collections.emptyMap(); + } + return Collections.singletonMap( k, v ); + } + default: { + Map linkedMap = new LinkedHashMap( map.size() ); + if ( skipNulls || checkNulls ) { + for ( Map.Entry e : map.entrySet() ) { + K k = e.getKey(); + V v = e.getValue(); + if ( skipNulls ) { + if ( k == null || v == null ) { + continue; + } + } + else if ( checkNulls ) { + Objects.requireNonNull( k, "key" ); + Objects.requireNonNull( v, "value" ); + } + linkedMap.put( k, v ); + } + } + else { + linkedMap.putAll( map ); + } + return Collections.unmodifiableMap( linkedMap ); + } + } + } +} \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/domain/Item.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/domain/Item.java new file mode 100644 index 0000000000..a78c3bc007 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/domain/Item.java @@ -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._3370.domain; + +import java.util.Collections; +import java.util.Map; + +public abstract class Item { + + public abstract String getId(); + + public abstract Map getAttributes(); + + public static Item.Builder builder() { + return new Item.Builder(); + } + + public static class Builder extends ImmutableItem.Builder { + + public ImmutableItem.Builder addSomeData(String key, String data) { + return super.attributes( Collections.singletonMap( key, data ) ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/dto/ItemDTO.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/dto/ItemDTO.java new file mode 100644 index 0000000000..70dba87fe4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3370/dto/ItemDTO.java @@ -0,0 +1,26 @@ +/* + * 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._3370.dto; + +import java.util.Map; + +public class ItemDTO { + private final String id; + private final Map attributes; + + public ItemDTO(String id, Map attributes) { + this.id = id; + this.attributes = attributes; + } + + public String getId() { + return id; + } + + public Map getAttributes() { + return attributes; + } +} From 21fdaa0f824fc358efc05507c591f86767fa3245 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 3 Nov 2024 12:54:37 +0100 Subject: [PATCH 459/597] #3747 Do not generate redundant if condition with constructor mapping and RETURN_DEFAULT null value mapping strategy --- .../ap/internal/model/BeanMappingMethod.ftl | 2 +- .../ap/test/bugs/_3747/Issue3747Mapper.java | 42 +++++++++++++++++++ .../ap/test/bugs/_3747/Issue3747Test.java | 28 +++++++++++++ .../test/bugs/_3747/Issue3747MapperImpl.java | 29 +++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3747/Issue3747Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3747/Issue3747Test.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_3747/Issue3747MapperImpl.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 61d9cc1837..3036e4a2c8 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -116,7 +116,7 @@ - <#else> + <#elseif !propertyMappingsByParameter(sourceParameters[0]).empty> <#if mapNullToDefault>if ( <@includeModel object=getPresenceCheckByParameter(sourceParameters[0]) /> ) { <#list propertyMappingsByParameter(sourceParameters[0]) as propertyMapping> <@includeModel object=propertyMapping targetBeanName=resultName existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3747/Issue3747Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3747/Issue3747Mapper.java new file mode 100644 index 0000000000..04addc43f3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3747/Issue3747Mapper.java @@ -0,0 +1,42 @@ +/* + * 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._3747; + +import org.mapstruct.Mapper; +import org.mapstruct.NullValueMappingStrategy; + +/** + * @author Filip Hrisafov + */ +@Mapper(nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT) +public interface Issue3747Mapper { + + Target map(Source source); + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Target { + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3747/Issue3747Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3747/Issue3747Test.java new file mode 100644 index 0000000000..57650e4dc0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3747/Issue3747Test.java @@ -0,0 +1,28 @@ +/* + * 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._3747; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +/** + * @author Filip Hrisafov + */ +@IssueKey("3747") +@WithClasses(Issue3747Mapper.class) +class Issue3747Test { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + void shouldNotGenerateObsoleteCode() { + generatedSource.addComparisonToFixtureFor( Issue3747Mapper.class ); + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_3747/Issue3747MapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_3747/Issue3747MapperImpl.java new file mode 100644 index 0000000000..adc937dab1 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_3747/Issue3747MapperImpl.java @@ -0,0 +1,29 @@ +/* + * 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._3747; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-11-03T12:50:02+0100", + comments = "version: , compiler: javac, environment: Java 21.0.3 (N/A)" +) +public class Issue3747MapperImpl implements Issue3747Mapper { + + @Override + public Target map(Source source) { + + String value = null; + if ( source != null ) { + value = source.getValue(); + } + + Target target = new Target( value ); + + return target; + } +} From c2bd847599c1cdfad562eb02ca47b16e8dfa56d6 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 9 Nov 2024 10:15:33 +0100 Subject: [PATCH 460/597] #3732 Do not generate obsolete imports for LocalDateTime <-> LocalDate conversion --- ...avaLocalDateTimeToLocalDateConversion.java | 16 ------- .../ap/test/bugs/_3732/Issue3732Mapper.java | 47 +++++++++++++++++++ .../ap/test/bugs/_3732/Issue3732Test.java | 22 +++++++++ 3 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3732/Issue3732Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3732/Issue3732Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaLocalDateTimeToLocalDateConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaLocalDateTimeToLocalDateConversion.java index ae571ad6b0..9116bada4c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaLocalDateTimeToLocalDateConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaLocalDateTimeToLocalDateConversion.java @@ -7,11 +7,8 @@ import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.Set; import org.mapstruct.ap.internal.model.common.ConversionContext; -import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.util.Collections; /** * SimpleConversion for mapping {@link LocalDateTime} to @@ -25,22 +22,9 @@ protected String getToExpression(ConversionContext conversionContext) { return ".toLocalDate()"; } - @Override - protected Set getToConversionImportTypes(ConversionContext conversionContext) { - return Collections.asSet( - conversionContext.getTypeFactory().getType( LocalDate.class ) - ); - } - @Override protected String getFromExpression(ConversionContext conversionContext) { return ".atStartOfDay()"; } - @Override - protected Set getFromConversionImportTypes(ConversionContext conversionContext) { - return Collections.asSet( - conversionContext.getTypeFactory().getType( LocalDateTime.class ) - ); - } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3732/Issue3732Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3732/Issue3732Mapper.java new file mode 100644 index 0000000000..79b13aa8ce --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3732/Issue3732Mapper.java @@ -0,0 +1,47 @@ +/* + * 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._3732; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3732Mapper { + + Target map(Source source); + + Source map(Target source); + + class Source { + private LocalDateTime value; + + public LocalDateTime getValue() { + return value; + } + + public void setValue(LocalDateTime value) { + this.value = value; + } + } + + class Target { + + private LocalDate value; + + public LocalDate getValue() { + return value; + } + + public void setValue(LocalDate value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3732/Issue3732Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3732/Issue3732Test.java new file mode 100644 index 0000000000..9dbd9c0bb8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3732/Issue3732Test.java @@ -0,0 +1,22 @@ +/* + * 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._3732; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Filip Hrisafov + */ +@IssueKey("3732") +@WithClasses({ Issue3732Mapper.class }) +class Issue3732Test { + + @ProcessorTest + void shouldGenerateCorrectMapper() { + } +} From efdf435770dd4361ccfa6e64d643cc88f156e79e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 9 Nov 2024 10:44:54 +0100 Subject: [PATCH 461/597] #3751 Improve readme to include support for Java 16+ records --- readme.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 7903ddeb84..6932f4d748 100644 --- a/readme.md +++ b/readme.md @@ -19,7 +19,10 @@ ## What is MapStruct? -MapStruct is a Java [annotation processor](https://docs.oracle.com/javase/6/docs/technotes/guides/apt/index.html) for the generation of type-safe and performant mappers for Java bean classes. It saves you from writing mapping code by hand, which is a tedious and error-prone task. The generator comes with sensible defaults and many built-in type conversions, but it steps out of your way when it comes to configuring or implementing special behavior. +MapStruct is a Java [annotation processor](https://docs.oracle.com/en/java/javase/21/docs/specs/man/javac.html#annotation-processing) designed to generate type-safe and high-performance mappers for Java bean classes, including support for Java 16+ records. +By automating the creation of mappings, MapStruct eliminates the need for tedious and error-prone manual coding. +The generator provides sensible defaults and built-in type conversions, allowing it to handle standard mappings effortlessly, while also offering flexibility for custom configurations or specialized mapping behaviors. +With seamless integration into modern Java projects, MapStruct can map between conventional beans, records, and even complex hierarchies, making it an adaptable tool for diverse Java applications. Compared to mapping frameworks working at runtime, MapStruct offers the following advantages: From 772fae4c77ccd0d35c33afa7f04431090a964692 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 9 Nov 2024 12:20:14 +0100 Subject: [PATCH 462/597] Prepare release notes for 1.6.3 --- NEXT_RELEASE_CHANGELOG.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index e0f4cd31f0..dca36c820a 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,10 +1,9 @@ -### Features - -### Enhancements - ### Bugs -### Documentation +* Redundant if condition in Java record mapping with `RETURN_DEFAULT` strategy (#3747) +* Stackoverflow with Immutables custom builder (#3370) +* Unused import of `java.time.LocalDate` when mapping source `LocalDateTime` to target `LocalDate` (#3732) -### Build +### Documentation +* Add section to README.md comparing mapstruct with Java Records (#3751) From b4e25e49deae707b50ce061172e114292b414a23 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2024 11:31:12 +0000 Subject: [PATCH 463/597] Releasing version 1.6.3 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0b3ca1c113..e60cda2afe 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.3 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index c73676192b..adec24c93b 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.3 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index e62d5e4682..e276a889ce 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.3 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index f826421092..4d820495cd 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.3 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 21a2b151bd..45f4eb19c6 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.3 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 7c91837e79..d686c5a368 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.3 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 94c84fb4d0..ded84caf2e 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.3 pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - ${git.commit.author.time} + 2024-11-09T11:31:12Z 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index f55f0955d3..9df5b02892 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.3 parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index ed2df7718b..7410fd0a35 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.6.3 ../parent/pom.xml From 5bf2b152afcb5e3f4335f35ea9e0d747963e4298 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2024 11:40:01 +0000 Subject: [PATCH 464/597] Next version 1.7.0-SNAPSHOT --- NEXT_RELEASE_CHANGELOG.md | 11 ++++++----- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 10 files changed, 16 insertions(+), 15 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index dca36c820a..e0f4cd31f0 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,9 +1,10 @@ -### Bugs +### Features + +### Enhancements -* Redundant if condition in Java record mapping with `RETURN_DEFAULT` strategy (#3747) -* Stackoverflow with Immutables custom builder (#3370) -* Unused import of `java.time.LocalDate` when mapping source `LocalDateTime` to target `LocalDate` (#3732) +### Bugs ### Documentation -* Add section to README.md comparing mapstruct with Java Records (#3751) +### Build + diff --git a/build-config/pom.xml b/build-config/pom.xml index e60cda2afe..0b3ca1c113 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.3 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index adec24c93b..c73676192b 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.3 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index e276a889ce..e62d5e4682 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.3 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 4d820495cd..f826421092 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.3 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 45f4eb19c6..21a2b151bd 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.3 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index d686c5a368..7c91837e79 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.3 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index ded84caf2e..94c84fb4d0 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.6.3 + 1.7.0-SNAPSHOT pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - 2024-11-09T11:31:12Z + ${git.commit.author.time} 1.0.0.Alpha3 3.4.1 diff --git a/pom.xml b/pom.xml index 9df5b02892..f55f0955d3 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.6.3 + 1.7.0-SNAPSHOT parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 7410fd0a35..ed2df7718b 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.6.3 + 1.7.0-SNAPSHOT ../parent/pom.xml From 0df3f6af957c1468ae4baaf85b528131fbde9711 Mon Sep 17 00:00:00 2001 From: cussle <109949453+cussle@users.noreply.github.com> Date: Thu, 14 Nov 2024 05:22:57 +0900 Subject: [PATCH 465/597] docs & refactor: fix typos and improve readability in multiple classes (#3767) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AnnotatedConstructor: Fixed a variable name typo (noArgConstructorToInBecluded → noArgConstructorToBeIncluded). - AbstractBaseBuilder: Improved Javadoc by fixing typos and clarifying wording. - SourceRhsSelector: Corrected a typo in the class-level Javadoc. - InheritanceSelector: Enhanced readability by fixing typos and refining comments. --- .../ap/internal/model/AbstractBaseBuilder.java | 4 ++-- .../ap/internal/model/AnnotatedConstructor.java | 8 ++++---- .../model/source/selector/InheritanceSelector.java | 10 +++++----- .../model/source/selector/SourceRhsSelector.java | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java index 311cc00368..56a1831d52 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java @@ -41,7 +41,7 @@ public B method(Method sourceMethod) { } /** - * Checks if MapStruct is allowed to generate an automatic sub-mapping between {@code sourceType} and @{code + * Checks if MapStruct is allowed to generate an automatic sub-mapping between {@code sourceType} and {@code * targetType}. * This will evaluate to {@code true}, when: *

    11. @@ -66,7 +66,7 @@ private boolean isDisableSubMappingMethodsGeneration() { /** * Creates a forged assignment from the provided {@code sourceRHS} and {@code forgedMethod}. If a mapping method - * for the {@code forgedMethod} already exists, then this method used for the assignment. + * for the {@code forgedMethod} already exists, this method will be used for the assignment. * * @param sourceRHS that needs to be used for the assignment * @param forgedMethod the forged method for which we want to create an {@link Assignment} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedConstructor.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedConstructor.java index 1c38b1062b..889d602cdb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedConstructor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedConstructor.java @@ -36,14 +36,14 @@ public static AnnotatedConstructor forComponentModels(String name, if ( constructor instanceof NoArgumentConstructor ) { noArgumentConstructor = (NoArgumentConstructor) constructor; } - NoArgumentConstructor noArgConstructorToInBecluded = null; + NoArgumentConstructor noArgConstructorToBeIncluded = null; Set fragmentsToBeIncluded = Collections.emptySet(); if ( includeNoArgConstructor ) { if ( noArgumentConstructor != null ) { - noArgConstructorToInBecluded = noArgumentConstructor; + noArgConstructorToBeIncluded = noArgumentConstructor; } else { - noArgConstructorToInBecluded = new NoArgumentConstructor( name, fragmentsToBeIncluded ); + noArgConstructorToBeIncluded = new NoArgumentConstructor( name, fragmentsToBeIncluded ); } } else if ( noArgumentConstructor != null ) { @@ -53,7 +53,7 @@ else if ( noArgumentConstructor != null ) { name, mapperReferences, annotations, - noArgConstructorToInBecluded, + noArgConstructorToBeIncluded, fragmentsToBeIncluded ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java index 297dcaa4a3..9a4fba6e30 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/InheritanceSelector.java @@ -33,7 +33,7 @@ public List> getMatchingMethods(List int addToCandidateListIfMinimal(List> candidatesWithBestMathingType, + private int addToCandidateListIfMinimal(List> candidatesWithBestMatchingType, int bestMatchingTypeDistance, SelectedMethod method, int currentTypeDistance) { if ( currentTypeDistance == bestMatchingTypeDistance ) { - candidatesWithBestMathingType.add( method ); + candidatesWithBestMatchingType.add( method ); } else if ( currentTypeDistance < bestMatchingTypeDistance ) { bestMatchingTypeDistance = currentTypeDistance; - candidatesWithBestMathingType.clear(); - candidatesWithBestMathingType.add( method ); + candidatesWithBestMatchingType.clear(); + candidatesWithBestMatchingType.add( method ); } return bestMatchingTypeDistance; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java index fb797f5808..91a30902e0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SourceRhsSelector.java @@ -12,7 +12,7 @@ import org.mapstruct.ap.internal.model.source.Method; /** - * Selector that tries to resolve an ambiquity between methods that contain source parameters and + * Selector that tries to resolve an ambiguity between methods that contain source parameters and * {@link org.mapstruct.ap.internal.model.common.SourceRHS SourceRHS} type parameters. * @author Filip Hrisafov */ From 8de18e5a65a353e6ff12f6a8f130a08173bf9672 Mon Sep 17 00:00:00 2001 From: hsjni0110 <92289935+hsjni0110@users.noreply.github.com> Date: Thu, 14 Nov 2024 05:35:20 +0900 Subject: [PATCH 466/597] fix typos in method and variable names (#3766) --- .../internal/model/source/SourceMethod.java | 2 +- .../processor/MapperCreationProcessor.java | 26 +++++++++---------- .../processor/MethodRetrievalProcessor.java | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 7103fb2858..8427dd3eca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -186,7 +186,7 @@ public Builder setPrototypeMethods(List prototypeMethods) { return this; } - public Builder setDefininingType(Type definingType) { + public Builder setDefiningType(Type definingType) { this.definingType = definingType; return this; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 5fc1b07823..3b38a05b59 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -195,7 +195,7 @@ private Mapper getMapper(TypeElement element, MapperOptions mapperOptions, List< addAllFieldsIn( mappingContext.getUsedSupportedMappings(), supportingFieldSet ); fields.addAll( supportingFieldSet ); - // handle constructorfragments + // handle constructor fragments Set constructorFragments = new LinkedHashSet<>(); addAllFragmentsIn( mappingContext.getUsedSupportedMappings(), constructorFragments ); @@ -632,7 +632,7 @@ else if ( nameFilteredcandidates.size() > 1 ) { reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, inverseConfiguration ); } else { - reportErrorWhenAmbigousReverseMapping( candidates, method, inverseConfiguration ); + reportErrorWhenAmbiguousReverseMapping( candidates, method, inverseConfiguration ); } } } @@ -702,21 +702,21 @@ else if ( sourceMethod.getName().equals( name ) ) { else if ( candidates.size() > 1 ) { // ambiguity: find a matching method that matches configuredBy - List nameFilteredcandidates = new ArrayList<>(); + List nameFilteredCandidates = new ArrayList<>(); for ( SourceMethod candidate : candidates ) { if ( candidate.getName().equals( name ) ) { - nameFilteredcandidates.add( candidate ); + nameFilteredCandidates.add( candidate ); } } - if ( nameFilteredcandidates.size() == 1 ) { - resultMethod = first( nameFilteredcandidates ); + if ( nameFilteredCandidates.size() == 1 ) { + resultMethod = first( nameFilteredCandidates ); } - else if ( nameFilteredcandidates.size() > 1 ) { - reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, inheritConfiguration ); + else if ( nameFilteredCandidates.size() > 1 ) { + reportErrorWhenSeveralNamesMatch( nameFilteredCandidates, method, inheritConfiguration ); } else { - reportErrorWhenAmbigousMapping( candidates, method, inheritConfiguration ); + reportErrorWhenAmbiguousMapping( candidates, method, inheritConfiguration ); } } } @@ -729,8 +729,8 @@ private AnnotationMirror getAnnotationMirror(InheritConfigurationGem inheritConf return inheritConfiguration == null ? null : inheritConfiguration.mirror(); } - private void reportErrorWhenAmbigousReverseMapping(List candidates, SourceMethod method, - InheritInverseConfigurationGem inverseGem) { + private void reportErrorWhenAmbiguousReverseMapping(List candidates, SourceMethod method, + InheritInverseConfigurationGem inverseGem) { List candidateNames = new ArrayList<>(); for ( SourceMethod candidate : candidates ) { @@ -780,8 +780,8 @@ private void reportErrorWhenNonMatchingName(SourceMethod onlyCandidate, SourceMe ); } - private void reportErrorWhenAmbigousMapping(List candidates, SourceMethod method, - InheritConfigurationGem gem) { + private void reportErrorWhenAmbiguousMapping(List candidates, SourceMethod method, + InheritConfigurationGem gem) { List candidateNames = new ArrayList<>(); for ( SourceMethod candidate : candidates ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 4bd2ed48a5..2c8ad5dcb9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -389,7 +389,7 @@ private SourceMethod getReferencedMethod(TypeElement usedMapper, ExecutableType return new SourceMethod.Builder() .setDeclaringMapper( usedMapper.equals( mapperToImplement ) ? null : usedMapperAsType ) - .setDefininingType( definingType ) + .setDefiningType( definingType ) .setExecutable( method ) .setParameters( parameters ) .setReturnType( returnType ) From bee983cd3c627333a613a58be555090ac754da95 Mon Sep 17 00:00:00 2001 From: Minji Kim <101392857+alsswl@users.noreply.github.com> Date: Sat, 16 Nov 2024 06:13:36 +0900 Subject: [PATCH 467/597] Fix typos in comments (#3769) --- distribution/pom.xml | 2 +- .../org/mapstruct/ap/internal/model/BeanMappingMethod.java | 6 +++--- .../mapstruct/ap/internal/model/MappingBuilderContext.java | 2 +- .../internal/model/NestedTargetPropertyMappingHolder.java | 4 ++-- .../org/mapstruct/ap/internal/model/ValueMappingMethod.java | 4 ++-- .../java/org/mapstruct/ap/spi/AccessorNamingStrategy.java | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/distribution/pom.xml b/distribution/pom.xml index f826421092..db654236e4 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -130,7 +130,7 @@ enumToStringMapping(Method method, Type sourceType ) return mappings; } - // Start to fill the mappings with the defined valuemappings + // Start to fill the mappings with the defined valueMappings for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) { mappings.add( new MappingEntry( valueMapping.getSource(), valueMapping.getTarget() ) ); unmappedSourceConstants.remove( valueMapping.getSource() ); @@ -305,7 +305,7 @@ private List stringToEnumMapping(Method method, Type targetType ) } Set mappedSources = new LinkedHashSet<>(); - // Start to fill the mappings with the defined valuemappings + // Start to fill the mappings with the defined value mappings for ( ValueMappingOptions valueMapping : valueMappings.regularValueMappings ) { mappedSources.add( valueMapping.getSource() ); mappings.add( new MappingEntry( valueMapping.getSource(), valueMapping.getTarget() ) ); diff --git a/processor/src/main/java/org/mapstruct/ap/spi/AccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/AccessorNamingStrategy.java index c9d0734753..b4dde64324 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/AccessorNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/AccessorNamingStrategy.java @@ -68,7 +68,7 @@ default void init(MapStructProcessingEnvironment processingEnvironment) { * * @return getter name for collection properties * - * @deprecated MapStuct will not call this method anymore. Use {@link #getMethodType(ExecutableElement)} to + * @deprecated MapStruct will not call this method anymore. Use {@link #getMethodType(ExecutableElement)} to * determine the {@link MethodType}. When collections somehow need to be treated special, it should be done in * {@link #getMethodType(ExecutableElement) } as well. In the future, this method will be removed. */ From 737af6b50a1641d936c8a5b152bb97bf1f843d18 Mon Sep 17 00:00:00 2001 From: Roman Obolonskyi <65775868+Obolrom@users.noreply.github.com> Date: Sun, 17 Nov 2024 17:46:59 +0200 Subject: [PATCH 468/597] #3628 Add support for locale parameter for numberFormat and dateFormat --- .../java/org/mapstruct/IterableMapping.java | 19 + .../main/java/org/mapstruct/MapMapping.java | 27 + core/src/main/java/org/mapstruct/Mapping.java | 19 + .../BigDecimalToStringConversion.java | 20 +- .../BigIntegerToStringConversion.java | 20 +- .../internal/conversion/ConversionUtils.java | 12 + .../conversion/CreateDecimalFormat.java | 37 +- .../conversion/DateToStringConversion.java | 35 +- .../PrimitiveToStringConversion.java | 39 +- .../conversion/WrapperToStringConversion.java | 39 +- .../model/common/ConversionContext.java | 2 + .../common/DefaultConversionContext.java | 7 + .../model/common/FormattingParameters.java | 10 +- .../model/source/IterableMappingOptions.java | 3 +- .../model/source/MapMappingOptions.java | 14 +- .../internal/model/source/MappingOptions.java | 9 +- .../conversion/CreateDecimalFormat.ftl | 7 +- .../common/DefaultConversionContextTest.java | 6 +- .../conversion/date/DateConversionTest.java | 120 ++++ .../conversion/date/SourceTargetMapper.java | 24 +- .../numbers/NumberFormatConversionTest.java | 126 ++++- .../numbers/SourceTargetMapper.java | 41 +- .../numbers/SourceTargetMapperImpl.java | 525 ++++++++++++++++++ .../numbers/SourceTargetMapperImpl.java | 525 ++++++++++++++++++ 24 files changed, 1631 insertions(+), 55 deletions(-) create mode 100644 processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java diff --git a/core/src/main/java/org/mapstruct/IterableMapping.java b/core/src/main/java/org/mapstruct/IterableMapping.java index 76153127bd..d644dfe03b 100644 --- a/core/src/main/java/org/mapstruct/IterableMapping.java +++ b/core/src/main/java/org/mapstruct/IterableMapping.java @@ -66,19 +66,38 @@ /** * A format string as processable by {@link SimpleDateFormat} if the annotated method maps from an iterable of * {@code String} to an iterable {@link Date} or vice-versa. Will be ignored for all other element types. + *

      + * If the {@link #locale()} is also specified, the format will consider the specified locale when processing + * the date. Otherwise, the system's default locale will be used. * * @return A date format string as processable by {@link SimpleDateFormat}. + * @see #locale() */ String dateFormat() default ""; /** * A format string as processable by {@link DecimalFormat} if the annotated method maps from a * {@link Number} to a {@link String} or vice-versa. Will be ignored for all other element types. + *

      + * If the {@link #locale()} is also specified, the number format will be applied in the context of the given locale. + * Otherwise, the system's default locale will be used to process the number format. * * @return A decimal format string as processable by {@link DecimalFormat}. + * @see #locale() */ String numberFormat() default ""; + /** + * Specifies the locale to be used when processing {@link #dateFormat()} or {@link #numberFormat()}. + *

      + * The locale should be a plain tag representing the language, such as "en" for English, "de" for German, etc. + *

      + * If no locale is specified, the system's default locale will be used. + * + * @return A string representing the locale to be used when formatting dates or numbers. + */ + String locale() default ""; + /** * A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple * mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found' error. diff --git a/core/src/main/java/org/mapstruct/MapMapping.java b/core/src/main/java/org/mapstruct/MapMapping.java index 271272bb45..093099cf5a 100644 --- a/core/src/main/java/org/mapstruct/MapMapping.java +++ b/core/src/main/java/org/mapstruct/MapMapping.java @@ -56,8 +56,12 @@ /** * A format string as processable by {@link SimpleDateFormat} if the annotated method maps from a map with key type * {@code String} to an map with key type {@link Date} or vice-versa. Will be ignored for all other key types. + *

      + * If the {@link #locale()} is specified, the format will consider the specified locale when processing the date. + * Otherwise, the system's default locale will be used. * * @return A date format string as processable by {@link SimpleDateFormat}. + * @see #locale() */ String keyDateFormat() default ""; @@ -65,27 +69,50 @@ * A format string as processable by {@link SimpleDateFormat} if the annotated method maps from a map with value * type {@code String} to an map with value type {@link Date} or vice-versa. Will be ignored for all other value * types. + *

      + * If the {@link #locale()} is specified, the format will consider the specified locale when processing the date. + * Otherwise, the system's default locale will be used. * * @return A date format string as processable by {@link SimpleDateFormat}. + * @see #locale() */ String valueDateFormat() default ""; /** * A format string as processable by {@link DecimalFormat} if the annotated method maps from a * {@link Number} to a {@link String} or vice-versa. Will be ignored for all other key types. + *

      + * If the {@link #locale()} is specified, the number format will be applied in the context of the given locale. + * Otherwise, the system's default locale will be used. * * @return A decimal format string as processable by {@link DecimalFormat}. + * @see #locale() */ String keyNumberFormat() default ""; /** * A format string as processable by {@link DecimalFormat} if the annotated method maps from a * {@link Number} to a {@link String} or vice-versa. Will be ignored for all other value types. + *

      + * If the {@link #locale()} is specified, the number format will be applied in the context of the given locale. + * Otherwise, the system's default locale will be used. * * @return A decimal format string as processable by {@link DecimalFormat}. + * @see #locale() */ String valueNumberFormat() default ""; + /** + * Specifies the locale to be used when processing {@link SimpleDateFormat} or {@link DecimalFormat} for key or + * value mappings in maps. The locale should be a plain tag representing the language, such as "en" for English, + * "de" for German, etc. + *

      + * If no locale is specified, the system's default locale will be used. + * + * @return A string representing the locale to be used when formatting dates or numbers in maps. + */ + String locale() default ""; + /** * A key value qualifier can be specified to aid the selection process of a suitable mapper. This is useful in * case multiple mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found' diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 8b0c4adb0c..6c95b0db2c 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -175,19 +175,38 @@ /** * A format string as processable by {@link SimpleDateFormat} if the attribute is mapped from {@code String} to * {@link Date} or vice-versa. Will be ignored for all other attribute types and when mapping enum constants. + *

      + * If the {@link #locale()} is also specified, the format will consider the specified locale when processing + * the date. Otherwise, the system's default locale will be used. * * @return A date format string as processable by {@link SimpleDateFormat}. + * @see #locale() */ String dateFormat() default ""; /** * A format string as processable by {@link DecimalFormat} if the annotated method maps from a * {@link Number} to a {@link String} or vice-versa. Will be ignored for all other element types. + *

      + * If the {@link #locale()} is also specified, the number format will be applied in the context of the given locale. + * Otherwise, the system's default locale will be used to process the number format. * * @return A decimal format string as processable by {@link DecimalFormat}. + * @see #locale() */ String numberFormat() default ""; + /** + * Specifies the locale to be used when processing {@link #dateFormat()} or {@link #numberFormat()}. + *

      + * The locale should be a plain tag representing the language, such as "en" for English, "de" for German, etc. + *

      + * If no locale is specified, the system's default locale will be used. + * + * @return A string representing the locale to be used when formatting dates or numbers. + */ + String locale() default ""; + /** * A constant {@link String} based on which the specified target property is to be set. *

      diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java index 92a93e3893..384013a7b3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigDecimalToStringConversion.java @@ -14,8 +14,9 @@ import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.Type; -import static org.mapstruct.ap.internal.util.Collections.asSet; import static org.mapstruct.ap.internal.conversion.ConversionUtils.bigDecimal; +import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale; +import static org.mapstruct.ap.internal.util.Collections.asSet; /** * Conversion between {@link BigDecimal} and {@link String}. @@ -64,18 +65,31 @@ protected Set getFromConversionImportTypes(ConversionContext conversionCon public List getRequiredHelperMethods(ConversionContext conversionContext) { List helpers = new ArrayList<>(); if ( conversionContext.getNumberFormat() != null ) { - helpers.add( new CreateDecimalFormat( conversionContext.getTypeFactory() ) ); + helpers.add( new CreateDecimalFormat( + conversionContext.getTypeFactory(), + conversionContext.getLocale() != null + ) ); } return helpers; } private void appendDecimalFormatter(StringBuilder sb, ConversionContext conversionContext) { - sb.append( "createDecimalFormat( " ); + boolean withLocale = conversionContext.getLocale() != null; + sb.append( "createDecimalFormat" ); + if ( withLocale ) { + sb.append( "WithLocale" ); + } + sb.append( "( " ); if ( conversionContext.getNumberFormat() != null ) { sb.append( "\"" ); sb.append( conversionContext.getNumberFormat() ); sb.append( "\"" ); } + if ( withLocale ) { + sb.append( ", " ).append( locale( conversionContext ) ).append( ".forLanguageTag( \"" ); + sb.append( conversionContext.getLocale() ); + sb.append( "\" )" ); + } sb.append( " )" ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java index df0a48a673..540d89db58 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/BigIntegerToStringConversion.java @@ -15,9 +15,10 @@ import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.Type; -import static org.mapstruct.ap.internal.util.Collections.asSet; +import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale; import static org.mapstruct.ap.internal.conversion.ConversionUtils.bigDecimal; import static org.mapstruct.ap.internal.conversion.ConversionUtils.bigInteger; +import static org.mapstruct.ap.internal.util.Collections.asSet; /** * Conversion between {@link BigInteger} and {@link String}. @@ -72,18 +73,31 @@ protected Set getFromConversionImportTypes(ConversionContext conversionCon public List getRequiredHelperMethods(ConversionContext conversionContext) { List helpers = new ArrayList<>(); if ( conversionContext.getNumberFormat() != null ) { - helpers.add( new CreateDecimalFormat( conversionContext.getTypeFactory() ) ); + helpers.add( new CreateDecimalFormat( + conversionContext.getTypeFactory(), + conversionContext.getLocale() != null + ) ); } return helpers; } private void appendDecimalFormatter(StringBuilder sb, ConversionContext conversionContext) { - sb.append( "createDecimalFormat( " ); + boolean withLocale = conversionContext.getLocale() != null; + sb.append( "createDecimalFormat" ); + if ( withLocale ) { + sb.append( "WithLocale" ); + } + sb.append( "( " ); if ( conversionContext.getNumberFormat() != null ) { sb.append( "\"" ); sb.append( conversionContext.getNumberFormat() ); sb.append( "\"" ); } + if ( withLocale ) { + sb.append( ", " ).append( locale( conversionContext ) ).append( ".forLanguageTag( \"" ); + sb.append( conversionContext.getLocale() ); + sb.append( "\" )" ); + } sb.append( " )" ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java index aa01a73276..96960c4a11 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java @@ -11,6 +11,7 @@ import java.sql.Time; import java.sql.Timestamp; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; @@ -279,4 +280,15 @@ public static String url(ConversionContext conversionContext) { return typeReferenceName( conversionContext, URL.class ); } + /** + * Name for {@link java.text.DecimalFormatSymbols}. + * + * @param conversionContext Conversion context + * + * @return Name or fully-qualified name. + */ + public static String decimalFormatSymbols(ConversionContext conversionContext) { + return typeReferenceName( conversionContext, DecimalFormatSymbols.class ); + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java index 77c59445ab..d1b49cff69 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.java @@ -5,9 +5,11 @@ */ package org.mapstruct.ap.internal.conversion; -import static org.mapstruct.ap.internal.util.Collections.asSet; - import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; import java.util.Set; import org.mapstruct.ap.internal.model.HelperMethod; @@ -16,6 +18,8 @@ import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.MappingMethodOptions; +import static org.mapstruct.ap.internal.util.Collections.asSet; + /** * HelperMethod that creates a {@link java.text.DecimalFormat} * @@ -27,13 +31,30 @@ public class CreateDecimalFormat extends HelperMethod { private final Parameter parameter; + private final Parameter localeParameter; private final Type returnType; private final Set importTypes; - public CreateDecimalFormat(TypeFactory typeFactory) { + public CreateDecimalFormat(TypeFactory typeFactory, boolean withLocale) { this.parameter = new Parameter( "numberFormat", typeFactory.getType( String.class ) ); + this.localeParameter = withLocale ? new Parameter( "locale", typeFactory.getType( Locale.class ) ) : null; this.returnType = typeFactory.getType( DecimalFormat.class ); - this.importTypes = asSet( parameter.getType(), returnType ); + if ( withLocale ) { + this.importTypes = asSet( + parameter.getType(), + returnType, + typeFactory.getType( DecimalFormatSymbols.class ), + typeFactory.getType( Locale.class ) + ); + } + else { + this.importTypes = asSet( parameter.getType(), returnType ); + } + } + + @Override + public String getName() { + return localeParameter == null ? "createDecimalFormat" : "createDecimalFormatWithLocale"; } @Override @@ -60,4 +81,12 @@ public MappingMethodOptions getOptions() { public String describe() { return null; } + + @Override + public List getParameters() { + if ( localeParameter == null ) { + return super.getParameters(); + } + return Arrays.asList( getParameter(), localeParameter ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java index 361ce9d9ab..35c7f88d74 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java @@ -10,15 +10,18 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Locale; +import java.util.Set; import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.TypeConversion; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; -import static java.util.Arrays.asList; -import static org.mapstruct.ap.internal.util.Collections.asSet; +import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale; import static org.mapstruct.ap.internal.conversion.ConversionUtils.simpleDateFormat; +import static org.mapstruct.ap.internal.util.Collections.asSet; /** * Conversion between {@link String} and {@link Date}. @@ -29,7 +32,7 @@ public class DateToStringConversion implements ConversionProvider { @Override public Assignment to(ConversionContext conversionContext) { - return new TypeConversion( asSet( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) ), + return new TypeConversion( getImportTypes( conversionContext ), Collections.emptyList(), getConversionExpression( conversionContext, "format" ) ); @@ -37,8 +40,8 @@ public Assignment to(ConversionContext conversionContext) { @Override public Assignment from(ConversionContext conversionContext) { - return new TypeConversion( asSet( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) ), - asList( conversionContext.getTypeFactory().getType( ParseException.class ) ), + return new TypeConversion( getImportTypes( conversionContext ), + Collections.singletonList( conversionContext.getTypeFactory().getType( ParseException.class ) ), getConversionExpression( conversionContext, "parse" ) ); } @@ -48,6 +51,17 @@ public List getRequiredHelperMethods(ConversionContext conversionC return Collections.emptyList(); } + private Set getImportTypes(ConversionContext conversionContext) { + if ( conversionContext.getLocale() == null ) { + return Collections.singleton( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) ); + } + + return asSet( + conversionContext.getTypeFactory().getType( SimpleDateFormat.class ), + conversionContext.getTypeFactory().getType( Locale.class ) + ); + } + private String getConversionExpression(ConversionContext conversionContext, String method) { StringBuilder conversionString = new StringBuilder( "new " ); conversionString.append( simpleDateFormat( conversionContext ) ); @@ -56,7 +70,16 @@ private String getConversionExpression(ConversionContext conversionContext, Stri if ( conversionContext.getDateFormat() != null ) { conversionString.append( " \"" ); conversionString.append( conversionContext.getDateFormat() ); - conversionString.append( "\" " ); + conversionString.append( "\"" ); + + if ( conversionContext.getLocale() != null ) { + conversionString.append( ", " ).append( locale( conversionContext ) ).append( ".forLanguageTag( \"" ); + conversionString.append( conversionContext.getLocale() ); + conversionString.append( "\" ) " ); + } + else { + conversionString.append( " " ); + } } conversionString.append( ")." ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/PrimitiveToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/PrimitiveToStringConversion.java index fcc7241290..909ce8c0f2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/PrimitiveToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/PrimitiveToStringConversion.java @@ -6,7 +6,9 @@ package org.mapstruct.ap.internal.conversion; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.Collections; +import java.util.Locale; import java.util.Set; import org.mapstruct.ap.internal.model.common.ConversionContext; @@ -15,6 +17,9 @@ import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.conversion.ConversionUtils.decimalFormat; +import static org.mapstruct.ap.internal.conversion.ConversionUtils.decimalFormatSymbols; +import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale; +import static org.mapstruct.ap.internal.util.Collections.asSet; /** * Conversion between primitive types such as {@code byte} or {@code long} and @@ -53,9 +58,15 @@ public String getToExpression(ConversionContext conversionContext) { @Override public Set getToConversionImportTypes(ConversionContext conversionContext) { if ( requiresDecimalFormat( conversionContext ) ) { - return Collections.singleton( - conversionContext.getTypeFactory().getType( DecimalFormat.class ) - ); + if ( conversionContext.getLocale() != null ) { + return asSet( + conversionContext.getTypeFactory().getType( DecimalFormat.class ), + conversionContext.getTypeFactory().getType( DecimalFormatSymbols.class ), + conversionContext.getTypeFactory().getType( Locale.class ) + ); + } + + return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) ); } return Collections.emptySet(); @@ -80,9 +91,15 @@ public String getFromExpression(ConversionContext conversionContext) { @Override protected Set getFromConversionImportTypes(ConversionContext conversionContext) { if ( requiresDecimalFormat( conversionContext ) ) { - return Collections.singleton( - conversionContext.getTypeFactory().getType( DecimalFormat.class ) - ); + if ( conversionContext.getLocale() != null ) { + return asSet( + conversionContext.getTypeFactory().getType( DecimalFormat.class ), + conversionContext.getTypeFactory().getType( DecimalFormatSymbols.class ), + conversionContext.getTypeFactory().getType( Locale.class ) + ); + } + + return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) ); } return Collections.emptySet(); @@ -97,6 +114,16 @@ private void appendDecimalFormatter(StringBuilder sb, ConversionContext conversi sb.append( "\"" ); sb.append( conversionContext.getNumberFormat() ); sb.append( "\"" ); + + if ( conversionContext.getLocale() != null ) { + sb.append( ", " ) + .append( decimalFormatSymbols( conversionContext ) ) + .append( ".getInstance( " ) + .append( locale( conversionContext ) ) + .append( ".forLanguageTag( \"" ) + .append( conversionContext.getLocale() ) + .append( " \" ) )" ); + } } sb.append( " )" ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/WrapperToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/WrapperToStringConversion.java index 300c901216..dd7b6bac8d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/WrapperToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/WrapperToStringConversion.java @@ -6,7 +6,9 @@ package org.mapstruct.ap.internal.conversion; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.Collections; +import java.util.Locale; import java.util.Set; import org.mapstruct.ap.internal.model.common.ConversionContext; @@ -15,6 +17,9 @@ import org.mapstruct.ap.internal.util.Strings; import static org.mapstruct.ap.internal.conversion.ConversionUtils.decimalFormat; +import static org.mapstruct.ap.internal.conversion.ConversionUtils.decimalFormatSymbols; +import static org.mapstruct.ap.internal.conversion.ConversionUtils.locale; +import static org.mapstruct.ap.internal.util.Collections.asSet; /** * Conversion between wrapper types such as {@link Integer} and {@link String}. @@ -52,9 +57,15 @@ public String getToExpression(ConversionContext conversionContext) { @Override public Set getToConversionImportTypes(ConversionContext conversionContext) { if ( requiresDecimalFormat( conversionContext ) ) { - return Collections.singleton( - conversionContext.getTypeFactory().getType( DecimalFormat.class ) - ); + if ( conversionContext.getLocale() != null ) { + return asSet( + conversionContext.getTypeFactory().getType( DecimalFormat.class ), + conversionContext.getTypeFactory().getType( DecimalFormatSymbols.class ), + conversionContext.getTypeFactory().getType( Locale.class ) + ); + } + + return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) ); } return Collections.emptySet(); @@ -79,9 +90,15 @@ public String getFromExpression(ConversionContext conversionContext) { @Override protected Set getFromConversionImportTypes(ConversionContext conversionContext) { if ( requiresDecimalFormat( conversionContext ) ) { - return Collections.singleton( - conversionContext.getTypeFactory().getType( DecimalFormat.class ) - ); + if ( conversionContext.getLocale() != null ) { + return asSet( + conversionContext.getTypeFactory().getType( DecimalFormat.class ), + conversionContext.getTypeFactory().getType( DecimalFormatSymbols.class ), + conversionContext.getTypeFactory().getType( Locale.class ) + ); + } + + return Collections.singleton( conversionContext.getTypeFactory().getType( DecimalFormat.class ) ); } return Collections.emptySet(); @@ -96,6 +113,16 @@ private void appendDecimalFormatter(StringBuilder sb, ConversionContext conversi sb.append( "\"" ); sb.append( conversionContext.getNumberFormat() ); sb.append( "\"" ); + + if ( conversionContext.getLocale() != null ) { + sb.append( ", " ) + .append( decimalFormatSymbols( conversionContext ) ) + .append( ".getInstance( " ) + .append( locale( conversionContext ) ) + .append( ".forLanguageTag( \"" ) + .append( conversionContext.getLocale() ) + .append( " \" ) )" ); + } } sb.append( " )" ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java index c2aa73f307..96d3d6fe78 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java @@ -32,6 +32,8 @@ public interface ConversionContext { String getNumberFormat(); + String getLocale(); + TypeFactory getTypeFactory(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java index f5a9fcc761..159f1663e2 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java @@ -21,6 +21,7 @@ public class DefaultConversionContext implements ConversionContext { private final FormattingParameters formattingParameters; private final String dateFormat; private final String numberFormat; + private final String locale; private final TypeFactory typeFactory; public DefaultConversionContext(TypeFactory typeFactory, FormattingMessager messager, Type sourceType, @@ -32,6 +33,7 @@ public DefaultConversionContext(TypeFactory typeFactory, FormattingMessager mess this.formattingParameters = formattingParameters; this.dateFormat = this.formattingParameters.getDate(); this.numberFormat = this.formattingParameters.getNumber(); + this.locale = this.formattingParameters.getLocale(); validateDateFormat(); } @@ -64,6 +66,11 @@ public String getNumberFormat() { return numberFormat; } + @Override + public String getLocale() { + return locale != null ? locale.toString() : null; + } + @Override public String getDateFormat() { return dateFormat; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/FormattingParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/FormattingParameters.java index e21f5d74fa..48cee4bbe5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/FormattingParameters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/FormattingParameters.java @@ -15,21 +15,23 @@ */ public class FormattingParameters { - public static final FormattingParameters EMPTY = new FormattingParameters( null, null, null, null, null ); + public static final FormattingParameters EMPTY = new FormattingParameters( null, null, null, null, null, null ); private final String date; private final String number; private final AnnotationMirror mirror; private final AnnotationValue dateAnnotationValue; private final Element element; + private final String locale; public FormattingParameters(String date, String number, AnnotationMirror mirror, - AnnotationValue dateAnnotationValue, Element element) { + AnnotationValue dateAnnotationValue, Element element, String locale) { this.date = date; this.number = number; this.mirror = mirror; this.dateAnnotationValue = dateAnnotationValue; this.element = element; + this.locale = locale; } public String getDate() { @@ -51,4 +53,8 @@ public AnnotationValue getDateAnnotationValue() { public Element getElement() { return element; } + + public String getLocale() { + return locale; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java index c4770efde6..50fca3e4d2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/IterableMappingOptions.java @@ -55,7 +55,8 @@ public static IterableMappingOptions fromGem(IterableMappingGem iterableMapping, iterableMapping.numberFormat().get(), iterableMapping.mirror(), iterableMapping.dateFormat().getAnnotationValue(), - method + method, + iterableMapping.locale().getValue() ); IterableMappingOptions options = diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java index fd1758d8d2..9f3d12faf3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapMappingOptions.java @@ -8,14 +8,14 @@ import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.util.TypeUtils; -import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.gem.MapMappingGem; import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; +import org.mapstruct.ap.internal.model.common.FormattingParameters; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.tools.gem.GemValue; /** @@ -47,6 +47,8 @@ public static MapMappingOptions fromGem(MapMappingGem mapMapping, MapperOptions return options; } + String locale = mapMapping.locale().getValue(); + SelectionParameters keySelection = new SelectionParameters( mapMapping.keyQualifiedBy().get(), mapMapping.keyQualifiedByName().get(), @@ -66,7 +68,8 @@ public static MapMappingOptions fromGem(MapMappingGem mapMapping, MapperOptions mapMapping.keyNumberFormat().get(), mapMapping.mirror(), mapMapping.keyDateFormat().getAnnotationValue(), - method + method, + locale ); FormattingParameters valueFormatting = new FormattingParameters( @@ -74,7 +77,8 @@ public static MapMappingOptions fromGem(MapMappingGem mapMapping, MapperOptions mapMapping.valueNumberFormat().get(), mapMapping.mirror(), mapMapping.valueDateFormat().getAnnotationValue(), - method + method, + locale ); MapMappingOptions options = new MapMappingOptions( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 22f9ccdc2f..746aca5a32 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -18,16 +18,16 @@ import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; -import org.mapstruct.ap.internal.util.ElementUtils; -import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.ap.internal.gem.MappingGem; import org.mapstruct.ap.internal.gem.MappingsGem; import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.model.common.FormattingParameters; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.TypeUtils; import org.mapstruct.tools.gem.GemValue; /** @@ -118,6 +118,8 @@ public static void addInstance(MappingGem mapping, ExecutableElement method, String conditionExpression = getConditionExpression( mapping, method, messager ); String dateFormat = mapping.dateFormat().getValue(); String numberFormat = mapping.numberFormat().getValue(); + String locale = mapping.locale().getValue(); + String defaultValue = mapping.defaultValue().getValue(); Set dependsOn = mapping.dependsOn().hasValue() ? @@ -129,7 +131,8 @@ public static void addInstance(MappingGem mapping, ExecutableElement method, numberFormat, mapping.mirror(), mapping.dateFormat().getAnnotationValue(), - method + method, + locale ); SelectionParameters selectionParams = new SelectionParameters( mapping.qualifiedBy().get(), diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.ftl index ce0f605f11..6753a73d6f 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/conversion/CreateDecimalFormat.ftl @@ -5,9 +5,10 @@ Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 --> -private DecimalFormat ${name}( String numberFormat ) { +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.SupportingMappingMethod" --> +private DecimalFormat ${name}( <#list parameters as param><@includeModel object=param/><#if param_has_next>, ) { - DecimalFormat df = new DecimalFormat( numberFormat ); + DecimalFormat df = new DecimalFormat( numberFormat<#if parameters.size() > 1>, DecimalFormatSymbols.getInstance( locale ) ); df.setParseBigDecimal( true ); return df; -} \ No newline at end of file +} diff --git a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java index ce2f895e0f..401a6b2240 100755 --- a/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java +++ b/processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java @@ -70,7 +70,7 @@ public void testInvalidDateFormatValidation() { statefulMessagerMock, type, type, - new FormattingParameters( "qwertz", null, null, null, null ) + new FormattingParameters( "qwertz", null, null, null, null, null ) ); assertThat( statefulMessagerMock.getLastKindPrinted() ).isEqualTo( Diagnostic.Kind.ERROR ); } @@ -84,7 +84,7 @@ public void testNullDateFormatValidation() { statefulMessagerMock, type, type, - new FormattingParameters( null, null, null, null, null ) + new FormattingParameters( null, null, null, null, null, null ) ); assertThat( statefulMessagerMock.getLastKindPrinted() ).isNull(); } @@ -97,7 +97,7 @@ public void testUnsupportedType() { statefulMessagerMock, type, type, - new FormattingParameters( "qwertz", null, null, null, null ) + new FormattingParameters( "qwertz", null, null, null, null, null ) ); assertThat( statefulMessagerMock.getLastKindPrinted() ).isNull(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java index 9ccf8e7e1f..bc90228206 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java @@ -54,6 +54,21 @@ public void shouldApplyDateFormatForConversions() { assertThat( target.getAnotherDate() ).isEqualTo( "14.02.13 00:00" ); } + @ProcessorTest + @EnabledOnJre( JRE.JAVA_8 ) + // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ + public void shouldApplyDateFormatForConversionsWithCustomLocale() { + Source source = new Source(); + source.setDate( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); + source.setAnotherDate( new GregorianCalendar( 2013, Calendar.FEBRUARY, 14 ).getTime() ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTargetWithCustomLocale( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getDate() ).isEqualTo( "juillet 06, 2013" ); + assertThat( target.getAnotherDate() ).isEqualTo( "14.02.13, 00:00" ); + } + @ProcessorTest @EnabledForJreRange(min = JRE.JAVA_11) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ @@ -69,6 +84,21 @@ public void shouldApplyDateFormatForConversionsJdk11() { assertThat( target.getAnotherDate() ).isEqualTo( "14.02.13, 00:00" ); } + @ProcessorTest + @EnabledForJreRange(min = JRE.JAVA_11) + // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ + public void shouldApplyDateFormatForConversionsJdk11WithCustomLocale() { + Source source = new Source(); + source.setDate( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); + source.setAnotherDate( new GregorianCalendar( 2013, Calendar.FEBRUARY, 14 ).getTime() ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTargetWithCustomLocale( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getDate() ).isEqualTo( "juillet 06, 2013" ); + assertThat( target.getAnotherDate() ).isEqualTo( "14.02.13, 00:00" ); + } + @ProcessorTest @EnabledOnJre(JRE.JAVA_8) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ @@ -86,6 +116,23 @@ public void shouldApplyDateFormatForConversionInReverseMapping() { ); } + @ProcessorTest + @EnabledOnJre(JRE.JAVA_8) + // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ + public void shouldApplyDateFormatForConversionInReverseMappingWithCustomLocale() { + Target target = new Target(); + target.setDate( "juillet 06, 2013" ); + target.setAnotherDate( "14.02.13 8:30" ); + + Source source = SourceTargetMapper.INSTANCE.targetToSourceWithCustomLocale( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getDate() ).isEqualTo( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); + assertThat( source.getAnotherDate() ).isEqualTo( + new GregorianCalendar( 2013, Calendar.FEBRUARY, 14, 8, 30 ).getTime() + ); + } + @ProcessorTest @EnabledForJreRange(min = JRE.JAVA_11) // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ @@ -103,6 +150,23 @@ public void shouldApplyDateFormatForConversionInReverseMappingJdk11() { ); } + @ProcessorTest + @EnabledForJreRange(min = JRE.JAVA_11) + // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ + public void shouldApplyDateFormatForConversionInReverseMappingJdk11WithCustomLocale() { + Target target = new Target(); + target.setDate( "juillet 06, 2013" ); + target.setAnotherDate( "14.02.13, 8:30" ); + + Source source = SourceTargetMapper.INSTANCE.targetToSourceWithCustomLocale( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getDate() ).isEqualTo( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); + assertThat( source.getAnotherDate() ).isEqualTo( + new GregorianCalendar( 2013, Calendar.FEBRUARY, 14, 8, 30 ).getTime() + ); + } + @ProcessorTest public void shouldApplyStringConversionForIterableMethod() { List dates = Arrays.asList( @@ -117,6 +181,20 @@ public void shouldApplyStringConversionForIterableMethod() { assertThat( stringDates ).containsExactly( "06.07.2013", "14.02.2013", "11.04.2013" ); } + @ProcessorTest + public void shouldApplyStringConversionForIterableMethodWithCustomLocale() { + List dates = Arrays.asList( + new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime(), + new GregorianCalendar( 2013, Calendar.FEBRUARY, 14 ).getTime(), + new GregorianCalendar( 2013, Calendar.APRIL, 11 ).getTime() + ); + + List stringDates = SourceTargetMapper.INSTANCE.stringListToDateListWithCustomLocale( dates ); + + assertThat( stringDates ).isNotNull(); + assertThat( stringDates ).containsExactly( "juillet 06, 2013", "février 14, 2013", "avril 11, 2013" ); + } + @ProcessorTest public void shouldApplyStringConversionForArrayMethod() { List dates = Arrays.asList( @@ -131,6 +209,20 @@ public void shouldApplyStringConversionForArrayMethod() { assertThat( stringDates ).isEqualTo( new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" } ); } + @ProcessorTest + public void shouldApplyStringConversionForArrayMethodWithCustomLocale() { + List dates = Arrays.asList( + new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime(), + new GregorianCalendar( 2013, Calendar.FEBRUARY, 14 ).getTime(), + new GregorianCalendar( 2013, Calendar.APRIL, 11 ).getTime() + ); + + String[] stringDates = SourceTargetMapper.INSTANCE.stringListToDateArrayWithCustomLocale( dates ); + + assertThat( stringDates ).isNotNull(); + assertThat( stringDates ).isEqualTo( new String[]{ "juillet 06, 2013", "février 14, 2013", "avril 11, 2013" } ); + } + @ProcessorTest public void shouldApplyStringConversionForReverseIterableMethod() { List stringDates = Arrays.asList( "06.07.2013", "14.02.2013", "11.04.2013" ); @@ -145,6 +237,20 @@ public void shouldApplyStringConversionForReverseIterableMethod() { ); } + @ProcessorTest + public void shouldApplyStringConversionForReverseIterableMethodWithCustomLocale() { + List stringDates = Arrays.asList( "juillet 06, 2013", "février 14, 2013", "avril 11, 2013" ); + + List dates = SourceTargetMapper.INSTANCE.dateListToStringListWithCustomLocale( stringDates ); + + assertThat( dates ).isNotNull(); + assertThat( dates ).containsExactly( + new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime(), + new GregorianCalendar( 2013, Calendar.FEBRUARY, 14 ).getTime(), + new GregorianCalendar( 2013, Calendar.APRIL, 11 ).getTime() + ); + } + @ProcessorTest public void shouldApplyStringConversionForReverseArrayMethod() { String[] stringDates = new String[]{ "06.07.2013", "14.02.2013", "11.04.2013" }; @@ -159,6 +265,20 @@ public void shouldApplyStringConversionForReverseArrayMethod() { ); } + @ProcessorTest + public void shouldApplyStringConversionForReverseArrayMethodWithCustomLocale() { + String[] stringDates = new String[]{ "juillet 06, 2013", "février 14, 2013", "avril 11, 2013" }; + + List dates = SourceTargetMapper.INSTANCE.stringArrayToDateListWithCustomLocale( stringDates ); + + assertThat( dates ).isNotNull(); + assertThat( dates ).containsExactly( + new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime(), + new GregorianCalendar( 2013, Calendar.FEBRUARY, 14 ).getTime(), + new GregorianCalendar( 2013, Calendar.APRIL, 11 ).getTime() + ); + } + @ProcessorTest public void shouldApplyStringConversionForReverseArrayArrayMethod() { Date[] dates = new Date[]{ diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/SourceTargetMapper.java index 2ef6a7bd96..1971a8c066 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/SourceTargetMapper.java @@ -22,21 +22,39 @@ public interface SourceTargetMapper { @Mapping(target = "date", dateFormat = "dd.MM.yyyy") Target sourceToTarget(Source source); - @InheritInverseConfiguration + @Mapping(target = "date", dateFormat = "MMMM dd, yyyy", locale = "fr") + Target sourceToTargetWithCustomLocale(Source source); + + @InheritInverseConfiguration(name = "sourceToTarget") Source targetToSource(Target target); + @InheritInverseConfiguration(name = "sourceToTargetWithCustomLocale") + Source targetToSourceWithCustomLocale(Target target); + @IterableMapping(dateFormat = "dd.MM.yyyy") List stringListToDateList(List dates); + @IterableMapping(dateFormat = "MMMM dd, yyyy", locale = "fr") + List stringListToDateListWithCustomLocale(List dates); + @IterableMapping(dateFormat = "dd.MM.yyyy") String[] stringListToDateArray(List dates); - @InheritInverseConfiguration + @IterableMapping(dateFormat = "MMMM dd, yyyy", locale = "fr") + String[] stringListToDateArrayWithCustomLocale(List dates); + + @InheritInverseConfiguration(name = "stringListToDateList") List dateListToStringList(List strings); - @InheritInverseConfiguration + @InheritInverseConfiguration(name = "stringListToDateListWithCustomLocale") + List dateListToStringListWithCustomLocale(List strings); + + @InheritInverseConfiguration(name = "stringListToDateArray") List stringArrayToDateList(String[] dates); + @InheritInverseConfiguration(name = "stringListToDateArrayWithCustomLocale") + List stringArrayToDateListWithCustomLocale(String[] dates); + @IterableMapping(dateFormat = "dd.MM.yyyy") String[] dateArrayToStringArray(Date[] dates); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java index 6dfc1b3ca1..39b4e98319 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/NumberFormatConversionTest.java @@ -7,14 +7,15 @@ import java.math.BigDecimal; import java.math.BigInteger; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.junit.jupiter.api.extension.RegisterExtension; import org.junitpioneer.jupiter.DefaultLocale; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; @@ -27,6 +28,10 @@ @DefaultLocale("en") public class NumberFormatConversionTest { + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() + .addComparisonToFixtureFor( SourceTargetMapper.class ); + @ProcessorTest public void shouldApplyStringConversions() { Source source = new Source(); @@ -68,6 +73,47 @@ public void shouldApplyStringConversions() { assertThat( target.getBigInteger1() ).isEqualTo( "1.23456789E12" ); } + @ProcessorTest + public void shouldApplyStringConversionsWithCustomLocale() { + Source source = new Source(); + source.setI( 1 ); + source.setIi( 2 ); + source.setD( 3.0 ); + source.setDd( 4.0 ); + source.setF( 3.0f ); + source.setFf( 4.0f ); + source.setL( 5L ); + source.setLl( 6L ); + source.setB( (byte) 7 ); + source.setBb( (byte) 8 ); + + source.setComplex1( 345346.456756 ); + source.setComplex2( 5007034.3 ); + + source.setBigDecimal1( new BigDecimal( "987E-20" ) ); + source.setBigInteger1( new BigInteger( "1234567890000" ) ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTargetWithCustomLocale( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getI() ).isEqualTo( "1.00" ); + assertThat( target.getIi() ).isEqualTo( "2.00" ); + assertThat( target.getD() ).isEqualTo( "3.00" ); + assertThat( target.getDd() ).isEqualTo( "4.00" ); + assertThat( target.getF() ).isEqualTo( "3.00" ); + assertThat( target.getFf() ).isEqualTo( "4.00" ); + assertThat( target.getL() ).isEqualTo( "5.00" ); + assertThat( target.getLl() ).isEqualTo( "6.00" ); + assertThat( target.getB() ).isEqualTo( "7.00" ); + assertThat( target.getBb() ).isEqualTo( "8.00" ); + + assertThat( target.getComplex1() ).isEqualTo( "345.35E3" ); + assertThat( target.getComplex2() ).isEqualTo( "$5007034.30" ); + + assertThat( target.getBigDecimal1() ).isEqualTo( "9,87E-18" ); + assertThat( target.getBigInteger1() ).isEqualTo( "1,23456789E12" ); + } + @ProcessorTest public void shouldApplyReverseStringConversions() { Target target = new Target(); @@ -109,17 +155,73 @@ public void shouldApplyReverseStringConversions() { assertThat( source.getBigInteger1() ).isEqualTo( new BigInteger( "1234567890000" ) ); } + @ProcessorTest + public void shouldApplyReverseStringConversionsWithCustomLocale() { + Target target = new Target(); + target.setI( "1.00" ); + target.setIi( "2.00" ); + target.setD( "3.00" ); + target.setDd( "4.00" ); + target.setF( "3.00" ); + target.setFf( "4.00" ); + target.setL( "5.00" ); + target.setLl( "6.00" ); + target.setB( "7.00" ); + target.setBb( "8.00" ); + + target.setComplex1( "345.35E3" ); + target.setComplex2( "$5007034.30" ); + + target.setBigDecimal1( "9,87E-18" ); + target.setBigInteger1( "1,23456789E12" ); + + Source source = SourceTargetMapper.INSTANCE.targetToSourceWithCustomLocale( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getI() ).isEqualTo( 1 ); + assertThat( source.getIi() ).isEqualTo( Integer.valueOf( 2 ) ); + assertThat( source.getD() ).isEqualTo( 3.0 ); + assertThat( source.getDd() ).isEqualTo( Double.valueOf( 4.0 ) ); + assertThat( source.getF() ).isEqualTo( 3.0f ); + assertThat( source.getFf() ).isEqualTo( Float.valueOf( 4.0f ) ); + assertThat( source.getL() ).isEqualTo( 5L ); + assertThat( source.getLl() ).isEqualTo( Long.valueOf( 6L ) ); + assertThat( source.getB() ).isEqualTo( (byte) 7 ); + assertThat( source.getBb() ).isEqualTo( (byte) 8 ); + + assertThat( source.getComplex1() ).isEqualTo( 345350.0 ); + assertThat( source.getComplex2() ).isEqualTo( 5007034.3 ); + + assertThat( source.getBigDecimal1() ).isEqualTo( new BigDecimal( "987E-20" ) ); + assertThat( source.getBigInteger1() ).isEqualTo( new BigInteger( "1234567890000" ) ); + } + @ProcessorTest public void shouldApplyStringConversionsToIterables() { - List target = SourceTargetMapper.INSTANCE.sourceToTarget( Arrays.asList( 2f ) ); + List target = SourceTargetMapper.INSTANCE.sourceToTarget( List.of( 2f ) ); assertThat( target ).hasSize( 1 ); - assertThat( target ).isEqualTo( Arrays.asList( "2.00" ) ); + assertThat( target ).isEqualTo( List.of( "2.00" ) ); List source = SourceTargetMapper.INSTANCE.targetToSource( target ); assertThat( source ).hasSize( 1 ); - assertThat( source ).isEqualTo( Arrays.asList( 2.00f ) ); + assertThat( source ).isEqualTo( List.of( 2.00f ) ); + } + + @ProcessorTest + public void shouldApplyStringConversionsToIterablesWithCustomLocale() { + + List target = SourceTargetMapper.INSTANCE.sourceToTargetWithCustomLocale( + List.of( new BigDecimal("987E-20") ) + ); + + assertThat( target ).hasSize( 1 ); + assertThat( target ).isEqualTo( List.of( "9,87E-18" ) ); + + List source = SourceTargetMapper.INSTANCE.targetToSourceWithCustomLocale( target ); + assertThat( source ).hasSize( 1 ); + assertThat( source ).isEqualTo( List.of( new BigDecimal("987E-20") ) ); } @ProcessorTest @@ -137,4 +239,20 @@ public void shouldApplyStringConversionsToMaps() { assertThat( source2 ).contains( entry( 1.00f, 2f ) ); } + + @ProcessorTest + public void shouldApplyStringConversionsToMapsWithCustomLocale() { + + Map source1 = new HashMap<>(); + source1.put( new BigDecimal( "987E-20" ), new BigDecimal( "97E-10" ) ); + + Map target = SourceTargetMapper.INSTANCE.sourceToTargetWithCustomLocale( source1 ); + assertThat( target ).hasSize( 1 ); + assertThat( target ).contains( entry( "9,87E-18", "9,7E-9" ) ); + + Map source2 = SourceTargetMapper.INSTANCE.targetToSourceWithCustomLocale( target ); + assertThat( source2 ).hasSize( 1 ); + assertThat( source2 ).contains( entry( new BigDecimal( "987E-20" ), new BigDecimal( "97E-10" ) ) ); + + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapper.java index 537452fca2..b82a7a6199 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapper.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.conversion.numbers; +import java.math.BigDecimal; import java.util.List; import java.util.Map; import org.mapstruct.InheritInverseConfiguration; @@ -41,21 +42,55 @@ public interface SourceTargetMapper { } ) Target sourceToTarget(Source source); - @InheritInverseConfiguration + @Mappings( { + @Mapping( target = "i", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "ii", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "d", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "dd", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "f", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "ff", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "l", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "ll", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "b", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "bb", numberFormat = NUMBER_FORMAT, locale = "ru" ), + @Mapping( target = "complex1", numberFormat = "##0.##E0", locale = "ru" ), + @Mapping( target = "complex2", numberFormat = "$#.00", locale = "ru" ), + @Mapping( target = "bigDecimal1", numberFormat = "#0.#E0", locale = "ru" ), + @Mapping( target = "bigInteger1", numberFormat = "0.#############E0", locale = "ru" ) + + } ) + Target sourceToTargetWithCustomLocale(Source source); + + @InheritInverseConfiguration( name = "sourceToTarget" ) Source targetToSource(Target target); + @InheritInverseConfiguration( name = "sourceToTargetWithCustomLocale" ) + Source targetToSourceWithCustomLocale(Target target); + @IterableMapping( numberFormat = NUMBER_FORMAT ) List sourceToTarget(List source); - @InheritInverseConfiguration + @InheritInverseConfiguration( name = "sourceToTarget" ) List targetToSource(List source); + @IterableMapping( numberFormat = "#0.#E0", locale = "fr" ) + List sourceToTargetWithCustomLocale(List source); + + @InheritInverseConfiguration( name = "sourceToTargetWithCustomLocale" ) + List targetToSourceWithCustomLocale(List source); + @MapMapping( keyNumberFormat = NUMBER_FORMAT, valueNumberFormat = "##" ) Map sourceToTarget(Map source); - @InheritInverseConfiguration + @MapMapping( keyNumberFormat = "#0.#E0", valueNumberFormat = "0.#############E0", locale = "fr" ) + Map sourceToTargetWithCustomLocale(Map source); + + @InheritInverseConfiguration( name = "sourceToTarget" ) Map targetToSource(Map source); + @InheritInverseConfiguration( name = "sourceToTargetWithCustomLocale" ) + Map targetToSourceWithCustomLocale(Map source); + } diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java new file mode 100644 index 0000000000..ccf8042e6b --- /dev/null +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java @@ -0,0 +1,525 @@ +/* + * 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.conversion.numbers; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-09-14T11:37:30+0300", + comments = "version: , compiler: javac, environment: Java 21.0.2 (Amazon.com Inc.)" +) +public class SourceTargetMapperImpl implements SourceTargetMapper { + + @Override + public Target sourceToTarget(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + target.setI( new DecimalFormat( "##.00" ).format( source.getI() ) ); + if ( source.getIi() != null ) { + target.setIi( new DecimalFormat( "##.00" ).format( source.getIi() ) ); + } + target.setD( new DecimalFormat( "##.00" ).format( source.getD() ) ); + if ( source.getDd() != null ) { + target.setDd( new DecimalFormat( "##.00" ).format( source.getDd() ) ); + } + target.setF( new DecimalFormat( "##.00" ).format( source.getF() ) ); + if ( source.getFf() != null ) { + target.setFf( new DecimalFormat( "##.00" ).format( source.getFf() ) ); + } + target.setL( new DecimalFormat( "##.00" ).format( source.getL() ) ); + if ( source.getLl() != null ) { + target.setLl( new DecimalFormat( "##.00" ).format( source.getLl() ) ); + } + target.setB( new DecimalFormat( "##.00" ).format( source.getB() ) ); + if ( source.getBb() != null ) { + target.setBb( new DecimalFormat( "##.00" ).format( source.getBb() ) ); + } + target.setComplex1( new DecimalFormat( "##0.##E0" ).format( source.getComplex1() ) ); + target.setComplex2( new DecimalFormat( "$#.00" ).format( source.getComplex2() ) ); + if ( source.getBigDecimal1() != null ) { + target.setBigDecimal1( createDecimalFormat( "#0.#E0" ).format( source.getBigDecimal1() ) ); + } + if ( source.getBigInteger1() != null ) { + target.setBigInteger1( createDecimalFormat( "0.#############E0" ).format( source.getBigInteger1() ) ); + } + + return target; + } + + @Override + public Target sourceToTargetWithCustomLocale(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + target.setI( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getI() ) ); + if ( source.getIi() != null ) { + target.setIi( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getIi() ) ); + } + target.setD( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getD() ) ); + if ( source.getDd() != null ) { + target.setDd( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getDd() ) ); + } + target.setF( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getF() ) ); + if ( source.getFf() != null ) { + target.setFf( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getFf() ) ); + } + target.setL( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getL() ) ); + if ( source.getLl() != null ) { + target.setLl( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getLl() ) ); + } + target.setB( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getB() ) ); + if ( source.getBb() != null ) { + target.setBb( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getBb() ) ); + } + target.setComplex1( new DecimalFormat( "##0.##E0", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getComplex1() ) ); + target.setComplex2( new DecimalFormat( "$#.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getComplex2() ) ); + if ( source.getBigDecimal1() != null ) { + target.setBigDecimal1( createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "ru" ) ).format( source.getBigDecimal1() ) ); + } + if ( source.getBigInteger1() != null ) { + target.setBigInteger1( createDecimalFormatWithLocale( "0.#############E0", Locale.forLanguageTag( "ru" ) ).format( source.getBigInteger1() ) ); + } + + return target; + } + + @Override + public Source targetToSource(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + try { + if ( target.getI() != null ) { + source.setI( new DecimalFormat( "##.00" ).parse( target.getI() ).intValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getIi() != null ) { + source.setIi( new DecimalFormat( "##.00" ).parse( target.getIi() ).intValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getD() != null ) { + source.setD( new DecimalFormat( "##.00" ).parse( target.getD() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getDd() != null ) { + source.setDd( new DecimalFormat( "##.00" ).parse( target.getDd() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getF() != null ) { + source.setF( new DecimalFormat( "##.00" ).parse( target.getF() ).floatValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getFf() != null ) { + source.setFf( new DecimalFormat( "##.00" ).parse( target.getFf() ).floatValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getL() != null ) { + source.setL( new DecimalFormat( "##.00" ).parse( target.getL() ).longValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getLl() != null ) { + source.setLl( new DecimalFormat( "##.00" ).parse( target.getLl() ).longValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getB() != null ) { + source.setB( new DecimalFormat( "##.00" ).parse( target.getB() ).byteValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBb() != null ) { + source.setBb( new DecimalFormat( "##.00" ).parse( target.getBb() ).byteValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getComplex1() != null ) { + source.setComplex1( new DecimalFormat( "##0.##E0" ).parse( target.getComplex1() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getComplex2() != null ) { + source.setComplex2( new DecimalFormat( "$#.00" ).parse( target.getComplex2() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBigDecimal1() != null ) { + source.setBigDecimal1( (BigDecimal) createDecimalFormat( "#0.#E0" ).parse( target.getBigDecimal1() ) ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBigInteger1() != null ) { + source.setBigInteger1( ( (BigDecimal) createDecimalFormat( "0.#############E0" ).parse( target.getBigInteger1() ) ).toBigInteger() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + + return source; + } + + @Override + public Source targetToSourceWithCustomLocale(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + try { + if ( target.getI() != null ) { + source.setI( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getI() ).intValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getIi() != null ) { + source.setIi( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getIi() ).intValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getD() != null ) { + source.setD( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getD() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getDd() != null ) { + source.setDd( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getDd() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getF() != null ) { + source.setF( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getF() ).floatValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getFf() != null ) { + source.setFf( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getFf() ).floatValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getL() != null ) { + source.setL( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getL() ).longValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getLl() != null ) { + source.setLl( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getLl() ).longValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getB() != null ) { + source.setB( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getB() ).byteValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBb() != null ) { + source.setBb( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getBb() ).byteValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getComplex1() != null ) { + source.setComplex1( new DecimalFormat( "##0.##E0", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getComplex1() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getComplex2() != null ) { + source.setComplex2( new DecimalFormat( "$#.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getComplex2() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBigDecimal1() != null ) { + source.setBigDecimal1( (BigDecimal) createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "ru" ) ).parse( target.getBigDecimal1() ) ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBigInteger1() != null ) { + source.setBigInteger1( ( (BigDecimal) createDecimalFormatWithLocale( "0.#############E0", Locale.forLanguageTag( "ru" ) ).parse( target.getBigInteger1() ) ).toBigInteger() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + + return source; + } + + @Override + public List sourceToTarget(List source) { + if ( source == null ) { + return null; + } + + List list = new ArrayList( source.size() ); + for ( Float float1 : source ) { + list.add( new DecimalFormat( "##.00" ).format( float1 ) ); + } + + return list; + } + + @Override + public List targetToSource(List source) { + if ( source == null ) { + return null; + } + + List list = new ArrayList( source.size() ); + for ( String string : source ) { + try { + list.add( new DecimalFormat( "##.00" ).parse( string ).floatValue() ); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + } + + return list; + } + + @Override + public List sourceToTargetWithCustomLocale(List source) { + if ( source == null ) { + return null; + } + + List list = new ArrayList( source.size() ); + for ( BigDecimal bigDecimal : source ) { + list.add( createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "fr" ) ).format( bigDecimal ) ); + } + + return list; + } + + @Override + public List targetToSourceWithCustomLocale(List source) { + if ( source == null ) { + return null; + } + + List list = new ArrayList( source.size() ); + for ( String string : source ) { + try { + list.add( (BigDecimal) createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "fr" ) ).parse( string ) ); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + } + + return list; + } + + @Override + public Map sourceToTarget(Map source) { + if ( source == null ) { + return null; + } + + Map map = LinkedHashMap.newLinkedHashMap( source.size() ); + + for ( java.util.Map.Entry entry : source.entrySet() ) { + String key = new DecimalFormat( "##.00" ).format( entry.getKey() ); + String value = new DecimalFormat( "##" ).format( entry.getValue() ); + map.put( key, value ); + } + + return map; + } + + @Override + public Map sourceToTargetWithCustomLocale(Map source) { + if ( source == null ) { + return null; + } + + Map map = LinkedHashMap.newLinkedHashMap( source.size() ); + + for ( java.util.Map.Entry entry : source.entrySet() ) { + String key = createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "fr" ) ).format( entry.getKey() ); + String value = createDecimalFormatWithLocale( "0.#############E0", Locale.forLanguageTag( "fr" ) ).format( entry.getValue() ); + map.put( key, value ); + } + + return map; + } + + @Override + public Map targetToSource(Map source) { + if ( source == null ) { + return null; + } + + Map map = LinkedHashMap.newLinkedHashMap( source.size() ); + + for ( java.util.Map.Entry entry : source.entrySet() ) { + Float key; + try { + key = new DecimalFormat( "##.00" ).parse( entry.getKey() ).floatValue(); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + Float value; + try { + value = new DecimalFormat( "##" ).parse( entry.getValue() ).floatValue(); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + map.put( key, value ); + } + + return map; + } + + @Override + public Map targetToSourceWithCustomLocale(Map source) { + if ( source == null ) { + return null; + } + + Map map = LinkedHashMap.newLinkedHashMap( source.size() ); + + for ( java.util.Map.Entry entry : source.entrySet() ) { + BigDecimal key; + try { + key = (BigDecimal) createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "fr" ) ).parse( entry.getKey() ); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + BigDecimal value; + try { + value = (BigDecimal) createDecimalFormatWithLocale( "0.#############E0", Locale.forLanguageTag( "fr" ) ).parse( entry.getValue() ); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + map.put( key, value ); + } + + return map; + } + + private DecimalFormat createDecimalFormatWithLocale( String numberFormat, Locale locale ) { + + DecimalFormat df = new DecimalFormat( numberFormat, DecimalFormatSymbols.getInstance( locale ) ); + df.setParseBigDecimal( true ); + return df; + } + + private DecimalFormat createDecimalFormat( String numberFormat ) { + + DecimalFormat df = new DecimalFormat( numberFormat ); + df.setParseBigDecimal( true ); + return df; + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java new file mode 100644 index 0000000000..0b536b8327 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java @@ -0,0 +1,525 @@ +/* + * 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.conversion.numbers; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2024-09-14T11:36:20+0300", + comments = "version: , compiler: javac, environment: Java 17.0.10 (Private Build)" +) +public class SourceTargetMapperImpl implements SourceTargetMapper { + + @Override + public Target sourceToTarget(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + target.setI( new DecimalFormat( "##.00" ).format( source.getI() ) ); + if ( source.getIi() != null ) { + target.setIi( new DecimalFormat( "##.00" ).format( source.getIi() ) ); + } + target.setD( new DecimalFormat( "##.00" ).format( source.getD() ) ); + if ( source.getDd() != null ) { + target.setDd( new DecimalFormat( "##.00" ).format( source.getDd() ) ); + } + target.setF( new DecimalFormat( "##.00" ).format( source.getF() ) ); + if ( source.getFf() != null ) { + target.setFf( new DecimalFormat( "##.00" ).format( source.getFf() ) ); + } + target.setL( new DecimalFormat( "##.00" ).format( source.getL() ) ); + if ( source.getLl() != null ) { + target.setLl( new DecimalFormat( "##.00" ).format( source.getLl() ) ); + } + target.setB( new DecimalFormat( "##.00" ).format( source.getB() ) ); + if ( source.getBb() != null ) { + target.setBb( new DecimalFormat( "##.00" ).format( source.getBb() ) ); + } + target.setComplex1( new DecimalFormat( "##0.##E0" ).format( source.getComplex1() ) ); + target.setComplex2( new DecimalFormat( "$#.00" ).format( source.getComplex2() ) ); + if ( source.getBigDecimal1() != null ) { + target.setBigDecimal1( createDecimalFormat( "#0.#E0" ).format( source.getBigDecimal1() ) ); + } + if ( source.getBigInteger1() != null ) { + target.setBigInteger1( createDecimalFormat( "0.#############E0" ).format( source.getBigInteger1() ) ); + } + + return target; + } + + @Override + public Target sourceToTargetWithCustomLocale(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + target.setI( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getI() ) ); + if ( source.getIi() != null ) { + target.setIi( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getIi() ) ); + } + target.setD( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getD() ) ); + if ( source.getDd() != null ) { + target.setDd( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getDd() ) ); + } + target.setF( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getF() ) ); + if ( source.getFf() != null ) { + target.setFf( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getFf() ) ); + } + target.setL( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getL() ) ); + if ( source.getLl() != null ) { + target.setLl( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getLl() ) ); + } + target.setB( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getB() ) ); + if ( source.getBb() != null ) { + target.setBb( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getBb() ) ); + } + target.setComplex1( new DecimalFormat( "##0.##E0", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getComplex1() ) ); + target.setComplex2( new DecimalFormat( "$#.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).format( source.getComplex2() ) ); + if ( source.getBigDecimal1() != null ) { + target.setBigDecimal1( createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "ru" ) ).format( source.getBigDecimal1() ) ); + } + if ( source.getBigInteger1() != null ) { + target.setBigInteger1( createDecimalFormatWithLocale( "0.#############E0", Locale.forLanguageTag( "ru" ) ).format( source.getBigInteger1() ) ); + } + + return target; + } + + @Override + public Source targetToSource(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + try { + if ( target.getI() != null ) { + source.setI( new DecimalFormat( "##.00" ).parse( target.getI() ).intValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getIi() != null ) { + source.setIi( new DecimalFormat( "##.00" ).parse( target.getIi() ).intValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getD() != null ) { + source.setD( new DecimalFormat( "##.00" ).parse( target.getD() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getDd() != null ) { + source.setDd( new DecimalFormat( "##.00" ).parse( target.getDd() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getF() != null ) { + source.setF( new DecimalFormat( "##.00" ).parse( target.getF() ).floatValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getFf() != null ) { + source.setFf( new DecimalFormat( "##.00" ).parse( target.getFf() ).floatValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getL() != null ) { + source.setL( new DecimalFormat( "##.00" ).parse( target.getL() ).longValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getLl() != null ) { + source.setLl( new DecimalFormat( "##.00" ).parse( target.getLl() ).longValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getB() != null ) { + source.setB( new DecimalFormat( "##.00" ).parse( target.getB() ).byteValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBb() != null ) { + source.setBb( new DecimalFormat( "##.00" ).parse( target.getBb() ).byteValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getComplex1() != null ) { + source.setComplex1( new DecimalFormat( "##0.##E0" ).parse( target.getComplex1() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getComplex2() != null ) { + source.setComplex2( new DecimalFormat( "$#.00" ).parse( target.getComplex2() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBigDecimal1() != null ) { + source.setBigDecimal1( (BigDecimal) createDecimalFormat( "#0.#E0" ).parse( target.getBigDecimal1() ) ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBigInteger1() != null ) { + source.setBigInteger1( ( (BigDecimal) createDecimalFormat( "0.#############E0" ).parse( target.getBigInteger1() ) ).toBigInteger() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + + return source; + } + + @Override + public Source targetToSourceWithCustomLocale(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + try { + if ( target.getI() != null ) { + source.setI( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getI() ).intValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getIi() != null ) { + source.setIi( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getIi() ).intValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getD() != null ) { + source.setD( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getD() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getDd() != null ) { + source.setDd( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getDd() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getF() != null ) { + source.setF( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getF() ).floatValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getFf() != null ) { + source.setFf( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getFf() ).floatValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getL() != null ) { + source.setL( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getL() ).longValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getLl() != null ) { + source.setLl( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getLl() ).longValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getB() != null ) { + source.setB( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getB() ).byteValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBb() != null ) { + source.setBb( new DecimalFormat( "##.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getBb() ).byteValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getComplex1() != null ) { + source.setComplex1( new DecimalFormat( "##0.##E0", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getComplex1() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getComplex2() != null ) { + source.setComplex2( new DecimalFormat( "$#.00", DecimalFormatSymbols.getInstance( Locale.forLanguageTag( "ru " ) ) ).parse( target.getComplex2() ).doubleValue() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBigDecimal1() != null ) { + source.setBigDecimal1( (BigDecimal) createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "ru" ) ).parse( target.getBigDecimal1() ) ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + try { + if ( target.getBigInteger1() != null ) { + source.setBigInteger1( ( (BigDecimal) createDecimalFormatWithLocale( "0.#############E0", Locale.forLanguageTag( "ru" ) ).parse( target.getBigInteger1() ) ).toBigInteger() ); + } + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + + return source; + } + + @Override + public List sourceToTarget(List source) { + if ( source == null ) { + return null; + } + + List list = new ArrayList( source.size() ); + for ( Float float1 : source ) { + list.add( new DecimalFormat( "##.00" ).format( float1 ) ); + } + + return list; + } + + @Override + public List targetToSource(List source) { + if ( source == null ) { + return null; + } + + List list = new ArrayList( source.size() ); + for ( String string : source ) { + try { + list.add( new DecimalFormat( "##.00" ).parse( string ).floatValue() ); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + } + + return list; + } + + @Override + public List sourceToTargetWithCustomLocale(List source) { + if ( source == null ) { + return null; + } + + List list = new ArrayList( source.size() ); + for ( BigDecimal bigDecimal : source ) { + list.add( createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "fr" ) ).format( bigDecimal ) ); + } + + return list; + } + + @Override + public List targetToSourceWithCustomLocale(List source) { + if ( source == null ) { + return null; + } + + List list = new ArrayList( source.size() ); + for ( String string : source ) { + try { + list.add( (BigDecimal) createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "fr" ) ).parse( string ) ); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + } + + return list; + } + + @Override + public Map sourceToTarget(Map source) { + if ( source == null ) { + return null; + } + + Map map = new LinkedHashMap( Math.max( (int) ( source.size() / .75f ) + 1, 16 ) ); + + for ( java.util.Map.Entry entry : source.entrySet() ) { + String key = new DecimalFormat( "##.00" ).format( entry.getKey() ); + String value = new DecimalFormat( "##" ).format( entry.getValue() ); + map.put( key, value ); + } + + return map; + } + + @Override + public Map sourceToTargetWithCustomLocale(Map source) { + if ( source == null ) { + return null; + } + + Map map = new LinkedHashMap( Math.max( (int) ( source.size() / .75f ) + 1, 16 ) ); + + for ( java.util.Map.Entry entry : source.entrySet() ) { + String key = createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "fr" ) ).format( entry.getKey() ); + String value = createDecimalFormatWithLocale( "0.#############E0", Locale.forLanguageTag( "fr" ) ).format( entry.getValue() ); + map.put( key, value ); + } + + return map; + } + + @Override + public Map targetToSource(Map source) { + if ( source == null ) { + return null; + } + + Map map = new LinkedHashMap( Math.max( (int) ( source.size() / .75f ) + 1, 16 ) ); + + for ( java.util.Map.Entry entry : source.entrySet() ) { + Float key; + try { + key = new DecimalFormat( "##.00" ).parse( entry.getKey() ).floatValue(); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + Float value; + try { + value = new DecimalFormat( "##" ).parse( entry.getValue() ).floatValue(); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + map.put( key, value ); + } + + return map; + } + + @Override + public Map targetToSourceWithCustomLocale(Map source) { + if ( source == null ) { + return null; + } + + Map map = new LinkedHashMap( Math.max( (int) ( source.size() / .75f ) + 1, 16 ) ); + + for ( java.util.Map.Entry entry : source.entrySet() ) { + BigDecimal key; + try { + key = (BigDecimal) createDecimalFormatWithLocale( "#0.#E0", Locale.forLanguageTag( "fr" ) ).parse( entry.getKey() ); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + BigDecimal value; + try { + value = (BigDecimal) createDecimalFormatWithLocale( "0.#############E0", Locale.forLanguageTag( "fr" ) ).parse( entry.getValue() ); + } + catch ( ParseException e ) { + throw new RuntimeException( e ); + } + map.put( key, value ); + } + + return map; + } + + private DecimalFormat createDecimalFormatWithLocale( String numberFormat, Locale locale ) { + + DecimalFormat df = new DecimalFormat( numberFormat, DecimalFormatSymbols.getInstance( locale ) ); + df.setParseBigDecimal( true ); + return df; + } + + private DecimalFormat createDecimalFormat( String numberFormat ) { + + DecimalFormat df = new DecimalFormat( numberFormat ); + df.setParseBigDecimal( true ); + return df; + } +} From f3d2b2e65b5709b7575076a220b938ece7931d21 Mon Sep 17 00:00:00 2001 From: dudxor4587 Date: Fri, 22 Nov 2024 07:14:16 +0900 Subject: [PATCH 469/597] Delete unnecessary conditions and modify return statement (#3772) --- .../mapstruct/ap/internal/conversion/Conversions.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 6acb69492a..5090c903f3 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -268,10 +268,10 @@ private void registerNativeTypeConversion(Class sourceType, Class targetTy if ( sourceType.isPrimitive() && targetType.isPrimitive() ) { register( sourceType, targetType, new PrimitiveToPrimitiveConversion( sourceType ) ); } - else if ( sourceType.isPrimitive() && !targetType.isPrimitive() ) { + else if ( sourceType.isPrimitive() ) { register( sourceType, targetType, new PrimitiveToWrapperConversion( sourceType, targetType ) ); } - else if ( !sourceType.isPrimitive() && targetType.isPrimitive() ) { + else if ( targetType.isPrimitive() ) { register( sourceType, targetType, inverse( new PrimitiveToWrapperConversion( targetType, sourceType ) ) ); } else { @@ -390,11 +390,7 @@ public boolean equals(Object obj) { return false; } - if ( !Objects.equals( targetType, other.targetType ) ) { - return false; - } - - return true; + return Objects.equals( targetType, other.targetType ); } } } From 084cf3abc148255404b0e2b79c878e68c8d606d1 Mon Sep 17 00:00:00 2001 From: Tran Ngoc Nhan Date: Fri, 29 Nov 2024 15:33:41 +0700 Subject: [PATCH 470/597] Fix javadoc typos (#3780) --- .../java/org/mapstruct/NullValuePropertyMappingStrategy.java | 2 +- .../ap/internal/model/NestedTargetPropertyMappingHolder.java | 2 +- .../java/org/mapstruct/ap/internal/model/source/Method.java | 2 +- .../main/java/org/mapstruct/ap/internal/util/NativeTypes.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java index 9e06e723b6..556d4253b1 100644 --- a/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java +++ b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java @@ -10,7 +10,7 @@ * {@link NullValuePropertyMappingStrategy} can be defined on {@link MapperConfig}, {@link Mapper}, {@link BeanMapping} * and {@link Mapping}. * Precedence is arranged in the reverse order. So {@link Mapping} will override {@link BeanMapping}, will - * overide {@link Mapper} + * override {@link Mapper} * * The enum only applies to update methods: methods that update a pre-existing target (annotated with * {@code @}{@link MappingTarget}). diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index ea05b84cae..2b407e240c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -64,7 +64,7 @@ public List getProcessedSourceParameters() { } /** - * @return all the targets that were hanled + * @return all the targets that were handled */ public Set getHandledTargets() { return handledTargets; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index ad2882080a..bea4d56101 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -83,7 +83,7 @@ public interface Method { Parameter getMappingTargetParameter(); /** - * Returns whether the meethod is designated as bean factory for + * Returns whether the method is designated as bean factory for * mapping target {@link org.mapstruct.ObjectFactory } * * @return true if it is a target bean factory. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java index 729cb180ef..0a4ca0cde0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java @@ -174,7 +174,7 @@ void removeAndValidateFloatingPointLiteralSuffix() { boolean endsWithDSuffix = PTRN_DOUBLE.matcher( val ).find(); // error handling if ( isFloat && endsWithDSuffix ) { - throw new NumberFormatException( "Assiging double to a float" ); + throw new NumberFormatException( "Assigning double to a float" ); } // remove suffix if ( endsWithLSuffix || endsWithFSuffix || endsWithDSuffix ) { From f98a742f9805dad05653d8fcbae05c085725e4da Mon Sep 17 00:00:00 2001 From: jinhyogyeom <148544516+jinhyogyeom@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:22:51 +0900 Subject: [PATCH 471/597] Standardize Class Names to PascalCase in tests (#3773) --- .../referenced/AbstractSourceTargetMapperPrivate.java | 2 +- .../referenced/AbstractSourceTargetMapperProtected.java | 2 +- .../accessibility/referenced/ReferencedAccessibilityTest.java | 4 ++-- ...perPrivateBase.java => SourceTargetMapperPrivateBase.java} | 2 +- ...rotectedBase.java => SourceTargetMapperProtectedBase.java} | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/{SourceTargetmapperPrivateBase.java => SourceTargetMapperPrivateBase.java} (91%) rename processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/{SourceTargetmapperProtectedBase.java => SourceTargetMapperProtectedBase.java} (90%) diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperPrivate.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperPrivate.java index 507e63a957..2a16c3980f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperPrivate.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperPrivate.java @@ -14,7 +14,7 @@ * @author Sjaak Derksen */ @Mapper -public abstract class AbstractSourceTargetMapperPrivate extends SourceTargetmapperPrivateBase { +public abstract class AbstractSourceTargetMapperPrivate extends SourceTargetMapperPrivateBase { public static final AbstractSourceTargetMapperPrivate INSTANCE = Mappers.getMapper( AbstractSourceTargetMapperPrivate.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperProtected.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperProtected.java index ef5df10285..ccdba61514 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperProtected.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/AbstractSourceTargetMapperProtected.java @@ -14,7 +14,7 @@ * @author Sjaak Derksen */ @Mapper -public abstract class AbstractSourceTargetMapperProtected extends SourceTargetmapperProtectedBase { +public abstract class AbstractSourceTargetMapperProtected extends SourceTargetMapperProtectedBase { public static final AbstractSourceTargetMapperProtected INSTANCE = Mappers.getMapper( AbstractSourceTargetMapperProtected.class ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java index 3cd62a8b4f..dc3d8ea10f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/ReferencedAccessibilityTest.java @@ -72,12 +72,12 @@ public void shouldNotBeAbleToAccessDefaultMethodInReferencedInOtherPackage() { @ProcessorTest @IssueKey( "206" ) - @WithClasses( { AbstractSourceTargetMapperProtected.class, SourceTargetmapperProtectedBase.class } ) + @WithClasses( { AbstractSourceTargetMapperProtected.class, SourceTargetMapperProtectedBase.class } ) public void shouldBeAbleToAccessProtectedMethodInBase() { } @ProcessorTest @IssueKey("206") - @WithClasses({ AbstractSourceTargetMapperPrivate.class, SourceTargetmapperPrivateBase.class }) + @WithClasses({ AbstractSourceTargetMapperPrivate.class, SourceTargetMapperPrivateBase.class }) @ExpectedCompilationOutcome( value = CompilationResult.SUCCEEDED, diagnostics = { diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetmapperPrivateBase.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperPrivateBase.java similarity index 91% rename from processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetmapperPrivateBase.java rename to processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperPrivateBase.java index 94f9b2668f..06dbb0cec7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetmapperPrivateBase.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperPrivateBase.java @@ -8,7 +8,7 @@ /** * @author Sjaak Derksen */ -public class SourceTargetmapperPrivateBase { +public class SourceTargetMapperPrivateBase { @SuppressWarnings("unused") private ReferencedTarget sourceToTarget(ReferencedSource source) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetmapperProtectedBase.java b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperProtectedBase.java similarity index 90% rename from processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetmapperProtectedBase.java rename to processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperProtectedBase.java index 263d74bee6..fe5a50fa37 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetmapperProtectedBase.java +++ b/processor/src/test/java/org/mapstruct/ap/test/accessibility/referenced/SourceTargetMapperProtectedBase.java @@ -9,7 +9,7 @@ * * @author Sjaak Derksen */ -public class SourceTargetmapperProtectedBase { +public class SourceTargetMapperProtectedBase { protected ReferencedTarget sourceToTarget( ReferencedSource source ) { ReferencedTarget target = new ReferencedTarget(); From 4812d2b030b199f6761fc8da031236d55bf3538b Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Sat, 7 Dec 2024 14:57:13 +0100 Subject: [PATCH 472/597] Align README with v1.6.3 release (#3784) --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index 6932f4d748..adb48b2fd4 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # MapStruct - Java bean mappings, the easy way! -[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.6.2-blue.svg)](https://central.sonatype.com/search?q=g:org.mapstruct%20v:1.6.2) +[![Latest Stable Version](https://img.shields.io/badge/Latest%20Stable%20Version-1.6.3-blue.svg)](https://central.sonatype.com/search?q=g:org.mapstruct%20v:1.6.3) [![Latest Version](https://img.shields.io/maven-central/v/org.mapstruct/mapstruct-processor.svg?maxAge=3600&label=Latest%20Release)](https://central.sonatype.com/search?q=g:org.mapstruct) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://github.com/mapstruct/mapstruct/blob/main/LICENSE.txt) @@ -68,7 +68,7 @@ For Maven-based projects, add the following to your POM file in order to use Map ```xml ... - 1.6.2 + 1.6.3 ... @@ -114,10 +114,10 @@ plugins { dependencies { ... - implementation 'org.mapstruct:mapstruct:1.6.2' + implementation 'org.mapstruct:mapstruct:1.6.3' - annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.2' - testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.6.2' // if you are using mapstruct in test code + annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3' // if you are using mapstruct in test code } ... ``` From 8f962919117c30c95165e5349314c0adea455d8f Mon Sep 17 00:00:00 2001 From: Tran Ngoc Nhan Date: Sat, 18 Jan 2025 19:04:42 +0700 Subject: [PATCH 473/597] Fix documentation typo and code polish (#3787) --- .../src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc | 2 +- .../src/main/asciidoc/chapter-6-mapping-collections.asciidoc | 4 ++-- .../java/org/mapstruct/ap/spi/util/IntrospectorUtils.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index ed64580eb4..ea574603ea 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -302,7 +302,7 @@ If you don't want explicitly name all properties from nested source bean, you ca The generated code will map every property from `CustomerDto.record` to `Customer` directly, without need to manually name any of them. The same goes for `Customer.account`. -When there are conflicts, these can be resolved by explicitely defining the mapping. For instance in the example above. `name` occurs in `CustomerDto.record` and in `CustomerDto.account`. The mapping `@Mapping( target = "name", source = "record.name" )` resolves this conflict. +When there are conflicts, these can be resolved by explicitly defining the mapping. For instance in the example above. `name` occurs in `CustomerDto.record` and in `CustomerDto.account`. The mapping `@Mapping( target = "name", source = "record.name" )` resolves this conflict. This "target this" notation can be very useful when mapping hierarchical objects to flat objects and vice versa (`@InheritInverseConfiguration`). diff --git a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc index 17025da4de..79d4544f9b 100644 --- a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc +++ b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc @@ -192,7 +192,7 @@ The option `DEFAULT` should not be used explicitly. It is used to distinguish be [TIP] ==== -When working with an `adder` method and JPA entities, Mapstruct assumes that the target collections are initialized with a collection implementation (e.g. an `ArrayList`). You can use factories to create a new target entity with intialized collections instead of Mapstruct creating the target entity by its constructor. +When working with an `adder` method and JPA entities, Mapstruct assumes that the target collections are initialized with a collection implementation (e.g. an `ArrayList`). You can use factories to create a new target entity with initialized collections instead of Mapstruct creating the target entity by its constructor. ==== [[implementation-types-for-collection-mappings]] @@ -224,4 +224,4 @@ When an iterable or map mapping method declares an interface type as return type |`ConcurrentMap`|`ConcurrentHashMap` |`ConcurrentNavigableMap`|`ConcurrentSkipListMap` -|=== +|=== \ No newline at end of file diff --git a/processor/src/main/java/org/mapstruct/ap/spi/util/IntrospectorUtils.java b/processor/src/main/java/org/mapstruct/ap/spi/util/IntrospectorUtils.java index fccc22b38b..95dcd7a92d 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/util/IntrospectorUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/util/IntrospectorUtils.java @@ -32,7 +32,7 @@ private IntrospectorUtils() { * @return The decapitalized version of the string. */ public static String decapitalize(String name) { - if ( name == null || name.length() == 0 ) { + if ( name == null || name.isEmpty() ) { return name; } if ( name.length() > 1 && Character.isUpperCase( name.charAt( 1 ) ) && From e0a7d3d0e62695591eef532f63874729c56f64b7 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 17 Jan 2025 18:01:47 +0100 Subject: [PATCH 474/597] Use latest Maven Wrapper --- .mvn/wrapper/MavenWrapperDownloader.java | 117 ------ .mvn/wrapper/maven-wrapper.jar | Bin 50710 -> 0 bytes .mvn/wrapper/maven-wrapper.properties | 21 +- mvnw | 451 ++++++++++------------- mvnw.cmd | 281 +++++++------- 5 files changed, 343 insertions(+), 527 deletions(-) delete mode 100644 .mvn/wrapper/MavenWrapperDownloader.java delete mode 100644 .mvn/wrapper/maven-wrapper.jar diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index b901097f2d..0000000000 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50710 zcmbTd1CVCTmM+|7+wQV$+qP}n>auOywyU~q+qUhh+uxis_~*a##hm*_WW?9E7Pb7N%LRFiwbEGCJ0XP=%-6oeT$XZcYgtzC2~q zk(K08IQL8oTl}>>+hE5YRgXTB@fZ4TH9>7=79e`%%tw*SQUa9~$xKD5rS!;ZG@ocK zQdcH}JX?W|0_Afv?y`-NgLum62B&WSD$-w;O6G0Sm;SMX65z)l%m1e-g8Q$QTI;(Q z+x$xth4KFvH@Bs6(zn!iF#nenk^Y^ce;XIItAoCsow38eq?Y-Auh!1in#Rt-_D>H^ z=EjbclGGGa6VnaMGmMLj`x3NcwA43Jb(0gzl;RUIRAUDcR1~99l2SAPkVhoRMMtN} zXvC<tOmX83grD8GSo_Lo?%lNfhD#EBgPo z*nf@ppMC#B!T)Ae0RG$mlJWmGl7CkuU~B8-==5i;rS;8i6rJ=PoQxf446XDX9g|c> zU64ePyMlsI^V5Jq5A+BPe#e73+kpc_r1tv#B)~EZ;7^67F0*QiYfrk0uVW;Qb=NsG zN>gsuCwvb?s-KQIppEaeXtEMdc9dy6Dfduz-tMTms+i01{eD9JE&h?Kht*$eOl#&L zJdM_-vXs(V#$Ed;5wyNWJdPNh+Z$+;$|%qR(t`4W@kDhd*{(7-33BOS6L$UPDeE_53j${QfKN-0v-HG z(QfyvFNbwPK%^!eIo4ac1;b>c0vyf9}Xby@YY!lkz-UvNp zwj#Gg|4B~?n?G^{;(W;|{SNoJbHTMpQJ*Wq5b{l9c8(%?Kd^1?H1om1de0Da9M;Q=n zUfn{f87iVb^>Exl*nZ0hs(Yt>&V9$Pg`zX`AI%`+0SWQ4Zc(8lUDcTluS z5a_KerZWe}a-MF9#Cd^fi!y3%@RFmg&~YnYZ6<=L`UJ0v={zr)>$A;x#MCHZy1st7 ztT+N07NR+vOwSV2pvWuN1%lO!K#Pj0Fr>Q~R40{bwdL%u9i`DSM4RdtEH#cW)6}+I-eE< z&tZs+(Ogu(H_;$a$!7w`MH0r%h&@KM+<>gJL@O~2K2?VrSYUBbhCn#yy?P)uF3qWU z0o09mIik+kvzV6w>vEZy@&Mr)SgxPzUiDA&%07m17udz9usD82afQEps3$pe!7fUf z0eiidkJ)m3qhOjVHC_M(RYCBO%CZKZXFb8}s0-+}@CIn&EF(rRWUX2g^yZCvl0bI} zbP;1S)iXnRC&}5-Tl(hASKqdSnO?ASGJ*MIhOXIblmEudj(M|W!+I3eDc}7t`^mtg z)PKlaXe(OH+q-)qcQ8a@!llRrpGI8DsjhoKvw9T;TEH&?s=LH0w$EzI>%u;oD@x83 zJL7+ncjI9nn!TlS_KYu5vn%f*@qa5F;| zEFxY&B?g=IVlaF3XNm_03PA)=3|{n-UCgJoTr;|;1AU9|kPE_if8!Zvb}0q$5okF$ zHaJdmO&gg!9oN|M{!qGE=tb|3pVQ8PbL$}e;NgXz<6ZEggI}wO@aBP**2Wo=yN#ZC z4G$m^yaM9g=|&!^ft8jOLuzc3Psca*;7`;gnHm}tS0%f4{|VGEwu45KptfNmwxlE~ z^=r30gi@?cOm8kAz!EylA4G~7kbEiRlRIzwrb~{_2(x^$-?|#e6Bi_**(vyr_~9Of z!n>Gqf+Qwiu!xhi9f53=PM3`3tNF}pCOiPU|H4;pzjcsqbwg*{{kyrTxk<;mx~(;; z1NMrpaQ`57yn34>Jo3b|HROE(UNcQash!0p2-!Cz;{IRv#Vp5!3o$P8!%SgV~k&Hnqhp`5eLjTcy93cK!3Hm-$`@yGnaE=?;*2uSpiZTs_dDd51U%i z{|Zd9ou-;laGS_x=O}a+ zB||za<795A?_~Q=r=coQ+ZK@@ zId~hWQL<%)fI_WDIX#=(WNl!Dm$a&ROfLTd&B$vatq!M-2Jcs;N2vps$b6P1(N}=oI3<3luMTmC|0*{ zm1w8bt7vgX($!0@V0A}XIK)w!AzUn7vH=pZEp0RU0p?}ch2XC-7r#LK&vyc2=-#Q2 z^L%8)JbbcZ%g0Du;|8=q8B>X=mIQirpE=&Ox{TiuNDnOPd-FLI^KfEF729!!0x#Es z@>3ursjFSpu%C-8WL^Zw!7a0O-#cnf`HjI+AjVCFitK}GXO`ME&on|^=~Zc}^LBp9 zj=-vlN;Uc;IDjtK38l7}5xxQF&sRtfn4^TNtnzXv4M{r&ek*(eNbIu!u$>Ed%` z5x7+&)2P&4>0J`N&ZP8$vcR+@FS0126s6+Jx_{{`3ZrIMwaJo6jdrRwE$>IU_JTZ} z(||hyyQ)4Z1@wSlT94(-QKqkAatMmkT7pCycEB1U8KQbFX&?%|4$yyxCtm3=W`$4fiG0WU3yI@c zx{wfmkZAYE_5M%4{J-ygbpH|(|GD$2f$3o_Vti#&zfSGZMQ5_f3xt6~+{RX=$H8at z?GFG1Tmp}}lmm-R->ve*Iv+XJ@58p|1_jRvfEgz$XozU8#iJS})UM6VNI!3RUU!{5 zXB(+Eqd-E;cHQ>)`h0(HO_zLmzR3Tu-UGp;08YntWwMY-9i^w_u#wR?JxR2bky5j9 z3Sl-dQQU$xrO0xa&>vsiK`QN<$Yd%YXXM7*WOhnRdSFt5$aJux8QceC?lA0_if|s> ze{ad*opH_kb%M&~(~&UcX0nFGq^MqjxW?HJIP462v9XG>j(5Gat_)#SiNfahq2Mz2 zU`4uV8m$S~o9(W>mu*=h%Gs(Wz+%>h;R9Sg)jZ$q8vT1HxX3iQnh6&2rJ1u|j>^Qf`A76K%_ubL`Zu?h4`b=IyL>1!=*%!_K)=XC z6d}4R5L+sI50Q4P3upXQ3Z!~1ZXLlh!^UNcK6#QpYt-YC=^H=EPg3)z*wXo*024Q4b2sBCG4I# zlTFFY=kQ>xvR+LsuDUAk)q%5pEcqr(O_|^spjhtpb1#aC& zghXzGkGDC_XDa%t(X`E+kvKQ4zrQ*uuQoj>7@@ykWvF332)RO?%AA&Fsn&MNzmFa$ zWk&&^=NNjxLjrli_8ESU)}U|N{%j&TQmvY~lk!~Jh}*=^INA~&QB9em!in_X%Rl1&Kd~Z(u z9mra#<@vZQlOY+JYUwCrgoea4C8^(xv4ceCXcejq84TQ#sF~IU2V}LKc~Xlr_P=ry zl&Hh0exdCbVd^NPCqNNlxM3vA13EI8XvZ1H9#bT7y*U8Y{H8nwGpOR!e!!}*g;mJ#}T{ekSb}5zIPmye*If(}}_=PcuAW#yidAa^9-`<8Gr0 z)Fz=NiZ{)HAvw{Pl5uu)?)&i&Us$Cx4gE}cIJ}B4Xz~-q7)R_%owbP!z_V2=Aq%Rj z{V;7#kV1dNT9-6R+H}}(ED*_!F=~uz>&nR3gb^Ce%+0s#u|vWl<~JD3MvS0T9thdF zioIG3c#Sdsv;LdtRv3ml7%o$6LTVL>(H`^@TNg`2KPIk*8-IB}X!MT0`hN9Ddf7yN z?J=GxPL!uJ7lqwowsl?iRrh@#5C$%E&h~Z>XQcvFC*5%0RN-Opq|=IwX(dq(*sjs+ zqy99+v~m|6T#zR*e1AVxZ8djd5>eIeCi(b8sUk)OGjAsKSOg^-ugwl2WSL@d#?mdl zib0v*{u-?cq}dDGyZ%$XRY=UkQwt2oGu`zQneZh$=^! zj;!pCBWQNtvAcwcWIBM2y9!*W|8LmQy$H~5BEx)78J`4Z0(FJO2P^!YyQU{*Al+fs z){!4JvT1iLrJ8aU3k0t|P}{RN)_^v%$$r;+p0DY7N8CXzmS*HB*=?qaaF9D@#_$SN zSz{moAK<*RH->%r7xX~9gVW$l7?b|_SYI)gcjf0VAUJ%FcQP(TpBs; zg$25D!Ry_`8xpS_OJdeo$qh#7U+cepZ??TII7_%AXsT$B z=e)Bx#v%J0j``00Zk5hsvv6%T^*xGNx%KN-=pocSoqE5_R)OK%-Pbu^1MNzfds)mL zxz^F4lDKV9D&lEY;I+A)ui{TznB*CE$=9(wgE{m}`^<--OzV-5V4X2w9j(_!+jpTr zJvD*y6;39&T+==$F&tsRKM_lqa1HC}aGL0o`%c9mO=fts?36@8MGm7Vi{Y z^<7m$(EtdSr#22<(rm_(l_(`j!*Pu~Y>>xc>I9M#DJYDJNHO&4=HM%YLIp?;iR&$m z#_$ZWYLfGLt5FJZhr3jpYb`*%9S!zCG6ivNHYzNHcI%khtgHBliM^Ou}ZVD7ehU9 zS+W@AV=?Ro!=%AJ>Kcy9aU3%VX3|XM_K0A+ZaknKDyIS3S-Hw1C7&BSW5)sqj5Ye_ z4OSW7Yu-;bCyYKHFUk}<*<(@TH?YZPHr~~Iy%9@GR2Yd}J2!N9K&CN7Eq{Ka!jdu; zQNB*Y;i(7)OxZK%IHGt#Rt?z`I|A{q_BmoF!f^G}XVeTbe1Wnzh%1g>j}>DqFf;Rp zz7>xIs12@Ke0gr+4-!pmFP84vCIaTjqFNg{V`5}Rdt~xE^I;Bxp4)|cs8=f)1YwHz zqI`G~s2~qqDV+h02b`PQpUE#^^Aq8l%y2|ByQeXSADg5*qMprEAE3WFg0Q39`O+i1 z!J@iV!`Y~C$wJ!5Z+j5$i<1`+@)tBG$JL=!*uk=2k;T<@{|s1$YL079FvK%mPhyHV zP8^KGZnp`(hVMZ;s=n~3r2y;LTwcJwoBW-(ndU-$03{RD zh+Qn$ja_Z^OuMf3Ub|JTY74s&Am*(n{J3~@#OJNYuEVVJd9*H%)oFoRBkySGm`hx! zT3tG|+aAkXcx-2Apy)h^BkOyFTWQVeZ%e2@;*0DtlG9I3Et=PKaPt&K zw?WI7S;P)TWED7aSH$3hL@Qde?H#tzo^<(o_sv_2ci<7M?F$|oCFWc?7@KBj-;N$P zB;q!8@bW-WJY9do&y|6~mEruZAVe$!?{)N9rZZxD-|oltkhW9~nR8bLBGXw<632!l z*TYQn^NnUy%Ds}$f^=yQ+BM-a5X4^GHF=%PDrRfm_uqC zh{sKwIu|O0&jWb27;wzg4w5uA@TO_j(1X?8E>5Zfma|Ly7Bklq|s z9)H`zoAGY3n-+&JPrT!>u^qg9Evx4y@GI4$n-Uk_5wttU1_t?6><>}cZ-U+&+~JE) zPlDbO_j;MoxdLzMd~Ew|1o^a5q_1R*JZ=#XXMzg?6Zy!^hop}qoLQlJ{(%!KYt`MK z8umEN@Z4w!2=q_oe=;QttPCQy3Nm4F@x>@v4sz_jo{4m*0r%J(w1cSo;D_hQtJs7W z><$QrmG^+<$4{d2bgGo&3-FV}avg9zI|Rr(k{wTyl3!M1q+a zD9W{pCd%il*j&Ft z5H$nENf>>k$;SONGW`qo6`&qKs*T z2^RS)pXk9b@(_Fw1bkb)-oqK|v}r$L!W&aXA>IpcdNZ_vWE#XO8X`#Yp1+?RshVcd zknG%rPd*4ECEI0wD#@d+3NbHKxl}n^Sgkx==Iu%}HvNliOqVBqG?P2va zQ;kRJ$J6j;+wP9cS za#m;#GUT!qAV%+rdWolk+)6kkz4@Yh5LXP+LSvo9_T+MmiaP-eq6_k;)i6_@WSJ zlT@wK$zqHu<83U2V*yJ|XJU4farT#pAA&@qu)(PO^8PxEmPD4;Txpio+2)#!9 z>&=i7*#tc0`?!==vk>s7V+PL#S1;PwSY?NIXN2=Gu89x(cToFm))7L;< z+bhAbVD*bD=}iU`+PU+SBobTQ%S!=VL!>q$rfWsaaV}Smz>lO9JXT#`CcH_mRCSf4%YQAw`$^yY z3Y*^Nzk_g$xn7a_NO(2Eb*I=^;4f!Ra#Oo~LLjlcjke*k*o$~U#0ZXOQ5@HQ&T46l z7504MUgZkz2gNP1QFN8Y?nSEnEai^Rgyvl}xZfMUV6QrJcXp;jKGqB=D*tj{8(_pV zqyB*DK$2lgYGejmJUW)*s_Cv65sFf&pb(Yz8oWgDtQ0~k^0-wdF|tj}MOXaN@ydF8 zNr={U?=;&Z?wr^VC+`)S2xl}QFagy;$mG=TUs7Vi2wws5zEke4hTa2)>O0U?$WYsZ z<8bN2bB_N4AWd%+kncgknZ&}bM~eDtj#C5uRkp21hWW5gxWvc6b*4+dn<{c?w9Rmf zIVZKsPl{W2vQAlYO3yh}-{Os=YBnL8?uN5(RqfQ=-1cOiUnJu>KcLA*tQK3FU`_bM zM^T28w;nAj5EdAXFi&Kk1Nnl2)D!M{@+D-}bIEe+Lc4{s;YJc-{F#``iS2uk;2!Zp zF9#myUmO!wCeJIoi^A+T^e~20c+c2C}XltaR!|U-HfDA=^xF97ev}$l6#oY z&-&T{egB)&aV$3_aVA51XGiU07$s9vubh_kQG?F$FycvS6|IO!6q zq^>9|3U^*!X_C~SxX&pqUkUjz%!j=VlXDo$!2VLH!rKj@61mDpSr~7B2yy{>X~_nc zRI+7g2V&k zd**H++P9dg!-AOs3;GM`(g<+GRV$+&DdMVpUxY9I1@uK28$az=6oaa+PutlO9?6#? zf-OsgT>^@8KK>ggkUQRPPgC7zjKFR5spqQb3ojCHzj^(UH~v+!y*`Smv)VpVoPwa6 zWG18WJaPKMi*F6Zdk*kU^`i~NNTfn3BkJniC`yN98L-Awd)Z&mY? zprBW$!qL-OL7h@O#kvYnLsfff@kDIegt~?{-*5A7JrA;#TmTe?jICJqhub-G@e??D zqiV#g{)M!kW1-4SDel7TO{;@*h2=_76g3NUD@|c*WO#>MfYq6_YVUP+&8e4|%4T`w zXzhmVNziAHazWO2qXcaOu@R1MrPP{t)`N)}-1&~mq=ZH=w=;-E$IOk=y$dOls{6sRR`I5>|X zpq~XYW4sd;J^6OwOf**J>a7u$S>WTFPRkjY;BfVgQst)u4aMLR1|6%)CB^18XCz+r ztkYQ}G43j~Q&1em(_EkMv0|WEiKu;z2zhb(L%$F&xWwzOmk;VLBYAZ8lOCziNoPw1 zv2BOyXA`A8z^WH!nXhKXM`t0;6D*-uGds3TYGrm8SPnJJOQ^fJU#}@aIy@MYWz**H zvkp?7I5PE{$$|~{-ZaFxr6ZolP^nL##mHOErB^AqJqn^hFA=)HWj!m3WDaHW$C)i^ z9@6G$SzB=>jbe>4kqr#sF7#K}W*Cg-5y6kun3u&0L7BpXF9=#7IN8FOjWrWwUBZiU zT_se3ih-GBKx+Uw0N|CwP3D@-C=5(9T#BH@M`F2!Goiqx+Js5xC92|Sy0%WWWp={$(am!#l~f^W_oz78HX<0X#7 zp)p1u~M*o9W@O8P{0Qkg@Wa# z2{Heb&oX^CQSZWSFBXKOfE|tsAm#^U-WkDnU;IowZ`Ok4!mwHwH=s|AqZ^YD4!5!@ zPxJj+Bd-q6w_YG`z_+r;S86zwXb+EO&qogOq8h-Ect5(M2+>(O7n7)^dP*ws_3U6v zVsh)sk^@*c>)3EML|0<-YROho{lz@Nd4;R9gL{9|64xVL`n!m$-Jjrx?-Bacp!=^5 z1^T^eB{_)Y<9)y{-4Rz@9_>;_7h;5D+@QcbF4Wv7hu)s0&==&6u)33 zHRj+&Woq-vDvjwJCYES@$C4{$?f$Ibi4G()UeN11rgjF+^;YE^5nYprYoJNoudNj= zm1pXSeG64dcWHObUetodRn1Fw|1nI$D9z}dVEYT0lQnsf_E1x2vBLql7NrHH!n&Sq z6lc*mvU=WS6=v9Lrl}&zRiu_6u;6g%_DU{9b+R z#YHqX7`m9eydf?KlKu6Sb%j$%_jmydig`B*TN`cZL-g!R)iE?+Q5oOqBFKhx z%MW>BC^(F_JuG(ayE(MT{S3eI{cKiwOtPwLc0XO*{*|(JOx;uQOfq@lp_^cZo=FZj z4#}@e@dJ>Bn%2`2_WPeSN7si^{U#H=7N4o%Dq3NdGybrZgEU$oSm$hC)uNDC_M9xc zGzwh5Sg?mpBIE8lT2XsqTt3j3?We8}3bzLBTQd639vyg^$0#1epq8snlDJP2(BF)K zSx30RM+{f+b$g{9usIL8H!hCO117Xgv}ttPJm9wVRjPk;ePH@zxv%j9k5`TzdXLeT zFgFX`V7cYIcBls5WN0Pf6SMBN+;CrQ(|EsFd*xtwr#$R{Z9FP`OWtyNsq#mCgZ7+P z^Yn$haBJ)r96{ZJd8vlMl?IBxrgh=fdq_NF!1{jARCVz>jNdC)H^wfy?R94#MPdUjcYX>#wEx+LB#P-#4S-%YH>t-j+w zOFTI8gX$ard6fAh&g=u&56%3^-6E2tpk*wx3HSCQ+t7+*iOs zPk5ysqE}i*cQocFvA68xHfL|iX(C4h*67@3|5Qwle(8wT&!&{8*{f%0(5gH+m>$tq zp;AqrP7?XTEooYG1Dzfxc>W%*CyL16q|fQ0_jp%%Bk^k!i#Nbi(N9&T>#M{gez_Ws zYK=l}adalV(nH}I_!hNeb;tQFk3BHX7N}}R8%pek^E`X}%ou=cx8InPU1EE0|Hen- zyw8MoJqB5=)Z%JXlrdTXAE)eqLAdVE-=>wGHrkRet}>3Yu^lt$Kzu%$3#(ioY}@Gu zjk3BZuQH&~7H+C*uX^4}F*|P89JX;Hg2U!pt>rDi(n(Qe-c}tzb0#6_ItoR0->LSt zR~UT<-|@TO%O`M+_e_J4wx7^)5_%%u+J=yF_S#2Xd?C;Ss3N7KY^#-vx+|;bJX&8r zD?|MetfhdC;^2WG`7MCgs>TKKN=^=!x&Q~BzmQio_^l~LboTNT=I zC5pme^P@ER``p$2md9>4!K#vV-Fc1an7pl>_|&>aqP}+zqR?+~Z;f2^`a+-!Te%V? z;H2SbF>jP^GE(R1@%C==XQ@J=G9lKX+Z<@5}PO(EYkJh=GCv#)Nj{DkWJM2}F&oAZ6xu8&g7pn1ps2U5srwQ7CAK zN&*~@t{`31lUf`O;2w^)M3B@o)_mbRu{-`PrfNpF!R^q>yTR&ETS7^-b2*{-tZAZz zw@q5x9B5V8Qd7dZ!Ai$9hk%Q!wqbE1F1c96&zwBBaRW}(^axoPpN^4Aw}&a5dMe+*Gomky_l^54*rzXro$ z>LL)U5Ry>~FJi=*{JDc)_**c)-&faPz`6v`YU3HQa}pLtb5K)u%K+BOqXP0)rj5Au$zB zW1?vr?mDv7Fsxtsr+S6ucp2l#(4dnr9sD*v+@*>g#M4b|U?~s93>Pg{{a5|rm2xfI z`>E}?9S@|IoUX{Q1zjm5YJT|3S>&09D}|2~BiMo=z4YEjXlWh)V&qs;*C{`UMxp$9 zX)QB?G$fPD6z5_pNs>Jeh{^&U^)Wbr?2D6-q?)`*1k@!UvwQgl8eG$r+)NnFoT)L6 zg7lEh+E6J17krfYJCSjWzm67hEth24pomhz71|Qodn#oAILN)*Vwu2qpJirG)4Wnv}9GWOFrQg%Je+gNrPl8mw7ykE8{ z=|B4+uwC&bpp%eFcRU6{mxRV32VeH8XxX>v$du<$(DfinaaWxP<+Y97Z#n#U~V zVEu-GoPD=9$}P;xv+S~Ob#mmi$JQmE;Iz4(){y*9pFyW-jjgdk#oG$fl4o9E8bo|L zWjo4l%n51@Kz-n%zeSCD`uB?T%FVk+KBI}=ve zvlcS#wt`U6wrJo}6I6Rwb=1GzZfwE=I&Ne@p7*pH84XShXYJRgvK)UjQL%R9Zbm(m zxzTQsLTON$WO7vM)*vl%Pc0JH7WhP;$z@j=y#avW4X8iqy6mEYr@-}PW?H)xfP6fQ z&tI$F{NNct4rRMSHhaelo<5kTYq+(?pY)Ieh8*sa83EQfMrFupMM@nfEV@EmdHUv9 z35uzIrIuo4#WnF^_jcpC@uNNaYTQ~uZWOE6P@LFT^1@$o&q+9Qr8YR+ObBkpP9=F+$s5+B!mX2~T zAuQ6RenX?O{IlLMl1%)OK{S7oL}X%;!XUxU~xJN8xk z`xywS*naF(J#?vOpB(K=o~lE;m$zhgPWDB@=p#dQIW>xe_p1OLoWInJRKbEuoncf; zmS1!u-ycc1qWnDg5Nk2D)BY%jmOwCLC+Ny>`f&UxFowIsHnOXfR^S;&F(KXd{ODlm z$6#1ccqt-HIH9)|@fHnrKudu!6B$_R{fbCIkSIb#aUN|3RM>zuO>dpMbROZ`^hvS@ z$FU-;e4W}!ubzKrU@R*dW*($tFZ>}dd*4_mv)#O>X{U@zSzQt*83l9mI zI$8O<5AIDx`wo0}f2fsPC_l>ONx_`E7kdXu{YIZbp1$(^oBAH({T~&oQ&1{X951QW zmhHUxd)t%GQ9#ak5fTjk-cahWC;>^Rg7(`TVlvy0W@Y!Jc%QL3Ozu# zDPIqBCy&T2PWBj+d-JA-pxZlM=9ja2ce|3B(^VCF+a*MMp`(rH>Rt6W1$;r{n1(VK zLs>UtkT43LR2G$AOYHVailiqk7naz2yZGLo*xQs!T9VN5Q>eE(w zw$4&)&6xIV$IO^>1N-jrEUg>O8G4^@y+-hQv6@OmF@gy^nL_n1P1-Rtyy$Bl;|VcV zF=p*&41-qI5gG9UhKmmnjs932!6hceXa#-qfK;3d*a{)BrwNFeKU|ge?N!;zk+kB! zMD_uHJR#%b54c2tr~uGPLTRLg$`fupo}cRJeTwK;~}A>(Acy4k-Xk&Aa1&eWYS1ULWUj@fhBiWY$pdfy+F z@G{OG{*v*mYtH3OdUjwEr6%_ZPZ3P{@rfbNPQG!BZ7lRyC^xlMpWH`@YRar`tr}d> z#wz87t?#2FsH-jM6m{U=gp6WPrZ%*w0bFm(T#7m#v^;f%Z!kCeB5oiF`W33W5Srdt zdU?YeOdPG@98H7NpI{(uN{FJdu14r(URPH^F6tOpXuhU7T9a{3G3_#Ldfx_nT(Hec zo<1dyhsVsTw;ZkVcJ_0-h-T3G1W@q)_Q30LNv)W?FbMH+XJ* zy=$@39Op|kZv`Rt>X`zg&at(?PO^I=X8d9&myFEx#S`dYTg1W+iE?vt#b47QwoHI9 zNP+|3WjtXo{u}VG(lLUaW0&@yD|O?4TS4dfJI`HC-^q;M(b3r2;7|FONXphw-%7~* z&;2!X17|05+kZOpQ3~3!Nb>O94b&ZSs%p)TK)n3m=4eiblVtSx@KNFgBY_xV6ts;NF;GcGxMP8OKV^h6LmSb2E#Qnw ze!6Mnz7>lE9u{AgQ~8u2zM8CYD5US8dMDX-5iMlgpE9m*s+Lh~A#P1er*rF}GHV3h z=`STo?kIXw8I<`W0^*@mB1$}pj60R{aJ7>C2m=oghKyxMbFNq#EVLgP0cH3q7H z%0?L93-z6|+jiN|@v>ix?tRBU(v-4RV`}cQH*fp|)vd3)8i9hJ3hkuh^8dz{F5-~_ zUUr1T3cP%cCaTooM8dj|4*M=e6flH0&8ve32Q)0dyisl))XkZ7Wg~N}6y`+Qi2l+e zUd#F!nJp{#KIjbQdI`%oZ`?h=5G^kZ_uN`<(`3;a!~EMsWV|j-o>c?x#;zR2ktiB! z);5rrHl?GPtr6-o!tYd|uK;Vbsp4P{v_4??=^a>>U4_aUXPWQ$FPLE4PK$T^3Gkf$ zHo&9$U&G`d(Os6xt1r?sg14n)G8HNyWa^q8#nf0lbr4A-Fi;q6t-`pAx1T*$eKM*$ z|CX|gDrk#&1}>5H+`EjV$9Bm)Njw&7-ZR{1!CJTaXuP!$Pcg69`{w5BRHysB$(tWUes@@6aM69kb|Lx$%BRY^-o6bjH#0!7b;5~{6J+jKxU!Kmi# zndh@+?}WKSRY2gZ?Q`{(Uj|kb1%VWmRryOH0T)f3cKtG4oIF=F7RaRnH0Rc_&372={_3lRNsr95%ZO{IX{p@YJ^EI%+gvvKes5cY+PE@unghjdY5#9A!G z70u6}?zmd?v+{`vCu-53_v5@z)X{oPC@P)iA3jK$`r zSA2a7&!^zmUiZ82R2=1cumBQwOJUPz5Ay`RLfY(EiwKkrx%@YN^^XuET;tE zmr-6~I7j!R!KrHu5CWGSChO6deaLWa*9LLJbcAJsFd%Dy>a!>J`N)Z&oiU4OEP-!Ti^_!p}O?7`}i7Lsf$-gBkuY*`Zb z7=!nTT;5z$_5$=J=Ko+Cp|Q0J=%oFr>hBgnL3!tvFoLNhf#D0O=X^h+x08iB;@8pXdRHxX}6R4k@i6%vmsQwu^5z zk1ip`#^N)^#Lg#HOW3sPI33xqFB4#bOPVnY%d6prwxf;Y-w9{ky4{O6&94Ra8VN@K zb-lY;&`HtxW@sF!doT5T$2&lIvJpbKGMuDAFM#!QPXW87>}=Q4J3JeXlwHys?!1^#37q_k?N@+u&Ns20pEoBeZC*np;i;M{2C0Z4_br2gsh6eL z#8`#sn41+$iD?^GL%5?cbRcaa-Nx0vE(D=*WY%rXy3B%gNz0l?#noGJGP728RMY#q z=2&aJf@DcR?QbMmN)ItUe+VM_U!ryqA@1VVt$^*xYt~-qvW!J4Tp<-3>jT=7Zow5M z8mSKp0v4b%a8bxFr>3MwZHSWD73D@+$5?nZAqGM#>H@`)mIeC#->B)P8T$zh-Pxnc z8)~Zx?TWF4(YfKuF3WN_ckpCe5;x4V4AA3(i$pm|78{%!q?|~*eH0f=?j6i)n~Hso zmTo>vqEtB)`%hP55INf7HM@taH)v`Fw40Ayc*R!T?O{ziUpYmP)AH`euTK!zg9*6Z z!>M=$3pd0!&TzU=hc_@@^Yd3eUQpX4-33}b{?~5t5lgW=ldJ@dUAH%`l5US1y_`40 zs(X`Qk}vvMDYYq+@Rm+~IyCX;iD~pMgq^KY)T*aBz@DYEB={PxA>)mI6tM*sx-DmGQHEaHwRrAmNjO!ZLHO4b;;5mf@zzlPhkP($JeZGE7 z?^XN}Gf_feGoG~BjUgVa*)O`>lX=$BSR2)uD<9 z>o^|nb1^oVDhQbfW>>!;8-7<}nL6L^V*4pB=>wwW+RXAeRvKED(n1;R`A6v$6gy0I(;Vf?!4;&sgn7F%LpM}6PQ?0%2Z@b{It<(G1CZ|>913E0nR2r^Pa*Bp z@tFGi*CQ~@Yc-?{cwu1 zsilf=k^+Qs>&WZG(3WDixisHpR>`+ihiRwkL(3T|=xsoNP*@XX3BU8hr57l3k;pni zI``=3Nl4xh4oDj<%>Q1zYXHr%Xg_xrK3Nq?vKX3|^Hb(Bj+lONTz>4yhU-UdXt2>j z<>S4NB&!iE+ao{0Tx^N*^|EZU;0kJkx@zh}S^P{ieQjGl468CbC`SWnwLRYYiStXm zOxt~Rb3D{dz=nHMcY)#r^kF8|q8KZHVb9FCX2m^X*(|L9FZg!5a7((!J8%MjT$#Fs)M1Pb zq6hBGp%O1A+&%2>l0mpaIzbo&jc^!oN^3zxap3V2dNj3x<=TwZ&0eKX5PIso9j1;e zwUg+C&}FJ`k(M|%%}p=6RPUq4sT3-Y;k-<68ciZ~_j|bt>&9ZLHNVrp#+pk}XvM{8 z`?k}o-!if>hVlCP9j%&WI2V`5SW)BCeR5>MQhF)po=p~AYN%cNa_BbV6EEh_kk^@a zD>4&>uCGCUmyA-c)%DIcF4R6!>?6T~Mj_m{Hpq`*(wj>foHL;;%;?(((YOxGt)Bhx zuS+K{{CUsaC++%}S6~CJ=|vr(iIs-je)e9uJEU8ZJAz)w166q)R^2XI?@E2vUQ!R% zn@dxS!JcOimXkWJBz8Y?2JKQr>`~SmE2F2SL38$SyR1^yqj8_mkBp)o$@+3BQ~Mid z9U$XVqxX3P=XCKj0*W>}L0~Em`(vG<>srF8+*kPrw z20{z(=^w+ybdGe~Oo_i|hYJ@kZl*(9sHw#Chi&OIc?w`nBODp?ia$uF%Hs(X>xm?j zqZQ`Ybf@g#wli`!-al~3GWiE$K+LCe=Ndi!#CVjzUZ z!sD2O*;d28zkl))m)YN7HDi^z5IuNo3^w(zy8 zszJG#mp#Cj)Q@E@r-=NP2FVxxEAeOI2e=|KshybNB6HgE^(r>HD{*}S}mO>LuRGJT{*tfTzw_#+er-0${}%YPe@CMJ1Ng#j#)i)SnY@ss3gL;g zg2D~#Kpdfu#G;q1qz_TwSz1VJT(b3zby$Vk&;Y#1(A)|xj`_?i5YQ;TR%jice5E;0 zYHg;`zS5{S*9xI6o^j>rE8Ua*XhIw{_-*&@(R|C(am8__>+Ws&Q^ymy*X4~hR2b5r zm^p3sw}yv=tdyncy_Ui7{BQS732et~Z_@{-IhHDXAV`(Wlay<#hb>%H%WDi+K$862nA@BDtM#UCKMu+kM`!JHyWSi?&)A7_ z3{cyNG%a~nnH_!+;g&JxEMAmh-Z}rC!o7>OVzW&PoMyTA_g{hqXG)SLraA^OP**<7 zjWbr7z!o2n3hnx7A=2O=WL;`@9N{vQIM@&|G-ljrPvIuJHYtss0Er0fT5cMXNUf1B z7FAwBDixt0X7C3S)mPe5g`YtME23wAnbU)+AtV}z+e8G;0BP=bI;?(#|Ep!vVfDbK zvx+|CKF>yt0hWQ3drchU#XBU+HiuG*V^snFAPUp-5<#R&BUAzoB!aZ+e*KIxa26V}s6?nBK(U-7REa573wg-jqCg>H8~>O{ z*C0JL-?X-k_y%hpUFL?I>0WV{oV`Nb)nZbJG01R~AG>flIJf)3O*oB2i8~;!P?Wo_ z0|QEB*fifiL6E6%>tlAYHm2cjTFE@*<);#>689Z6S#BySQ@VTMhf9vYQyLeDg1*F} zjq>i1*x>5|CGKN{l9br3kB0EHY|k4{%^t7-uhjd#NVipUZa=EUuE5kS1_~qYX?>hJ z$}!jc9$O$>J&wnu0SgfYods^z?J4X;X7c77Me0kS-dO_VUQ39T(Kv(Y#s}Qqz-0AH z^?WRL(4RzpkD+T5FG_0NyPq-a-B7A5LHOCqwObRJi&oRi(<;OuIN7SV5PeHU$<@Zh zPozEV`dYmu0Z&Tqd>t>8JVde9#Pt+l95iHe$4Xwfy1AhI zDM4XJ;bBTTvRFtW>E+GzkN)9k!hA5z;xUOL2 zq4}zn-DP{qc^i|Y%rvi|^5k-*8;JZ~9a;>-+q_EOX+p1Wz;>i7c}M6Nv`^NY&{J-> z`(mzDJDM}QPu5i44**2Qbo(XzZ-ZDu%6vm8w@DUarqXj41VqP~ zs&4Y8F^Waik3y1fQo`bVUH;b=!^QrWb)3Gl=QVKr+6sxc=ygauUG|cm?|X=;Q)kQ8 zM(xrICifa2p``I7>g2R~?a{hmw@{!NS5`VhH8+;cV(F>B94M*S;5#O`YzZH1Z%yD? zZ61w(M`#aS-*~Fj;x|J!KM|^o;MI#Xkh0ULJcA?o4u~f%Z^16ViA27FxU5GM*rKq( z7cS~MrZ=f>_OWx8j#-Q3%!aEU2hVuTu(7`TQk-Bi6*!<}0WQi;_FpO;fhpL4`DcWp zGOw9vx0N~6#}lz(r+dxIGZM3ah-8qrqMmeRh%{z@dbUD2w15*_4P?I~UZr^anP}DB zU9CCrNiy9I3~d#&!$DX9e?A});BjBtQ7oGAyoI$8YQrkLBIH@2;lt4E^)|d6Jwj}z z&2_E}Y;H#6I4<10d_&P0{4|EUacwFHauvrjAnAm6yeR#}f}Rk27CN)vhgRqEyPMMS7zvunj2?`f;%?alsJ+-K+IzjJx>h8 zu~m_y$!J5RWAh|C<6+uiCNsOKu)E72M3xKK(a9Okw3e_*O&}7llNV!=P87VM2DkAk zci!YXS2&=P0}Hx|wwSc9JP%m8dMJA*q&VFB0yMI@5vWoAGraygwn){R+Cj6B1a2Px z5)u(K5{+;z2n*_XD!+Auv#LJEM)(~Hx{$Yb^ldQmcYF2zNH1V30*)CN_|1$v2|`LnFUT$%-tO0Eg|c5$BB~yDfzS zcOXJ$wpzVK0MfTjBJ0b$r#_OvAJ3WRt+YOLlJPYMx~qp>^$$$h#bc|`g0pF-Ao43? z>*A+8lx>}L{p(Tni2Vvk)dtzg$hUKjSjXRagj)$h#8=KV>5s)J4vGtRn5kP|AXIz! zPgbbVxW{2o4s-UM;c#We8P&mPN|DW7_uLF!a|^0S=wr6Esx9Z$2|c1?GaupU6$tb| zY_KU`(_29O_%k(;>^|6*pZURH3`@%EuKS;Ns z1lujmf;r{qAN&Q0&m{wJSZ8MeE7RM5+Sq;ul_ z`+ADrd_Um+G37js6tKsArNB}n{p*zTUxQr>3@wA;{EUbjNjlNd6$Mx zg0|MyU)v`sa~tEY5$en7^PkC=S<2@!nEdG6L=h(vT__0F=S8Y&eM=hal#7eM(o^Lu z2?^;05&|CNliYrq6gUv;|i!(W{0N)LWd*@{2q*u)}u*> z7MQgk6t9OqqXMln?zoMAJcc zMKaof_Up})q#DzdF?w^%tTI7STI^@8=Wk#enR*)&%8yje>+tKvUYbW8UAPg55xb70 zEn5&Ba~NmOJlgI#iS8W3-@N%>V!#z-ZRwfPO1)dQdQkaHsiqG|~we2ALqG7Ruup(DqSOft2RFg_X%3w?6VqvV1uzX_@F(diNVp z4{I|}35=11u$;?|JFBEE*gb;T`dy+8gWJ9~pNsecrO`t#V9jW-6mnfO@ff9od}b(3s4>p0i30gbGIv~1@a^F2kl7YO;DxmF3? zWi-RoXhzRJV0&XE@ACc?+@6?)LQ2XNm4KfalMtsc%4!Fn0rl zpHTrHwR>t>7W?t!Yc{*-^xN%9P0cs0kr=`?bQ5T*oOo&VRRu+1chM!qj%2I!@+1XF z4GWJ=7ix9;Wa@xoZ0RP`NCWw0*8247Y4jIZ>GEW7zuoCFXl6xIvz$ezsWgKdVMBH> z{o!A7f;R-@eK9Vj7R40xx)T<2$?F2E<>Jy3F;;=Yt}WE59J!1WN367 zA^6pu_zLoZIf*x031CcwotS{L8bJE(<_F%j_KJ2P_IusaZXwN$&^t716W{M6X2r_~ zaiMwdISX7Y&Qi&Uh0upS3TyEIXNDICQlT5fHXC`aji-c{U(J@qh-mWl-uMN|T&435 z5)a1dvB|oe%b2mefc=Vpm0C%IUYYh7HI*;3UdgNIz}R##(#{(_>82|zB0L*1i4B5j-xi9O4x10rs_J6*gdRBX=@VJ+==sWb&_Qc6tSOowM{BX@(zawtjl zdU!F4OYw2@Tk1L^%~JCwb|e#3CC>srRHQ*(N%!7$Mu_sKh@|*XtR>)BmWw!;8-mq7 zBBnbjwx8Kyv|hd*`5}84flTHR1Y@@uqjG`UG+jN_YK&RYTt7DVwfEDXDW4U+iO{>K zw1hr{_XE*S*K9TzzUlJH2rh^hUm2v7_XjwTuYap|>zeEDY$HOq3X4Tz^X}E9z)x4F zs+T?Ed+Hj<#jY-`Va~fT2C$=qFT-5q$@p9~0{G&eeL~tiIAHXA!f6C(rAlS^)&k<- zXU|ZVs}XQ>s5iONo~t!XXZgtaP$Iau;JT%h)>}v54yut~pykaNye4axEK#5@?TSsQ zE;Jvf9I$GVb|S`7$pG)4vgo9NXsKr?u=F!GnA%VS2z$@Z(!MR9?EPcAqi5ft)Iz6sNl`%kj+_H-X`R<>BFrBW=fSlD|{`D%@Rcbu2?%>t7i34k?Ujb)2@J-`j#4 zLK<69qcUuniIan-$A1+fR=?@+thwDIXtF1Tks@Br-xY zfB+zblrR(ke`U;6U~-;p1Kg8Lh6v~LjW@9l2P6s+?$2!ZRPX`(ZkRGe7~q(4&gEi<$ch`5kQ?*1=GSqkeV z{SA1EaW_A!t{@^UY2D^YO0(H@+kFVzZaAh0_`A`f(}G~EP~?B|%gtxu&g%^x{EYSz zk+T;_c@d;+n@$<>V%P=nk36?L!}?*=vK4>nJSm+1%a}9UlmTJTrfX4{Lb7smNQn@T zw9p2%(Zjl^bWGo1;DuMHN(djsEm)P8mEC2sL@KyPjwD@d%QnZ$ zMJ3cnn!_!iP{MzWk%PI&D?m?C(y2d|2VChluN^yHya(b`h>~GkI1y;}O_E57zOs!{ zt2C@M$^PR2U#(dZmA-sNreB@z-yb0Bf7j*yONhZG=onhx>t4)RB`r6&TP$n zgmN*)eCqvgriBO-abHQ8ECN0bw?z5Bxpx z=jF@?zFdVn?@gD5egM4o$m`}lV(CWrOKKq(sv*`mNcHcvw&Xryfw<{ch{O&qc#WCTXX6=#{MV@q#iHYba!OUY+MGeNTjP%Fj!WgM&`&RlI^=AWTOqy-o zHo9YFt!gQ*p7{Fl86>#-JLZo(b^O`LdFK~OsZBRR@6P?ad^Ujbqm_j^XycM4ZHFyg ziUbIFW#2tj`65~#2V!4z7DM8Z;fG0|APaQ{a2VNYpNotB7eZ5kp+tPDz&Lqs0j%Y4tA*URpcfi z_M(FD=fRGdqf430j}1z`O0I=;tLu81bwJXdYiN7_&a-?ly|-j*+=--XGvCq#32Gh(=|qj5F?kmihk{%M&$}udW5)DHK zF_>}5R8&&API}o0osZJRL3n~>76nUZ&L&iy^s>PMnNcYZ|9*1$v-bzbT3rpWsJ+y{ zPrg>5Zlery96Um?lc6L|)}&{992{_$J&=4%nRp9BAC6!IB=A&=tF>r8S*O-=!G(_( zwXbX_rGZgeiK*&n5E;f=k{ktyA1(;x_kiMEt0*gpp_4&(twlS2e5C?NoD{n>X2AT# zY@Zp?#!b1zNq96MQqeO*M1MMBin5v#RH52&Xd~DO6-BZLnA6xO1$sou(YJ1Dlc{WF zVa%2DyYm`V#81jP@70IJ;DX@y*iUt$MLm)ByAD$eUuji|5{ptFYq(q)mE(5bOpxjM z^Q`AHWq44SG3`_LxC9fwR)XRVIp=B%<(-lOC3jI#bb@dK(*vjom!=t|#<@dZql%>O z15y^{4tQoeW9Lu%G&V$90x6F)xN6y_oIn;!Q zs)8jT$;&;u%Y>=T3hg34A-+Y*na=|glcStr5D;&5*t5*DmD~x;zQAV5{}Ya`?RRGa zT*t9@$a~!co;pD^!J5bo?lDOWFx%)Y=-fJ+PDGc0>;=q=s?P4aHForSB+)v0WY2JH z?*`O;RHum6j%#LG)Vu#ciO#+jRC3!>T(9fr+XE7T2B7Z|0nR5jw@WG)kDDzTJ=o4~ zUpeyt7}_nd`t}j9BKqryOha{34erm)RmST)_9Aw)@ zHbiyg5n&E{_CQR@h<}34d7WM{s{%5wdty1l+KX8*?+-YkNK2Be*6&jc>@{Fd;Ps|| z26LqdI3#9le?;}risDq$K5G3yoqK}C^@-8z^wj%tdgw-6@F#Ju{Sg7+y)L?)U$ez> zoOaP$UFZ?y5BiFycir*pnaAaY+|%1%8&|(@VB)zweR%?IidwJyK5J!STzw&2RFx zZV@qeaCB01Hu#U9|1#=Msc8Pgz5P*4Lrp!Q+~(G!OiNR{qa7|r^H?FC6gVhkk3y7=uW#Sh;&>78bZ}aK*C#NH$9rX@M3f{nckYI+5QG?Aj1DM)@~z_ zw!UAD@gedTlePB*%4+55naJ8ak_;))#S;4ji!LOqY5VRI){GMwHR~}6t4g>5C_#U# ztYC!tjKjrKvRy=GAsJVK++~$|+s!w9z3H4G^mACv=EErXNSmH7qN}%PKcN|8%9=i)qS5+$L zu&ya~HW%RMVJi4T^pv?>mw*Gf<)-7gf#Qj|e#w2|v4#t!%Jk{&xlf;$_?jW*n!Pyx zkG$<18kiLOAUPuFfyu-EfWX%4jYnjBYc~~*9JEz6oa)_R|8wjZA|RNrAp%}14L7fW zi7A5Wym*K+V8pkqqO-X#3ft{0qs?KVt^)?kS>AicmeO&q+~J~ zp0YJ_P~_a8j= zsAs~G=8F=M{4GZL{|B__UorX@MRNQLn?*_gym4aW(~+i13knnk1P=khoC-ViMZk+x zLW(l}oAg1H`dU+Fv**;qw|ANDSRs>cGqL!Yw^`; zv;{E&8CNJcc)GHzTYM}f&NPw<6j{C3gaeelU#y!M)w-utYEHOCCJo|Vgp7K6C_$14 zqIrLUB0bsgz^D%V%fbo2f9#yb#CntTX?55Xy|Kps&Xek*4_r=KDZ z+`TQuv|$l}MWLzA5Ay6Cvsa^7xvwXpy?`w(6vx4XJ zWuf1bVSb#U8{xlY4+wlZ$9jjPk)X_;NFMqdgq>m&W=!KtP+6NL57`AMljW+es zzqjUjgz;V*kktJI?!NOg^s_)ph45>4UDA!Vo0hn>KZ+h-3=?Y3*R=#!fOX zP$Y~+14$f66ix?UWB_6r#fMcC^~X4R-<&OD1CSDNuX~y^YwJ>sW0j`T<2+3F9>cLo z#!j57$ll2K9(%$4>eA7(>FJX5e)pR5&EZK!IMQzOfik#FU*o*LGz~7u(8}XzIQRy- z!U7AlMTIe|DgQFmc%cHy_9^{o`eD%ja_L>ckU6$O4*U**o5uR7`FzqkU8k4gxtI=o z^P^oGFPm5jwZMI{;nH}$?p@uV8FT4r=|#GziKXK07bHJLtK}X%I0TON$uj(iJ`SY^ zc$b2CoxCQ>7LH@nxcdW&_C#fMYBtTxcg46dL{vf%EFCZ~eErMvZq&Z%Lhumnkn^4A zsx$ay(FnN7kYah}tZ@0?-0Niroa~13`?hVi6`ndno`G+E8;$<6^gsE-K3)TxyoJ4M zb6pj5=I8^FD5H@`^V#Qb2^0cx7wUz&cruA5g>6>qR5)O^t1(-qqP&1g=qvY#s&{bx zq8Hc%LsbK1*%n|Y=FfojpE;w~)G0-X4i*K3{o|J7`krhIOd*c*$y{WIKz2n2*EXEH zT{oml3Th5k*vkswuFXdGDlcLj15Nec5pFfZ*0?XHaF_lVuiB%Pv&p7z)%38}%$Gup zVTa~C8=cw%6BKn_|4E?bPNW4PT7}jZQLhDJhvf4z;~L)506IE0 zX!tWXX(QOQPRj-p80QG79t8T2^az4Zp2hOHziQlvT!|H)jv{Ixodabzv6lBj)6WRB z{)Kg@$~~(7$-az?lw$4@L%I&DI0Lo)PEJJziWP33a3azb?jyXt1v0N>2kxwA6b%l> zZqRpAo)Npi&loWbjFWtEV)783BbeIAhqyuc+~>i7aQ8shIXt)bjCWT6$~ro^>99G} z2XfmT0(|l!)XJb^E!#3z4oEGIsL(xd; zYX1`1I(cG|u#4R4T&C|m*9KB1`UzKvho5R@1eYtUL9B72{i(ir&ls8g!pD ztR|25xGaF!4z5M+U@@lQf(12?xGy`!|3E}7pI$k`jOIFjiDr{tqf0va&3pOn6Pu)% z@xtG2zjYuJXrV)DUrIF*y<1O1<$#54kZ#2;=X51J^F#0nZ0(;S$OZDt_U2bx{RZ=Q zMMdd$fH|!s{ zXq#l;{`xfV`gp&C>A`WrQU?d{!Ey5(1u*VLJt>i27aZ-^&2IIk=zP5p+{$q(K?2(b z8?9h)kvj9SF!Dr zoyF}?V|9;6abHxWk2cEvGs$-}Pg}D+ZzgkaN&$Snp%;5m%zh1E#?Wac-}x?BYlGN#U#Mek*}kek#I9XaHt?mz3*fDrRTQ#&#~xyeqJk1QJ~E$7qsw6 z?sV;|?*=-{M<1+hXoj?@-$y+(^BJ1H~wQ9G8C0#^aEAyhDduNX@haoa=PuPp zYsGv8UBfQaRHgBgLjmP^eh>fLMeh{8ic)?xz?#3kX-D#Z{;W#cd_`9OMFIaJg-=t`_3*!YDgtNQ2+QUEAJB9M{~AvT$H`E)IKmCR21H532+ata8_i_MR@ z2Xj<3w<`isF~Ah$W{|9;51ub*f4#9ziKrOR&jM{x7I_7()O@`F*5o$KtZ?fxU~g`t zUovNEVKYn$U~VX8eR)qb`7;D8pn*Pp$(otYTqL)5KH$lUS-jf}PGBjy$weoceAcPp z&5ZYB$r&P$MN{0H0AxCe4Qmd3T%M*5d4i%#!nmBCN-WU-4m4Tjxn-%j3HagwTxCZ9 z)j5vO-C7%s%D!&UfO>bi2oXiCw<-w{vVTK^rVbv#W=WjdADJy8$khnU!`ZWCIU`># zyjc^1W~pcu>@lDZ{zr6gv%)2X4n27~Ve+cQqcND%0?IFSP4sH#yIaXXYAq^z3|cg` z`I3$m%jra>e2W-=DiD@84T!cb%||k)nPmEE09NC%@PS_OLhkrX*U!cgD*;;&gIaA(DyVT4QD+q_xu z>r`tg{hiGY&DvD-)B*h+YEd+Zn)WylQl}<4>(_NlsKXCRV;a)Rcw!wtelM2_rWX`j zTh5A|i6=2BA(iMCnj_fob@*eA;V?oa4Z1kRBGaU07O70fb6-qmA$Hg$ps@^ka1=RO zTbE_2#)1bndC3VuK@e!Sftxq4=Uux}fDxXE#Q5_x=E1h>T5`DPHz zbH<_OjWx$wy7=%0!mo*qH*7N4tySm+R0~(rbus`7;+wGh;C0O%x~fEMkt!eV>U$`i z5>Q(o z=t$gPjgGh0&I7KY#k50V7DJRX<%^X z>6+ebc9efB3@eE2Tr){;?_w`vhgF>`-GDY(YkR{9RH(MiCnyRtd!LxXJ75z+?2 zGi@m^+2hKJ5sB1@Xi@s_@p_Kwbc<*LQ_`mr^Y%j}(sV_$`J(?_FWP)4NW*BIL~sR>t6 zM;qTJZ~GoY36&{h-Pf}L#y2UtR}>ZaI%A6VkU>vG4~}9^i$5WP2Tj?Cc}5oQxe2=q z8BeLa$hwCg_psjZyC2+?yX4*hJ58Wu^w9}}7X*+i5Rjqu5^@GzXiw#SUir1G1`jY% zOL=GE_ENYxhcyUrEt9XlMNP6kx6h&%6^u3@zB8KUCAa18T(R2J`%JjWZ z!{7cXaEW+Qu*iJPu+m>QqW}Lo$4Z+!I)0JNzZ&_M%=|B1yejFRM04bGAvu{=lNPd+ zJRI^DRQ(?FcVUD+bgEcAi@o(msqys9RTCG#)TjI!9~3-dc`>gW;HSJuQvH~d`MQs86R$|SKXHh zqS9Qy)u;T`>>a!$LuaE2keJV%;8g)tr&Nnc;EkvA-RanHXsy)D@XN0a>h}z2j81R; zsUNJf&g&rKpuD0WD@=dDrPHdBoK42WoBU|nMo17o(5^;M|dB4?|FsAGVrSyWcI`+FVw^vTVC`y}f(BwJl zrw3Sp151^9=}B})6@H*i4-dIN_o^br+BkcLa^H56|^2XsT0dESw2 zMX>(KqNl=x2K5=zIKg}2JpGAZu{I_IO}0$EQ5P{4zol**PCt3F4`GX}2@vr8#Y)~J zKb)gJeHcFnR@4SSh%b;c%J`l=W*40UPjF#q{<}ywv-=vHRFmDjv)NtmC zQx9qm)d%0zH&qG7AFa3VAU1S^(n8VFTC~Hb+HjYMjX8r#&_0MzlNR*mnLH5hi}`@{ zK$8qiDDvS_(L9_2vHgzEQ${DYSE;DqB!g*jhJghE&=LTnbgl&Xepo<*uRtV{2wDHN z)l;Kg$TA>Y|K8Lc&LjWGj<+bp4Hiye_@BfU(y#nF{fpR&|Ltbye?e^j0}8JC4#xi% zv29ZR%8%hk=3ZDvO-@1u8KmQ@6p%E|dlHuy#H1&MiC<*$YdLkHmR#F3ae;bKd;@*i z2_VfELG=B}JMLCO-6UQy^>RDE%K4b>c%9ki`f~Z2Qu8hO7C#t%Aeg8E%+}6P7Twtg z-)dj(w}_zFK&86KR@q9MHicUAucLVshUdmz_2@32(V`y3`&Kf8Q2I)+!n0mR=rrDU zXvv^$ho;yh*kNqJ#r1}b0|i|xRUF6;lhx$M*uG3SNLUTC@|htC z-=fsw^F%$qqz4%QdjBrS+ov}Qv!z00E+JWas>p?z@=t!WWU3K*?Z(0meTuTOC7OTx zU|kFLE0bLZ+WGcL$u4E}5dB0g`h|uwv3=H6f+{5z9oLv-=Q45+n~V4WwgO=CabjM% zBAN+RjM65(-}>Q2V#i1Na@a0`08g&y;W#@sBiX6Tpy8r}*+{RnyGUT`?XeHSqo#|J z^ww~c;ou|iyzpErDtlVU=`8N7JSu>4M z_pr9=tX0edVn9B}YFO2y(88j#S{w%E8vVOpAboK*27a7e4Ekjt0)hIX99*1oE;vex z7#%jhY=bPijA=Ce@9rRO(Vl_vnd00!^TAc<+wVvRM9{;hP*rqEL_(RzfK$er_^SN; z)1a8vo8~Dr5?;0X0J62Cusw$A*c^Sx1)dom`-)Pl7hsW4i(r*^Mw`z5K>!2ixB_mu z*Ddqjh}zceRFdmuX1akM1$3>G=#~|y?eYv(e-`Qy?bRHIq=fMaN~fB zUa6I8Rt=)jnplP>yuS+P&PxeWpJ#1$F`iqRl|jF$WL_aZFZl@kLo&d$VJtu&w?Q0O zzuXK>6gmygq(yXJy0C1SL}T8AplK|AGNUOhzlGeK_oo|haD@)5PxF}rV+5`-w{Aag zus45t=FU*{LguJ11Sr-28EZkq;!mJO7AQGih1L4rEyUmp>B!%X0YemsrV3QFvlgt* z5kwlPzaiJ+kZ^PMd-RRbl(Y?F*m`4*UIhIuf#8q>H_M=fM*L_Op-<_r zBZagV=4B|EW+KTja?srADTZXCd3Yv%^Chfpi)cg{ED${SI>InNpRj5!euKv?=Xn92 zsS&FH(*w`qLIy$doc>RE&A5R?u zzkl1sxX|{*fLpXvIW>9d<$ePROttn3oc6R!sN{&Y+>Jr@yeQN$sFR z;w6A<2-0%UA?c8Qf;sX7>>uKRBv3Ni)E9pI{uVzX|6Bb0U)`lhLE3hK58ivfRs1}d zNjlGK0hdq0qjV@q1qI%ZFMLgcpWSY~mB^LK)4GZ^h_@H+3?dAe_a~k*;9P_d7%NEFP6+ zgV(oGr*?W(ql?6SQ~`lUsjLb%MbfC4V$)1E0Y_b|OIYxz4?O|!kRb?BGrgiH5+(>s zoqM}v*;OBfg-D1l`M6T6{K`LG+0dJ1)!??G5g(2*vlNkm%Q(MPABT$r13q?|+kL4- zf)Mi5r$sn;u41aK(K#!m+goyd$c!KPl~-&-({j#D4^7hQkV3W|&>l_b!}!z?4($OA z5IrkfuT#F&S1(`?modY&I40%gtroig{YMvF{K{>5u^I51k8RriGd${z)=5k2tG zM|&Bp5kDTfb#vfuTTd?)a=>bX=lokw^y9+2LS?kwHQIWI~pYgy7 zb?A-RKVm_vM5!9?C%qYdfRAw& zAU7`up~%g=p@}pg#b7E)BFYx3g%(J36Nw(Dij!b>cMl@CSNbrW!DBDbTD4OXk!G4x zi}JBKc8HBYx$J~31PXH+4^x|UxK~(<@I;^3pWN$E=sYma@JP|8YL`L(zI6Y#c%Q{6 z*APf`DU$S4pr#_!60BH$FGViP14iJmbrzSrOkR;f3YZa{#E7Wpd@^4E-zH8EgPc-# zKWFPvh%WbqU_%ZEt`=Q?odKHc7@SUmY{GK`?40VuL~o)bS|is$Hn=<=KGHOsEC5tB zFb|q}gGlL97NUf$G$>^1b^3E18PZ~Pm9kX%*ftnolljiEt@2#F2R5ah$zbXd%V_Ev zyDd{1o_uuoBga$fB@Fw!V5F3jIr=a-ykqrK?WWZ#a(bglI_-8pq74RK*KfQ z0~Dzus7_l;pMJYf>Bk`)`S8gF!To-BdMnVw5M-pyu+aCiC5dwNH|6fgRsIKZcF&)g zr}1|?VOp}I3)IR@m1&HX1~#wsS!4iYqES zK}4J{Ei>;e3>LB#Oly>EZkW14^@YmpbgxCDi#0RgdM${&wxR+LiX}B+iRioOB0(pDKpVEI;ND?wNx>%e|m{RsqR_{(nmQ z3ZS}@t!p4a(BKx_-CYwrcyJ5u1TO9bcXti$8sy>xcLKqKCc#~UOZYD{llKTSFEjJ~ zyNWt>tLU}*>^`TvPxtP%F`ZJQw@W0^>x;!^@?k_)9#bF$j0)S3;mH-IR5y82l|%=F z2lR8zhP?XNP-ucZZ6A+o$xOyF!w;RaLHGh57GZ|TCXhJqY~GCh)aXEV$1O&$c}La1 zjuJxkY9SM4av^Hb;i7efiYaMwI%jGy`3NdY)+mcJhF(3XEiSlU3c|jMBi|;m-c?~T z+x0_@;SxcoY=(6xNgO$bBt~Pj8`-<1S|;Bsjrzw3@zSjt^JC3X3*$HI79i~!$RmTz zsblZsLYs7L$|=1CB$8qS!tXrWs!F@BVuh?kN(PvE5Av-*r^iYu+L^j^m9JG^#=m>@ z=1soa)H*w6KzoR$B8mBCXoU;f5^bVuwQ3~2LKg!yxomG1#XPmn(?YH@E~_ED+W6mxs%x{%Z<$pW`~ON1~2XjP5v(0{C{+6Dm$00tsd3w=f=ZENy zOgb-=f}|Hb*LQ$YdWg<(u7x3`PKF)B7ZfZ6;1FrNM63 z?O6tE%EiU@6%rVuwIQjvGtOofZBGZT1Sh(xLIYt9c4VI8`!=UJd2BfLjdRI#SbVAX ziT(f*RI^T!IL5Ac>ql7uduF#nuCRJ1)2bdvAyMxp-5^Ww5p#X{rb5)(X|fEhDHHW{ zw(Lfc$g;+Q`B0AiPGtmK%*aWfQQ$d!*U<|-@n2HZvCWSiw^I>#vh+LyC;aaVWGbmkENr z&kl*8o^_FW$T?rDYLO1Pyi%>@&kJKQoH2E0F`HjcN}Zlnx1ddoDA>G4Xu_jyp6vuT zPvC}pT&Owx+qB`zUeR|4G;OH(<<^_bzkjln0k40t`PQxc$7h(T8Ya~X+9gDc8Z9{Z z&y0RAU}#_kQGrM;__MK9vwIwK^aoqFhk~dK!ARf1zJqHMxF2?7-8|~yoO@_~Ed;_wvT%Vs{9RK$6uUQ|&@#6vyBsFK9eZW1Ft#D2)VpQRwpR(;x^ zdoTgMqfF9iBl%{`QDv7B0~8{8`8k`C4@cbZAXBu00v#kYl!#_Wug{)2PwD5cNp?K^ z9+|d-4z|gZ!L{57>!Ogfbzchm>J1)Y%?NThxIS8frAw@z>Zb9v%3_3~F@<=LG%r*U zaTov}{{^z~SeX!qgSYow`_5)ij*QtGp4lvF`aIGQ>@3ZTkDmsl#@^5*NGjOuu82}o zzLF~Q9SW+mP=>88%eSA1W4_W7-Q>rdq^?t=m6}^tDPaBRGFLg%ak93W!kOp#EO{6& zP%}Iff5HZQ9VW$~+9r=|Quj#z*=YwcnssS~9|ub2>v|u1JXP47vZ1&L1O%Z1DsOrDfSIMHU{VT>&>H=9}G3i@2rP+rx@eU@uE8rJNec zij~#FmuEBj03F1~ct@C@$>y)zB+tVyjV3*n`mtAhIM0$58vM9jOQC}JJOem|EpwqeMuYPxu3sv}oMS?S#o6GGK@8PN59)m&K4Dc&X% z(;XL_kKeYkafzS3Wn5DD>Yiw{LACy_#jY4op(>9q>>-*9@C0M+=b#bknAWZ37^(Ij zq>H%<@>o4a#6NydoF{_M4i4zB_KG)#PSye9bk0Ou8h%1Dtl7Q_y#7*n%g)?m>xF~( zjqvOwC;*qvN_3(*a+w2|ao0D?@okOvg8JskUw(l7n`0fncglavwKd?~l_ryKJ^Ky! zKCHkIC-o7%fFvPa$)YNh022lakMar^dgL=t#@XLyNHHw!b?%WlM)R@^!)I!smZL@k zBi=6wE5)2v&!UNV(&)oOYW(6Qa!nUjDKKBf-~Da=#^HE4(@mWk)LPvhyN3i4goB$3K8iV7uh zsv+a?#c4&NWeK(3AH;ETrMOIFgu{_@%XRwCZ;L=^8Ts)hix4Pf3yJRQ<8xb^CkdmC z?c_gB)XmRsk`9ch#tx4*hO=#qS7={~Vb4*tTf<5P%*-XMfUUYkI9T1cEF;ObfxxI-yNuA=I$dCtz3ey znVkctYD*`fUuZ(57+^B*R=Q}~{1z#2!ca?)+YsRQb+lt^LmEvZt_`=j^wqig+wz@n@ z`LIMQJT3bxMzuKg8EGBU+Q-6cs5(@5W?N>JpZL{$9VF)veF`L5%DSYTNQEypW%6$u zm_~}T{HeHj1bAlKl8ii92l9~$dm=UM21kLemA&b$;^!wB7#IKWGnF$TVq!!lBlG4 z{?Rjz?P(uvid+|i$VH?`-C&Gcb3{(~Vpg`w+O);Wk1|Mrjxrht0GfRUnZqz2MhrXa zqgVC9nemD5)H$to=~hp)c=l9?#~Z_7i~=U-`FZxb-|TR9@YCxx;Zjo-WpMNOn2)z) zFPGGVl%3N$f`gp$gPnWC+f4(rmts%fidpo^BJx72zAd7|*Xi{2VXmbOm)1`w^tm9% znM=0Fg4bDxH5PxPEm{P3#A(mxqlM7SIARP?|2&+c7qmU8kP&iApzL|F>Dz)Ixp_`O zP%xrP1M6@oYhgo$ZWwrAsYLa4 z|I;DAvJxno9HkQrhLPQk-8}=De{9U3U%)dJ$955?_AOms!9gia%)0E$Mp}$+0er@< zq7J&_SzvShM?e%V?_zUu{niL@gt5UFOjFJUJ}L?$f%eU%jUSoujr{^O=?=^{19`ON zlRIy8Uo_nqcPa6@yyz`CM?pMJ^^SN^Fqtt`GQ8Q#W4kE7`V9^LT}j#pMChl!j#g#J zr-=CCaV%xyFeQ9SK+mG(cTwW*)xa(eK;_Z(jy)woZp~> zA(4}-&VH+TEeLzPTqw&FOoK(ZjD~m{KW05fiGLe@E3Z2`rLukIDahE*`u!ubU)9`o zn^-lyht#E#-dt~S>}4y$-mSbR8{T@}22cn^refuQ08NjLOv?JiEWjyOnzk<^R5%gO zhUH_B{oz~u#IYwVnUg8?3P*#DqD8#X;%q%HY**=I>>-S|!X*-!x1{^l#OnR56O>iD zc;i;KS+t$koh)E3)w0OjWJl_aW2;xF=9D9Kr>)(5}4FqUbk# zI#$N8o0w;IChL49m9CJTzoC!|u{Ljd%ECgBOf$}&jA^$(V#P#~)`&g`H8E{uv52pp zwto`xUL-L&WTAVREEm$0g_gYPL(^vHq(*t1WCH_6alhkeW&GCZ3hL)|{O-jiFOBrF z!EW=Jej|dqQitT6!B-7&io2K)WIm~Q)v@yq%U|VpV+I?{y0@Yd%n8~-NuuM*pM~KA z85YB};IS~M(c<}4Hxx>qRK0cdl&e?t253N%vefkgds>Ubn8X}j6Vpgs>a#nFq$osY z1ZRwLqFv=+BTb=i%D2Wv>_yE0z}+niZ4?rE|*a3d7^kndWGwnFqt+iZ(7+aln<}jzbAQ(#Z2SS}3S$%Bd}^ zc9ghB%O)Z_mTZMRC&H#)I#fiLuIkGa^`4e~9oM5zKPx?zjkC&Xy0~r{;S?FS%c7w< zWbMpzc(xSw?9tGxG~_l}Acq}zjt5ClaB7-!vzqnlrX;}$#+PyQ9oU)_DfePh2E1<7 ztok6g6K^k^DuHR*iJ?jw?bs_whk|bx`dxu^nC6#e{1*m~z1eq7m}Cf$*^Eua(oi_I zAL+3opNhJteu&mWQ@kQWPucmiP)4|nFG`b2tpC;h{-PI@`+h?9v=9mn|0R-n8#t=+Z*FD(c5 zjj79Jxkgck*DV=wpFgRZuwr%}KTm+dx?RT@aUHJdaX-ODh~gByS?WGx&czAkvkg;x zrf92l8$Or_zOwJVwh>5rB`Q5_5}ef6DjS*$x30nZbuO3dijS*wvNEqTY5p1_A0gWr znH<(Qvb!os14|R)n2Ost>jS2;d1zyLHu`Svm|&dZD+PpP{Bh>U&`Md;gRl64q;>{8MJJM$?UNUd`aC>BiLe>*{ zJY15->yW+<3rLgYeTruFDtk1ovU<$(_y7#HgUq>)r0{^}Xbth}V#6?%5jeFYt;SG^ z3qF)=uWRU;Jj)Q}cpY8-H+l_n$2$6{ZR?&*IGr{>ek!69ZH0ZoJ*Ji+ezzlJ^%qL3 zO5a`6gwFw(moEzqxh=yJ9M1FTn!eo&qD#y5AZXErHs%22?A+JmS&GIolml!)rZTnUDM3YgzYfT#;OXn)`PWv3Ta z!-i|-Wojv*k&bC}_JJDjiAK(Ba|YZgUI{f}TdEOFT2+}nPmttytw7j%@bQZDV1vvj z^rp{gRkCDmYJHGrE1~e~AE!-&6B6`7UxVQuvRrfdFkGX8H~SNP_X4EodVd;lXd^>eV1jN+Tt4}Rsn)R0LxBz0c=NXU|pUe!MQQFkGBWbR3&(jLm z%RSLc#p}5_dO{GD=DEFr=Fc% z85CBF>*t!6ugI?soX(*JNxBp+-DdZ4X0LldiK}+WWGvXV(C(Ht|!3$psR=&c*HIM=BmX;pRIpz@Ale{9dhGe(U2|Giv;# zOc|;?p67J=Q(kamB*aus=|XP|m{jN^6@V*Bpm?ye56Njh#vyJqE=DweC;?Rv7faX~ zde03n^I~0B2vUmr;w^X37tVxUK?4}ifsSH5_kpKZIzpYu0;Kv}SBGfI2AKNp+VN#z`nI{UNDRbo-wqa4NEls zICRJpu)??cj^*WcZ^MAv+;bDbh~gpN$1Cor<{Y2oyIDws^JsfW^5AL$azE(T0p&pP z1Mv~6Q44R&RHoH95&OuGx2srIr<@zYJTOMKiVs;Bx3py89I87LOb@%mr`0)#;7_~Z zzcZj8?w=)>%5@HoCHE_&hnu(n_yQ-L(~VjpjjkbT7e)Dk5??fApg(d>vwLRJ-x{um z*Nt?DqTSxh_MIyogY!vf1mU1`Gld-&L)*43f6dilz`Q@HEz;+>MDDYv9u!s;WXeao zUq=TaL$P*IFgJzrGc>j1dDOd zed+=ZBo?w4mr$2)Ya}?vedDopomhW1`#P<%YOJ_j=WwClX0xJH-f@s?^tmzs_j7t!k zK@j^zS0Q|mM4tVP5Ram$VbS6|YDY&y?Q1r1joe9dj08#CM{RSMTU}(RCh`hp_Rkl- zGd|Cv~G@F{DLhCizAm9AN!^{rNs8hu!G@8RpnGx7e`-+K$ffN<0qjR zGq^$dj_Tv!n*?zOSyk5skI7JVKJ)3jysnjIu-@VSzQiP8r6MzudCU=~?v-U8yzo^7 zGf~SUTvEp+S*!X9uX!sq=o}lH;r{pzk~M*VA(uyQ`3C8!{C;)&6)95fv(cK!%Cuz$ z_Zal57H6kPN>25KNiI6z6F)jzEkh#%OqU#-__Xzy)KyH};81#N6OfX$$IXWzOn`Q& z4f$Z1t>)8&8PcYfEwY5UadU1yg+U*(1m2ZlHoC-!2?gB!!fLhmTl))D@dhvkx#+Yj z1O=LV{(T%{^IeCuFK>%QR!VZ4GnO5tK8a+thWE zg4VytZrwcS?7^ zuZfhYnB8dwd%VLO?DK7pV5Wi<(`~DYqOXn8#jUIL^)12*Dbhk4GmL_E2`WX&iT16o zk(t|hok(Y|v-wzn?4x34T)|+SfZP>fiq!><*%vnxGN~ypST-FtC+@TPv*vYv@iU!_ z@2gf|PrgQ?Ktf*9^CnJ(x*CtZVB8!OBfg0%!wL;Z8(tYYre0vcnPGlyCc$V(Ipl*P z_(J!a=o@vp^%Efme!K74(Ke7A>Y}|sxV+JL^aYa{~m%5#$$+R1? zGaQhZTTX!#s#=Xtpegqero$RNt&`4xn3g$)=y*;=N=Qai)}~`xtxI_N*#MMCIq#HFifT zz(-*m;pVH&+4bixL&Bbg)W5FN^bH87pAHp)zPkWNMfTFqS=l~AC$3FX3kQUSh_C?-ZftyClgM)o_D7cX$RGlEYblux0jv5 zTr|i-I3@ZPCGheCl~BGhImF)K4!9@?pC(gi3ozX=a!|r1)LFxy_8c&wY0<^{2cm|P zv6Y`QktY*;I)IUd5y3ne1CqpVanlY45z8hf4&$EUBnucDj16pDa4&GI&TArYhf*xh zdj>*%APH8(h~c>o@l#%T>R$e>rwVx_WUB|~V`p^JHsg*y12lzj&zF}w6W09HwB2yb z%Q~`es&(;7#*DUC_w-Dmt7|$*?TA_m;zB+-u{2;Bg{O}nV7G_@7~<)Bv8fH^G$XG8$(&{A zwXJK5LRK%M34(t$&NI~MHT{UQ9qN-V_yn|%PqC81EIiSzmMM=2zb`mIwiP_b)x+2M z7Gd`83h79j#SItpQ}luuf2uOU`my_rY5T{6P#BNlb%h%<#MZb=m@y5aW;#o1^2Z)SWo+b`y0gV^iRcZtz5!-05vF z7wNo=hc6h4hc&s@uL^jqRvD6thVYtbErDK9k!;+a0xoE0WL7zLixjn5;$fXvT=O3I zT6jI&^A7k6R{&5#lVjz#8%_RiAa2{di{`kx79K+j72$H(!ass|B%@l%KeeKchYLe_ z>!(JC2fxsv>XVen+Y42GeYPxMWqm`6F$(E<6^s|g(slNk!lL*6v^W2>f6hh^mE$s= z3D$)}{V5(Qm&A6bp%2Q}*GZ5Qrf}n7*Hr51?bJOyA-?B4vg6y_EX<*-e20h{=0Mxs zbuQGZ$fLyO5v$nQ&^kuH+mNq9O#MWSfThtH|0q1i!NrWj^S}_P;Q1OkYLW6U^?_7G zx2wg?CULj7))QU(n{$0JE%1t2dWrMi2g-Os{v|8^wK{@qlj%+1b^?NI z$}l2tjp0g>K3O+p%yK<9!XqmQ?E9>z&(|^Pi~aSRwI5x$jaA62GFz9%fmO3t3a>cq zK8Xbv=5Ps~4mKN5+Eqw12(!PEyedFXv~VLxMB~HwT1Vfo51pQ#D8e$e4pFZ{&RC2P z5gTIzl{3!&(tor^BwZfR8j4k{7Rq#`riKXP2O-Bh66#WWK2w=z;iD9GLl+3 zpHIaI4#lQ&S-xBK8PiQ%dwOh?%BO~DCo06pN7<^dnZCN@NzY{_Z1>rrB0U|nC&+!2 z2y!oBcTd2;@lzyk(B=TkyZ)zy0deK05*Q0zk+o$@nun`VI1Er7pjq>8V zNmlW{p7S^Btgb(TA}jL(uR>`0w8gHP^T~Sh5Tkip^spk4SBAhC{TZU}_Z)UJw-}zm zPq{KBm!k)?P{`-(9?LFt&YN4s%SIZ-9lJ!Ws~B%exHOeVFk3~}HewnnH(d)qkLQ_d z6h>O)pEE{vbOVw}E+jdYC^wM+AAhaI(YAibUc@B#_mDss0Ji&BK{WG`4 zOk>vSNq(Bq2IB@s>>Rxm6Wv?h;ZXkpb1l8u|+_qXWdC*jjcPCixq;!%BVPSp#hP zqo`%cNf&YoQXHC$D=D45RiT|5ngPlh?0T~?lUf*O)){K@*Kbh?3RW1j9-T?%lDk@y z4+~?wKI%Y!-=O|_IuKz|=)F;V7ps=5@g)RrE;;tvM$gUhG>jHcw2Hr@fS+k^Zr~>G z^JvPrZc}_&d_kEsqAEMTMJw!!CBw)u&ZVzmq+ZworuaE&TT>$pYsd9|g9O^0orAe8 z221?Va!l1|Y5X1Y?{G7rt1sX#qFA^?RLG^VjoxPf63;AS=_mVDfGJKg73L zsGdnTUD40y(>S##2l|W2Cy!H(@@5KBa(#gs`vlz}Y~$ot5VsqPQ{{YtjYFvIumZzt zA{CcxZLJR|4#{j7k~Tu*jkwz8QA|5G1$Cl895R`Zyp;irp1{KN){kB30O8P1W5;@bG znvX74roeMmQlUi=v9Y%(wl$ZC#9tKNFpvi3!C}f1m6Ct|l2g%psc{TJp)@yu)*e2> z((p0Fg*8gJ!|3WZke9;Z{8}&NRkv7iP=#_y-F}x^y?2m%-D_aj^)f04%mneyjo_;) z6qc_Zu$q37d~X``*eP~Q>I2gg%rrV8v=kDfpp$=%Vj}hF)^dsSWygoN(A$g*E=Do6FX?&(@F#7pbiJ`;c0c@Ul zDqW_90Wm#5f2L<(Lf3)3TeXtI7nhYwRm(F;*r_G6K@OPW4H(Y3O5SjUzBC}u3d|eQ8*8d@?;zUPE+i#QNMn=r(ap?2SH@vo*m z3HJ%XuG_S6;QbWy-l%qU;8x;>z>4pMW7>R}J%QLf%@1BY(4f_1iixd-6GlO7Vp*yU zp{VU^3?s?90i=!#>H`lxT!q8rk>W_$2~kbpz7eV{3wR|8E=8**5?qn8#n`*(bt1xRQrdGxyx2y%B$qmw#>ZV$c7%cO#%JM1lY$Y0q?Yuo> ze9KdJoiM)RH*SB%^;TAdX-zEjA7@%y=!0=Zg%iWK7jVI9b&Dk}0$Af&08KHo+ zOwDhFvA(E|ER%a^cdh@^wLUlmIv6?_3=BvX8jKk92L=Y}7Jf5OGMfh` zBdR1wFCi-i5@`9km{isRb0O%TX+f~)KNaEz{rXQa89`YIF;EN&gN)cigu6mNh>?Cm zAO&Im2flv6D{jwm+y<%WsPe4!89n~KN|7}Cb{Z;XweER73r}Qp2 zz}WP4j}U0&(uD&9yGy6`!+_v-S(yG*iytsTR#x_Rc>=6u^vnRDnf1gP{#2>`ffrAC% zTZ5WQ@hAK;P;>kX{D)mIXe4%a5p=LO1xXH@8T?mz7Q@d)$3pL{{B!2{-v70L*o1AO+|n5beiw~ zk@(>m?T3{2k2c;NWc^`4@P&Z?BjxXJ@;x1qhn)9Mn*IFdt_J-dIqx5#d`NfyfX~m( zIS~5)MfZ2Uy?_4W`47i}u0ZgPh<{D|w_d#;D}Q&U$Q-G}xM1A@1f{#%A$jh6Qp&0hQ<0bPOM z-{1Wm&p%%#eb_?x7i;bol EfAhh=DF6Tf diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index abd303b673..d58dfb70ba 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,19 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip diff --git a/mvnw b/mvnw index 41c0f0c23d..19529ddf8c 100755 --- a/mvnw +++ b/mvnw @@ -19,292 +19,241 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir +# Apache Maven Wrapper startup batch script, version 3.3.2 # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac -fi +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 fi fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" +} - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" done + printf %x\\n $h +} - saveddir=`pwd` +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - M2_HOME=`dirname "$PRG"`/.. +die() { + printf %s\\n "$1" >&2 + exit 1 +} - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" fi -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" fi -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; fi -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 86115719e5..249bdf3822 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,3 +1,4 @@ +<# : batch portion @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @@ -18,165 +19,131 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir +@REM Apache Maven Wrapper startup batch script, version 3.3.2 @REM @REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output @REM ---------------------------------------------------------------------------- -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) ) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" From 57d4f88a48f6dc95f02ecbd9d19b6c396be2444f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 20 Jan 2025 07:56:19 +0100 Subject: [PATCH 475/597] Java EA GitHub Actions improvements (#3803) * Use Java 17 for building MapStruct (still Java 8 compatible) * Upgrade to Spring 6 for tests * Adjust excludes and min tests version for some integration tests --- .github/workflows/java-ea.yml | 6 +-- .github/workflows/main.yml | 43 ++++++++----------- .github/workflows/release.yml | 2 +- ...eatureCompilationExclusionCliEnhancer.java | 3 ++ .../itest/tests/MavenIntegrationTest.java | 8 ++++ .../ProcessorInvocationInterceptor.java | 10 ++++- .../test/resources/fullFeatureTest/pom.xml | 10 +++++ .../superTypeGenerationTest/generator/pom.xml | 1 - .../superTypeGenerationTest/usage/pom.xml | 1 - .../generator/pom.xml | 1 - .../targetTypeGenerationTest/usage/pom.xml | 1 - parent/pom.xml | 6 +-- processor/pom.xml | 2 +- .../_1395/{ => spring}/Issue1395Mapper.java | 2 +- .../_1395/{ => spring}/Issue1395Test.java | 2 +- .../_1395/{ => spring}/NotUsedService.java | 2 +- .../test/bugs/_1395/{ => spring}/Source.java | 2 +- .../test/bugs/_1395/{ => spring}/Target.java | 2 +- .../_2807/{ => spring}/Issue2807Test.java | 8 ++-- .../{ => spring}/SpringLifeCycleMapper.java | 8 ++-- .../_2807/{ => spring}/after/AfterMethod.java | 2 +- .../{ => spring}/before/BeforeMethod.java | 2 +- .../beforewithtarget/BeforeWithTarget.java | 2 +- .../test/bugs/_880/{ => spring}/Config.java | 2 +- .../DefaultsToProcessorOptionsMapper.java | 2 +- .../bugs/_880/{ => spring}/Issue880Test.java | 2 +- .../test/bugs/_880/{ => spring}/Poodle.java | 2 +- .../UsesConfigFromAnnotationMapper.java | 2 +- .../decorator/jsr330/Jsr330DecoratorTest.java | 3 ++ ...30DefaultCompileOptionFieldMapperTest.java | 3 ++ ...330CompileOptionConstructorMapperTest.java | 3 ++ .../Jsr330ConstructorMapperTest.java | 3 ++ .../jsr330/field/Jsr330FieldMapperTest.java | 3 ++ .../jsr330/setter/Jsr330SetterMapperTest.java | 3 ++ .../org/mapstruct/ap/testutil/WithSpring.java | 3 ++ readme.md | 2 +- 36 files changed, 96 insertions(+), 63 deletions(-) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/{ => spring}/Issue1395Mapper.java (91%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/{ => spring}/Issue1395Test.java (92%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/{ => spring}/NotUsedService.java (81%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/{ => spring}/Source.java (88%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/{ => spring}/Target.java (88%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/{ => spring}/Issue2807Test.java (69%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/{ => spring}/SpringLifeCycleMapper.java (70%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/{ => spring}/after/AfterMethod.java (88%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/{ => spring}/before/BeforeMethod.java (87%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/{ => spring}/beforewithtarget/BeforeWithTarget.java (88%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_880/{ => spring}/Config.java (87%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_880/{ => spring}/DefaultsToProcessorOptionsMapper.java (86%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_880/{ => spring}/Issue880Test.java (97%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_880/{ => spring}/Poodle.java (88%) rename processor/src/test/java/org/mapstruct/ap/test/bugs/_880/{ => spring}/UsesConfigFromAnnotationMapper.java (90%) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index d9b018bfb8..fce2dc06b2 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -7,11 +7,7 @@ env: jobs: test_jdk_ea: - strategy: - fail-fast: false - matrix: - java: [19-ea] - name: 'Linux JDK ${{ matrix.java }}' + name: 'Linux JDK EA' runs-on: ubuntu-latest steps: - name: 'Checkout' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b27dfc075a..9cc098161d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,45 +24,38 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java }} - name: 'Test' - run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true - linux: - name: 'Linux JDK 11' - runs-on: ubuntu-latest - steps: - - name: 'Checkout' - uses: actions/checkout@v3 - - name: 'Set up JDK 11' - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: 11 - - name: 'Test' - run: ./mvnw ${MAVEN_ARGS} install + run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=${{ matrix.java != 17 }} install -DskipDistribution=${{ matrix.java != 17 }} - name: 'Generate coverage report' + if: matrix.java == 17 run: ./mvnw jacoco:report - name: 'Upload coverage to Codecov' + if: matrix.java == 17 uses: codecov/codecov-action@v2 - name: 'Publish Snapshots' - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mapstruct/mapstruct' + if: matrix.java == 17 && github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mapstruct/mapstruct' run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy - linux-jdk-8: - name: 'Linux JDK 8' + integration_test_jdk: + strategy: + fail-fast: false + matrix: + java: [ 8, 11 ] + name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: - name: 'Checkout' uses: actions/checkout@v3 - - name: 'Set up JDK 11 for building everything' + - name: 'Set up JDK 17 for building everything' uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 11 + java-version: 17 - name: 'Install Processor' run: ./mvnw ${MAVEN_ARGS} -DskipTests install -pl processor -am - - name: 'Set up JDK 8 for running integration tests' + - name: 'Set up JDK ${{ matrix.java }} for running integration tests' uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 8 + java-version: ${{ matrix.java }} - name: 'Run integration tests' run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest windows: @@ -70,11 +63,11 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v3 - - name: 'Set up JDK 11' + - name: 'Set up JDK 17' uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 11 + java-version: 17 - name: 'Test' run: ./mvnw %MAVEN_ARGS% install mac: @@ -82,10 +75,10 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v3 - - name: 'Set up JDK 11' + - name: 'Set up JDK 17' uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 11 + java-version: 17 - name: 'Test' run: ./mvnw ${MAVEN_ARGS} install diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f18a12b813..682fa36876 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: 'zulu' cache: maven diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java index 15d9e42598..77a5e2af06 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -30,6 +30,7 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces switch ( currentJreVersion ) { case JAVA_8: + additionalExcludes.add( "org/mapstruct/ap/test/**/spring/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/annotatewith/deprecated/jdk11/*.java" ); @@ -42,6 +43,8 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces // TODO find out why this fails: additionalExcludes.add( "org/mapstruct/ap/test/collection/wildcard/BeanMapper.java" ); break; + case JAVA_11: + additionalExcludes.add( "org/mapstruct/ap/test/**/spring/**/*.java" ); default: } diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 0bef2994f6..ab43cc4cba 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -5,6 +5,7 @@ */ package org.mapstruct.itest.tests; +import org.junit.jupiter.api.condition.DisabledOnJre; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.parallel.Execution; @@ -80,12 +81,15 @@ void jakartaJaxbTest() { } @ProcessorTest(baseDir = "jsr330Test") + @EnabledForJreRange(min = JRE.JAVA_17) + @DisabledOnJre(JRE.OTHER) void jsr330Test() { } @ProcessorTest(baseDir = "lombokBuilderTest", processorTypes = { ProcessorTest.ProcessorType.JAVAC }) + @DisabledOnJre(JRE.OTHER) void lombokBuilderTest() { } @@ -94,6 +98,7 @@ void lombokBuilderTest() { ProcessorTest.ProcessorType.JAVAC_WITH_PATHS }) @EnabledForJreRange(min = JRE.JAVA_11) + @DisabledOnJre(JRE.OTHER) void lombokModuleTest() { } @@ -150,6 +155,7 @@ void expressionTextBlocksTest() { }, forkJvm = true) // We have to fork the jvm because there is an NPE in com.intellij.openapi.util.SystemInfo.getRtVersion // and the kotlin-maven-plugin uses that. See also https://youtrack.jetbrains.com/issue/IDEA-238907 + @DisabledOnJre(JRE.OTHER) void kotlinDataTest() { } @@ -163,6 +169,8 @@ void defaultPackageTest() { } @ProcessorTest(baseDir = "springTest") + @EnabledForJreRange(min = JRE.JAVA_17) + @DisabledOnJre(JRE.OTHER) void springTest() { } diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java index 89009aaffe..85ce64952f 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java @@ -134,14 +134,20 @@ private void addAdditionalCliArguments(Verifier verifier) } private void configureProcessor(Verifier verifier) { - String compilerId = processorTestContext.getProcessor().getCompilerId(); + ProcessorTest.ProcessorType processor = processorTestContext.getProcessor(); + String compilerId = processor.getCompilerId(); if ( compilerId != null ) { - String profile = processorTestContext.getProcessor().getProfile(); + String profile = processor.getProfile(); if ( profile == null ) { profile = "generate-via-compiler-plugin"; } verifier.addCliOption( "-P" + profile ); verifier.addCliOption( "-Dcompiler-id=" + compilerId ); + if ( processor == ProcessorTest.ProcessorType.JAVAC ) { + if ( CURRENT_VERSION.ordinal() >= JRE.JAVA_21.ordinal() ) { + verifier.addCliOption( "-Dmaven.compiler.proc=full" ); + } + } } else { verifier.addCliOption( "-Pgenerate-via-processor-plugin" ); diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index 1a31b28221..849f7150fc 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -27,6 +27,11 @@ x x x + x + x + x + x + x @@ -49,6 +54,11 @@ ${additionalExclude4} ${additionalExclude5} ${additionalExclude6} + ${additionalExclude7} + ${additionalExclude8} + ${additionalExclude9} + ${additionalExclude10} + ${additionalExclude11} diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml index 1b84638ef2..5ab2d0d18f 100644 --- a/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml +++ b/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml @@ -32,7 +32,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.1 -proc:none diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml index ee0d556b9f..d1e1dd7dff 100644 --- a/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml @@ -38,7 +38,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.1 -XprintProcessorInfo diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml index bf0d704851..67df383a18 100644 --- a/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml +++ b/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml @@ -32,7 +32,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.1 -proc:none diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml index da72f667a4..bd06b79a49 100644 --- a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml +++ b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml @@ -38,7 +38,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.1 -XprintProcessorInfo diff --git a/parent/pom.xml b/parent/pom.xml index 94c84fb4d0..5509e783d2 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -34,7 +34,7 @@ 3.4.1 3.2.2 3.1.0 - 5.3.31 + 6.2.2 1.6.0 8.36.1 5.10.1 @@ -47,7 +47,7 @@ jdt_apt 1.8 3.21.7 @@ -400,7 +400,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.13.0 org.apache.maven.plugins diff --git a/processor/pom.xml b/processor/pom.xml index ed2df7718b..13deafb413 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -24,7 +24,7 @@ - 11 + 17 diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Issue1395Mapper.java similarity index 91% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Mapper.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Issue1395Mapper.java index ebc9ae27e0..4e48020c8d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Issue1395Mapper.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._1395; +package org.mapstruct.ap.test.bugs._1395.spring; import org.mapstruct.InjectionStrategy; import org.mapstruct.Mapper; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Issue1395Test.java similarity index 92% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Issue1395Test.java index 042d0288cd..3417fbd707 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Issue1395Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Issue1395Test.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._1395; +package org.mapstruct.ap.test.bugs._1395.spring; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/NotUsedService.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/NotUsedService.java similarity index 81% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/NotUsedService.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/NotUsedService.java index 765764ef2b..ba47cf179f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/NotUsedService.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/NotUsedService.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._1395; +package org.mapstruct.ap.test.bugs._1395.spring; /** * @author Filip Hrisafov diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Source.java similarity index 88% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Source.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Source.java index 1b676c2b62..8f2a52d462 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Source.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._1395; +package org.mapstruct.ap.test.bugs._1395.spring; /** * @author Filip Hrisafov diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Target.java similarity index 88% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Target.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Target.java index 2c7b22fd4d..1ba393dbe0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1395/spring/Target.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._1395; +package org.mapstruct.ap.test.bugs._1395.spring; /** * @author Filip Hrisafov diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/Issue2807Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/Issue2807Test.java similarity index 69% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/Issue2807Test.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/Issue2807Test.java index 2cb96ce37e..102a9d26eb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/Issue2807Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/Issue2807Test.java @@ -3,11 +3,11 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._2807; +package org.mapstruct.ap.test.bugs._2807.spring; -import org.mapstruct.ap.test.bugs._2807.after.AfterMethod; -import org.mapstruct.ap.test.bugs._2807.before.BeforeMethod; -import org.mapstruct.ap.test.bugs._2807.beforewithtarget.BeforeWithTarget; +import org.mapstruct.ap.test.bugs._2807.spring.after.AfterMethod; +import org.mapstruct.ap.test.bugs._2807.spring.before.BeforeMethod; +import org.mapstruct.ap.test.bugs._2807.spring.beforewithtarget.BeforeWithTarget; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/SpringLifeCycleMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/SpringLifeCycleMapper.java similarity index 70% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/SpringLifeCycleMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/SpringLifeCycleMapper.java index daabe7f52a..408a2ca3e0 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/SpringLifeCycleMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/SpringLifeCycleMapper.java @@ -3,15 +3,15 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._2807; +package org.mapstruct.ap.test.bugs._2807.spring; import java.util.List; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; -import org.mapstruct.ap.test.bugs._2807.after.AfterMethod; -import org.mapstruct.ap.test.bugs._2807.before.BeforeMethod; -import org.mapstruct.ap.test.bugs._2807.beforewithtarget.BeforeWithTarget; +import org.mapstruct.ap.test.bugs._2807.spring.after.AfterMethod; +import org.mapstruct.ap.test.bugs._2807.spring.before.BeforeMethod; +import org.mapstruct.ap.test.bugs._2807.spring.beforewithtarget.BeforeWithTarget; import org.mapstruct.factory.Mappers; /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/after/AfterMethod.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/after/AfterMethod.java similarity index 88% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/after/AfterMethod.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/after/AfterMethod.java index f7c7457348..05770c6603 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/after/AfterMethod.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/after/AfterMethod.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._2807.after; +package org.mapstruct.ap.test.bugs._2807.spring.after; import java.util.List; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/before/BeforeMethod.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/before/BeforeMethod.java similarity index 87% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/before/BeforeMethod.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/before/BeforeMethod.java index 5252bee150..9fb9e7e883 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/before/BeforeMethod.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/before/BeforeMethod.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._2807.before; +package org.mapstruct.ap.test.bugs._2807.spring.before; import org.mapstruct.BeforeMapping; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/beforewithtarget/BeforeWithTarget.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/beforewithtarget/BeforeWithTarget.java similarity index 88% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/beforewithtarget/BeforeWithTarget.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/beforewithtarget/BeforeWithTarget.java index 69e62bdea1..a3ee8b57ea 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/beforewithtarget/BeforeWithTarget.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2807/spring/beforewithtarget/BeforeWithTarget.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._2807.beforewithtarget; +package org.mapstruct.ap.test.bugs._2807.spring.beforewithtarget; import java.util.List; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Config.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/Config.java similarity index 87% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Config.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/Config.java index 32f58384b9..7aef1e9e1f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Config.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/Config.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._880; +package org.mapstruct.ap.test.bugs._880.spring; import org.mapstruct.MapperConfig; import org.mapstruct.ReportingPolicy; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/DefaultsToProcessorOptionsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/DefaultsToProcessorOptionsMapper.java similarity index 86% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_880/DefaultsToProcessorOptionsMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/DefaultsToProcessorOptionsMapper.java index c54da3f7cb..5f67b41552 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/DefaultsToProcessorOptionsMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/DefaultsToProcessorOptionsMapper.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._880; +package org.mapstruct.ap.test.bugs._880.spring; import org.mapstruct.Mapper; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/Issue880Test.java similarity index 97% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/Issue880Test.java index 9d8540b409..d2f371abed 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Issue880Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/Issue880Test.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._880; +package org.mapstruct.ap.test.bugs._880.spring; import javax.tools.Diagnostic.Kind; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Poodle.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/Poodle.java similarity index 88% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Poodle.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/Poodle.java index e822db1cfe..71166f6d4c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/Poodle.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/Poodle.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._880; +package org.mapstruct.ap.test.bugs._880.spring; /** * @author Andreas Gudian diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/UsesConfigFromAnnotationMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/UsesConfigFromAnnotationMapper.java similarity index 90% rename from processor/src/test/java/org/mapstruct/ap/test/bugs/_880/UsesConfigFromAnnotationMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/UsesConfigFromAnnotationMapper.java index d9a1efe33e..bb9a7def9f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/UsesConfigFromAnnotationMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_880/spring/UsesConfigFromAnnotationMapper.java @@ -3,7 +3,7 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.bugs._880; +package org.mapstruct.ap.test.bugs._880.spring; import org.mapstruct.Mapper; import org.mapstruct.MappingConstants; diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java index a3bad68b92..be1a67cf09 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java @@ -11,6 +11,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.decorator.Address; import org.mapstruct.ap.test.decorator.AddressDto; @@ -46,6 +48,7 @@ @ComponentScan(basePackageClasses = Jsr330DecoratorTest.class) @Configuration @WithJavaxInject +@DisabledOnJre(JRE.OTHER) public class Jsr330DecoratorTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java index 6b92d9815a..192059c8a5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/_default/Jsr330DefaultCompileOptionFieldMapperTest.java @@ -10,6 +10,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; @@ -44,6 +46,7 @@ @ComponentScan(basePackageClasses = CustomerJsr330DefaultCompileOptionFieldMapper.class) @WithJavaxInject @Configuration +@DisabledOnJre(JRE.OTHER) public class Jsr330DefaultCompileOptionFieldMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java index fe9cf295f4..02b634da8d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/compileoptionconstructor/Jsr330CompileOptionConstructorMapperTest.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; @@ -44,6 +46,7 @@ @ComponentScan(basePackageClasses = CustomerJsr330CompileOptionConstructorMapper.class) @Configuration @WithJavaxInject +@DisabledOnJre(JRE.OTHER) public class Jsr330CompileOptionConstructorMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java index a5cfce5c2e..d51843f00c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/constructor/Jsr330ConstructorMapperTest.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; @@ -44,6 +46,7 @@ @ComponentScan(basePackageClasses = CustomerJsr330ConstructorMapper.class) @Configuration @WithJavaxInject +@DisabledOnJre(JRE.OTHER) public class Jsr330ConstructorMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java index 84e67f63a8..d713f9cc88 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/field/Jsr330FieldMapperTest.java @@ -10,6 +10,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; @@ -46,6 +48,7 @@ @ComponentScan(basePackageClasses = CustomerJsr330FieldMapper.class) @Configuration @WithJavaxInject +@DisabledOnJre(JRE.OTHER) public class Jsr330FieldMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/Jsr330SetterMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/Jsr330SetterMapperTest.java index 558206e51c..a6f18ed594 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/Jsr330SetterMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/injectionstrategy/jsr330/setter/Jsr330SetterMapperTest.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerDto; import org.mapstruct.ap.test.injectionstrategy.shared.CustomerEntity; @@ -42,6 +44,7 @@ @ComponentScan(basePackageClasses = CustomerJsr330SetterMapper.class) @Configuration @WithJavaxInject +@DisabledOnJre(JRE.OTHER) public class Jsr330SetterMapperTest { @RegisterExtension diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithSpring.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithSpring.java index 40db5afd81..04e387d429 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/WithSpring.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithSpring.java @@ -10,6 +10,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; /** * Meta annotation that adds the needed Spring Dependencies @@ -23,6 +25,7 @@ "spring-beans", "spring-context" }) +@DisabledOnJre(JRE.OTHER) public @interface WithSpring { } diff --git a/readme.md b/readme.md index adb48b2fd4..907c8411cd 100644 --- a/readme.md +++ b/readme.md @@ -130,7 +130,7 @@ To learn more about MapStruct, refer to the [project homepage](https://mapstruct ## Building from Source -MapStruct uses Maven for its build. Java 11 is required for building MapStruct from source. To build the complete project, run +MapStruct uses Maven for its build. Java 17 is required for building MapStruct from source. To build the complete project, run ./mvnw clean install From c08ba4ca7e2832ad398a61b6a5a181ebaecd4e0d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 22 Jan 2025 08:51:49 +0100 Subject: [PATCH 476/597] Update setup-java and checkout actions to v4 (#3804) --- .github/workflows/java-ea.yml | 2 +- .github/workflows/main.yml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index fce2dc06b2..fc3a4a0fb1 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: 'Set up JDK' uses: oracle-actions/setup-java@v1 with: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9cc098161d..a760175c0e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,9 +17,9 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: 'Set up JDK' - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.java }} @@ -43,16 +43,16 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: 'Set up JDK 17 for building everything' - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 17 - name: 'Install Processor' run: ./mvnw ${MAVEN_ARGS} -DskipTests install -pl processor -am - name: 'Set up JDK ${{ matrix.java }} for running integration tests' - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.java }} @@ -62,9 +62,9 @@ jobs: name: 'Windows' runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: 'Set up JDK 17' - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 17 @@ -76,7 +76,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: 'Set up JDK 17' - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 17 From 39551242d7cfbab7e6bbadfed8c771b483cc1a13 Mon Sep 17 00:00:00 2001 From: Zegveld <41897697+Zegveld@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:41:35 +0100 Subject: [PATCH 477/597] #3786: Improve error message when mapping non-iterable to array --- .../processor/MethodRetrievalProcessor.java | 5 +++ .../mapstruct/ap/internal/util/Message.java | 1 + .../bugs/_3786/ErroneousByteArrayMapper.java | 13 +++++++ .../ap/test/bugs/_3786/Issue3786Test.java | 36 +++++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3786/ErroneousByteArrayMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3786/Issue3786Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 2c8ad5dcb9..c8c13e6bf9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -555,6 +555,11 @@ private boolean checkParameterAndReturnType(ExecutableElement method, List Date: Sat, 10 May 2025 14:12:20 +0200 Subject: [PATCH 478/597] #3815: chore(docs): Improved wording about @Condition usage --- .../main/asciidoc/chapter-10-advanced-mapping-options.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 1e2bd133d4..b5c116ffd3 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -308,7 +308,7 @@ Conditional mapping can also be used to check if a source parameter should be ma A custom condition method for properties is a method that is annotated with `org.mapstruct.Condition` and returns `boolean`. A custom condition method for source parameters is annotated with `org.mapstruct.SourceParameterCondition`, `org.mapstruct.Condition(appliesTo = org.mapstruct.ConditionStrategy#SOURCE_PARAMETERS)` or meta-annotated with `Condition(appliesTo = ConditionStrategy#SOURCE_PARAMETERS)` -e.g. if you only want to map a String property when it is not `null`, and it is not empty then you can do something like: +e.g. if you only want to map a String property when it is not `null` and not empty then you can do something like: .Mapper using custom condition check method ==== From 668eeb5de1b1dc79cbf90e68cdf38fbba03c98a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:25:00 +0000 Subject: [PATCH 479/597] Bump com.google.protobuf:protobuf-java from 3.21.7 to 3.25.5 in /parent Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 3.21.7 to 3.25.5. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.21.7...v3.25.5) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 5509e783d2..5735d4760c 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -50,7 +50,7 @@ The processor module needs at least Java 17. --> 1.8 - 3.21.7 + 3.25.5 2.3.2 From 2c84d04463a3ec2edd6c05e726a579b4ba936847 Mon Sep 17 00:00:00 2001 From: Cause Chung Date: Sun, 11 May 2025 17:33:35 +0200 Subject: [PATCH 480/597] #3240 Add Support for Java21 SequencedSet and SequencedMap --- .github/workflows/main.yml | 24 +++--- .github/workflows/release.yml | 2 +- .../chapter-6-mapping-collections.asciidoc | 4 + ...eatureCompilationExclusionCliEnhancer.java | 7 ++ .../test/resources/fullFeatureTest/pom.xml | 2 + parent/pom.xml | 2 +- processor/pom.xml | 2 +- .../ap/internal/model/common/TypeFactory.java | 13 ++++ .../util/JavaCollectionConstants.java | 21 ++++++ ...dCollectionsDefaultImplementationTest.java | 74 +++++++++++++++++++ .../jdk21/SequencedCollectionsMapper.java | 26 +++++++ readme.md | 3 +- 12 files changed, 164 insertions(+), 16 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/JavaCollectionConstants.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/jdk21/SequencedCollectionsDefaultImplementationTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/jdk21/SequencedCollectionsMapper.java diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a760175c0e..166466348d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - java: [17, 21] + java: [21] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: @@ -24,31 +24,31 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java }} - name: 'Test' - run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=${{ matrix.java != 17 }} install -DskipDistribution=${{ matrix.java != 17 }} + run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=${{ matrix.java != 21 }} install -DskipDistribution=${{ matrix.java != 21 }} - name: 'Generate coverage report' - if: matrix.java == 17 + if: matrix.java == 21 run: ./mvnw jacoco:report - name: 'Upload coverage to Codecov' - if: matrix.java == 17 + if: matrix.java == 21 uses: codecov/codecov-action@v2 - name: 'Publish Snapshots' - if: matrix.java == 17 && github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mapstruct/mapstruct' + if: matrix.java == 21 && github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mapstruct/mapstruct' run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy integration_test_jdk: strategy: fail-fast: false matrix: - java: [ 8, 11 ] + java: [ 8, 11, 17 ] name: 'Linux JDK ${{ matrix.java }}' runs-on: ubuntu-latest steps: - name: 'Checkout' uses: actions/checkout@v4 - - name: 'Set up JDK 17 for building everything' + - name: 'Set up JDK 21 for building everything' uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: 17 + java-version: 21 - name: 'Install Processor' run: ./mvnw ${MAVEN_ARGS} -DskipTests install -pl processor -am - name: 'Set up JDK ${{ matrix.java }} for running integration tests' @@ -63,11 +63,11 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v4 - - name: 'Set up JDK 17' + - name: 'Set up JDK 21' uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: 17 + java-version: 21 - name: 'Test' run: ./mvnw %MAVEN_ARGS% install mac: @@ -75,10 +75,10 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v3 - - name: 'Set up JDK 17' + - name: 'Set up JDK 21' uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: 17 + java-version: 21 - name: 'Test' run: ./mvnw ${MAVEN_ARGS} install diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 682fa36876..61f347af3c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: 'zulu' cache: maven diff --git a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc index 79d4544f9b..4510c82cc0 100644 --- a/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc +++ b/documentation/src/main/asciidoc/chapter-6-mapping-collections.asciidoc @@ -212,12 +212,16 @@ When an iterable or map mapping method declares an interface type as return type |`Set`|`LinkedHashSet` +|`SequencedSet`|`LinkedHashSet` + |`SortedSet`|`TreeSet` |`NavigableSet`|`TreeSet` |`Map`|`LinkedHashMap` +|`SequencedMap`|`LinkedHashMap` + |`SortedMap`|`TreeMap` |`NavigableMap`|`TreeMap` diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java index 77a5e2af06..0cc1a87818 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -34,6 +34,7 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/annotatewith/deprecated/jdk11/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" ); if ( processorType == ProcessorTest.ProcessorType.ECLIPSE_JDT ) { additionalExcludes.add( "org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java" ); @@ -42,9 +43,15 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces case JAVA_9: // TODO find out why this fails: additionalExcludes.add( "org/mapstruct/ap/test/collection/wildcard/BeanMapper.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" ); break; case JAVA_11: additionalExcludes.add( "org/mapstruct/ap/test/**/spring/**/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" ); + break; + case JAVA_17: + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" ); + break; default: } diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index 849f7150fc..b5ddae9a79 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -59,6 +59,8 @@ ${additionalExclude9} ${additionalExclude10} ${additionalExclude11} + ${additionalExclude12} + ${additionalExclude13} diff --git a/parent/pom.xml b/parent/pom.xml index 5735d4760c..f6ad60a22a 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -47,7 +47,7 @@ jdt_apt 1.8 3.25.5 diff --git a/processor/pom.xml b/processor/pom.xml index 13deafb413..2341c4ddb0 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -24,7 +24,7 @@ - 17 + 21 diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index ceb190a332..c2646f63db 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -45,6 +45,7 @@ import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.Extractor; import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.JavaCollectionConstants; import org.mapstruct.ap.internal.util.JavaStreamConstants; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.NativeTypes; @@ -149,6 +150,18 @@ public TypeFactory(ElementUtils elementUtils, TypeUtils typeUtils, FormattingMes ConcurrentNavigableMap.class.getName(), withDefaultConstructor( getType( ConcurrentSkipListMap.class ) ) ); + implementationTypes.put( + JavaCollectionConstants.SEQUENCED_SET_FQN, + sourceVersionAtLeast19 ? + withFactoryMethod( getType( LinkedHashSet.class ), LINKED_HASH_SET_FACTORY_METHOD_NAME ) : + withLoadFactorAdjustment( getType( LinkedHashSet.class ) ) + ); + implementationTypes.put( + JavaCollectionConstants.SEQUENCED_MAP_FQN, + sourceVersionAtLeast19 ? + withFactoryMethod( getType( LinkedHashMap.class ), LINKED_HASH_MAP_FACTORY_METHOD_NAME ) : + withLoadFactorAdjustment( getType( LinkedHashMap.class ) ) + ); this.loggingVerbose = loggingVerbose; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JavaCollectionConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JavaCollectionConstants.java new file mode 100644 index 0000000000..68d556c542 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JavaCollectionConstants.java @@ -0,0 +1,21 @@ +/* + * 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.internal.util; + +/** + * Helper holding Java collections full qualified class names for conversion registration, + * to achieve Java compatibility. + * + * @author Cause Chung + */ +public final class JavaCollectionConstants { + public static final String SEQUENCED_MAP_FQN = "java.util.SequencedMap"; + public static final String SEQUENCED_SET_FQN = "java.util.SequencedSet"; + + private JavaCollectionConstants() { + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/jdk21/SequencedCollectionsDefaultImplementationTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/jdk21/SequencedCollectionsDefaultImplementationTest.java new file mode 100644 index 0000000000..4551d5b0b3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/jdk21/SequencedCollectionsDefaultImplementationTest.java @@ -0,0 +1,74 @@ +/* + * 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.collection.defaultimplementation.jdk21; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SequencedMap; +import java.util.SequencedSet; + +import org.mapstruct.ap.test.collection.defaultimplementation.Source; +import org.mapstruct.ap.test.collection.defaultimplementation.SourceFoo; +import org.mapstruct.ap.test.collection.defaultimplementation.Target; +import org.mapstruct.ap.test.collection.defaultimplementation.TargetFoo; +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; +import static org.assertj.core.api.Assertions.entry; + +@WithClasses({ + Source.class, + Target.class, + SourceFoo.class, + TargetFoo.class, + SequencedCollectionsMapper.class +}) +@IssueKey("3420") +class SequencedCollectionsDefaultImplementationTest { + + @ProcessorTest + public void shouldUseDefaultImplementationForSequencedMap() { + SequencedMap target = + SequencedCollectionsMapper.INSTANCE.sourceFooMapToTargetFooSequencedMap( createSourceFooMap() ); + + assertResultMap( target ); + } + + @ProcessorTest + public void shouldUseDefaultImplementationForSequencedSet() { + SequencedSet target = + SequencedCollectionsMapper.INSTANCE.sourceFoosToTargetFooSequencedSet( createSourceFooList() ); + + assertResultList( target ); + } + + private void assertResultList(Iterable fooIterable) { + assertThat( fooIterable ).isNotNull(); + assertThat( fooIterable ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) ); + } + + private void assertResultMap(Map result) { + assertThat( result ).isNotNull(); + assertThat( result ).hasSize( 2 ); + assertThat( result ).contains( entry( "1", new TargetFoo( "Bob" ) ), entry( "2", new TargetFoo( "Alice" ) ) ); + } + + private Map createSourceFooMap() { + Map map = new HashMap<>(); + map.put( 1L, new SourceFoo( "Bob" ) ); + map.put( 2L, new SourceFoo( "Alice" ) ); + + return map; + } + + private List createSourceFooList() { + return Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/jdk21/SequencedCollectionsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/jdk21/SequencedCollectionsMapper.java new file mode 100644 index 0000000000..bbffb56b0c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/jdk21/SequencedCollectionsMapper.java @@ -0,0 +1,26 @@ +/* + * 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.collection.defaultimplementation.jdk21; + +import java.util.Collection; +import java.util.Map; +import java.util.SequencedMap; +import java.util.SequencedSet; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.collection.defaultimplementation.SourceFoo; +import org.mapstruct.ap.test.collection.defaultimplementation.TargetFoo; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SequencedCollectionsMapper { + + SequencedCollectionsMapper INSTANCE = Mappers.getMapper( SequencedCollectionsMapper.class ); + + SequencedSet sourceFoosToTargetFooSequencedSet(Collection foos); + + SequencedMap sourceFooMapToTargetFooSequencedMap(Map foos); +} diff --git a/readme.md b/readme.md index 907c8411cd..16a70f3f0b 100644 --- a/readme.md +++ b/readme.md @@ -130,7 +130,8 @@ To learn more about MapStruct, refer to the [project homepage](https://mapstruct ## Building from Source -MapStruct uses Maven for its build. Java 17 is required for building MapStruct from source. To build the complete project, run +MapStruct uses Maven for its build. Java 21 is required for building MapStruct from source. +To build the complete project, run ./mvnw clean install From 602e29083fe980be0256fd01d37906ceab1ff6ca Mon Sep 17 00:00:00 2001 From: zral <73640367+zyberzebra@users.noreply.github.com> Date: Sun, 11 May 2025 19:59:59 +0200 Subject: [PATCH 481/597] #1140 Add warning when target has no properties --- .../ap/internal/model/BeanMappingMethod.java | 78 ++++++++++++------- .../mapstruct/ap/internal/util/Message.java | 1 + .../test/annotatewith/AnnotateWithTest.java | 5 +- .../ErroneousMapperWithClassOnMethod.java | 10 +-- .../ap/test/bugs/_1111/Issue1111Mapper.java | 25 +++++- .../ap/test/bugs/_1111/Issue1111Test.java | 2 +- .../ap/test/bugs/_1130/Issue1130Mapper.java | 19 +++++ ...roneousIssue1242MapperMultipleSources.java | 3 +- .../ap/test/bugs/_1242/Issue1242Mapper.java | 3 +- .../ap/test/bugs/_1242/Issue1242Test.java | 2 +- .../mapstruct/ap/test/bugs/_1242/TargetB.java | 9 +++ .../ap/test/bugs/_1345/Issue1345Mapper.java | 18 +++++ .../ap/test/bugs/_1460/Issue1460Mapper.java | 21 ++++- .../_1460/java8/Issue1460JavaTimeMapper.java | 11 ++- .../mapstruct/ap/test/bugs/_1590/Book.java | 10 +++ .../ap/test/bugs/_1590/Issue1590Test.java | 2 +- .../org/mapstruct/ap/test/bugs/_1650/C.java | 10 +++ .../mapstruct/ap/test/bugs/_1650/CPrime.java | 10 +++ .../ap/test/bugs/_1650/Issue1650Test.java | 2 +- .../ap/test/bugs/_1821/Issue1821Mapper.java | 20 +++++ .../ap/test/bugs/_1821/Issue1821Test.java | 2 +- .../ap/test/bugs/_611/Issue611Test.java | 4 +- .../ap/test/bugs/_611/SomeClass.java | 40 ++++++++++ .../ap/test/bugs/_611/SomeOtherClass.java | 20 +++++ .../simple/BuilderInfoTargetTest.java | 16 +++- .../simple/SimpleBuilderMapper.java | 3 - .../simple/SimpleImmutableUpdateMapper.java | 19 +++++ .../immutabletarget/ImmutableProductTest.java | 10 +++ .../ap/test/collection/wildcard/Idea.java | 9 +++ .../ap/test/collection/wildcard/Plan.java | 9 +++ .../visibility/ConstructorVisibilityTest.java | 12 +++ .../AbstractDestinationClassNameMapper.java | 2 +- .../AbstractDestinationPackageNameMapper.java | 2 +- .../DestinationClassNameMapper.java | 2 +- .../DestinationClassNameMapperDecorated.java | 2 +- .../DestinationClassNameMapperDecorator.java | 4 +- .../DestinationClassNameMapperWithConfig.java | 2 +- ...tionClassNameMapperWithConfigOverride.java | 2 +- .../destination/DestinationClassNameTest.java | 1 + .../DestinationClassNameWithJsr330Mapper.java | 2 +- .../DestinationPackageNameMapper.java | 2 +- ...DestinationPackageNameMapperDecorated.java | 2 +- ...DestinationPackageNameMapperDecorator.java | 4 +- ...estinationPackageNameMapperWithConfig.java | 2 +- ...onPackageNameMapperWithConfigOverride.java | 2 +- ...estinationPackageNameMapperWithSuffix.java | 2 +- .../DestinationPackageNameTest.java | 1 + .../mapstruct/ap/test/destination/Target.java | 19 +++++ .../ap/test/emptytarget/EmptyTarget.java | 9 +++ .../test/emptytarget/EmptyTargetMapper.java | 18 +++++ .../ap/test/emptytarget/EmptyTargetTest.java | 39 ++++++++++ .../mapstruct/ap/test/emptytarget/Source.java | 36 +++++++++ .../mapstruct/ap/test/emptytarget/Target.java | 34 ++++++++ .../test/emptytarget/TargetWithNoSetters.java | 15 ++++ .../ap/test/imports/SourceTargetMapper.java | 10 ++- .../ap/test/java8stream/wildcard/Idea.java | 9 +++ .../ap/test/java8stream/wildcard/Plan.java | 9 +++ .../ErroneousSourceTargetMapper.java | 16 +++- .../MissingIgnoredSourceTest.java | 2 +- .../objectfactory/ObjectFactoryMapper.java | 13 +++- .../fixture/AbstractParentSource.java | 9 +++ .../fixture/AbstractParentTarget.java | 9 +++ .../fixture/ImplementedParentSource.java | 9 +++ .../fixture/ImplementedParentTarget.java | 9 +++ .../ap/test/versioninfo/SimpleMapper.java | 3 +- .../SuppressTimestampViaMapper.java | 3 +- .../SuppressTimestampViaMapperConfig.java | 3 +- .../ap/test/versioninfo/VersionInfoTest.java | 3 +- .../fixture/SubclassAbstractMapperImpl.java | 13 ++++ .../SubclassImplementedMapperImpl.java | 8 ++ .../fixture/SubclassInterfaceMapperImpl.java | 5 ++ 71 files changed, 657 insertions(+), 85 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/SimpleImmutableUpdateMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/destination/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTarget.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTargetTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/emptytarget/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/emptytarget/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/emptytarget/TargetWithNoSetters.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 4ff2e8079f..08c8ceda59 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -235,9 +235,10 @@ else if ( !method.isUpdateMethod() ) { this.unprocessedTargetProperties = new LinkedHashMap<>( accessors ); + boolean constructorAccessorHadError = false; if ( !method.isUpdateMethod() && !hasFactoryMethod ) { ConstructorAccessor constructorAccessor = getConstructorAccessor( resultTypeToMap ); - if ( constructorAccessor != null ) { + if ( constructorAccessor != null && !constructorAccessor.hasError ) { this.unprocessedConstructorProperties = constructorAccessor.constructorAccessors; @@ -250,8 +251,10 @@ else if ( !method.isUpdateMethod() ) { else { this.unprocessedConstructorProperties = new LinkedHashMap<>(); } + constructorAccessorHadError = constructorAccessor != null && constructorAccessor.hasError; this.targetProperties.addAll( this.unprocessedConstructorProperties.keySet() ); + this.unprocessedTargetProperties.putAll( this.unprocessedConstructorProperties ); } else { @@ -322,7 +325,7 @@ else if ( !method.isUpdateMethod() ) { // report errors on unmapped properties if ( shouldHandledDefinedMappings ) { - reportErrorForUnmappedTargetPropertiesIfRequired(); + reportErrorForUnmappedTargetPropertiesIfRequired( resultTypeToMap, constructorAccessorHadError ); reportErrorForUnmappedSourcePropertiesIfRequired(); } reportErrorForMissingIgnoredSourceProperties(); @@ -964,7 +967,7 @@ private ConstructorAccessor getConstructorAccessor(Type type) { ) .collect( Collectors.joining( ", " ) ) ); - return null; + return new ConstructorAccessor( true, Collections.emptyList(), Collections.emptyMap() ); } else { return getConstructorAccessor( type, accessibleConstructors.get( 0 ) ); @@ -1023,7 +1026,7 @@ else if ( constructorProperties.size() != constructorParameters.size() ) { GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS, type ); - return null; + return new ConstructorAccessor( true, Collections.emptyList(), Collections.emptyMap() ); } else { Map constructorAccessors = new LinkedHashMap<>(); @@ -1731,36 +1734,45 @@ private ReportingPolicyGem getUnmappedTargetPolicy() { return method.getOptions().getMapper().unmappedTargetPolicy(); } - private void reportErrorForUnmappedTargetPropertiesIfRequired() { + private void reportErrorForUnmappedTargetPropertiesIfRequired(Type resultType, + boolean constructorAccessorHadError) { // fetch settings from element to implement ReportingPolicyGem unmappedTargetPolicy = getUnmappedTargetPolicy(); - if ( method instanceof ForgedMethod && targetProperties.isEmpty() ) { - //TODO until we solve 1140 we report this error when the target properties are empty - ForgedMethod forgedMethod = (ForgedMethod) method; - if ( forgedMethod.getHistory() == null ) { - Type sourceType = this.method.getParameters().get( 0 ).getType(); - Type targetType = this.method.getReturnType(); - ctx.getMessager().printMessage( - this.method.getExecutable(), - Message.PROPERTYMAPPING_FORGED_MAPPING_NOT_FOUND, - sourceType.describe(), - targetType.describe(), - targetType.describe(), - sourceType.describe() - ); + if ( targetProperties.isEmpty() ) { + if ( method instanceof ForgedMethod ) { + ForgedMethod forgedMethod = (ForgedMethod) method; + if ( forgedMethod.getHistory() == null ) { + Type sourceType = this.method.getParameters().get( 0 ).getType(); + Type targetType = this.method.getReturnType(); + ctx.getMessager().printMessage( + this.method.getExecutable(), + Message.PROPERTYMAPPING_FORGED_MAPPING_NOT_FOUND, + sourceType.describe(), + targetType.describe(), + targetType.describe(), + sourceType.describe() + ); + } + else { + ForgedMethodHistory history = forgedMethod.getHistory(); + ctx.getMessager().printMessage( + this.method.getExecutable(), + Message.PROPERTYMAPPING_FORGED_MAPPING_WITH_HISTORY_NOT_FOUND, + history.createSourcePropertyErrorMessage(), + history.getTargetType().describe(), + history.createTargetPropertyName(), + history.getTargetType().describe(), + history.getSourceType().describe() + ); + } } - else { - ForgedMethodHistory history = forgedMethod.getHistory(); + else if ( !constructorAccessorHadError ) { ctx.getMessager().printMessage( - this.method.getExecutable(), - Message.PROPERTYMAPPING_FORGED_MAPPING_WITH_HISTORY_NOT_FOUND, - history.createSourcePropertyErrorMessage(), - history.getTargetType().describe(), - history.createTargetPropertyName(), - history.getTargetType().describe(), - history.getSourceType().describe() + method.getExecutable(), + Message.PROPERTYMAPPING_TARGET_HAS_NO_TARGET_PROPERTIES, + resultType.describe() ); } } @@ -1780,7 +1792,8 @@ else if ( !unprocessedTargetProperties.isEmpty() && unmappedTargetPolicy.require reportErrorForUnmappedProperties( unprocessedTargetProperties, unmappedPropertiesMsg, - unmappedForgedPropertiesMsg ); + unmappedForgedPropertiesMsg + ); } } @@ -1907,12 +1920,19 @@ private void reportErrorForUnusedSourceParameters() { } private static class ConstructorAccessor { + private final boolean hasError; private final List parameterBindings; private final Map constructorAccessors; private ConstructorAccessor( List parameterBindings, Map constructorAccessors) { + this( false, parameterBindings, constructorAccessors ); + } + + private ConstructorAccessor(boolean hasError, List parameterBindings, + Map constructorAccessors) { + this.hasError = hasError; this.parameterBindings = parameterBindings; this.constructorAccessors = constructorAccessors; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 221aed2859..883b3e3792 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -92,6 +92,7 @@ public enum Message { PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET("No property named \"%s\" exists in source parameter(s). Please define the source explicitly."), PROPERTYMAPPING_NO_SUITABLE_COLLECTION_OR_MAP_CONSTRUCTOR( "%s does not have an accessible copy or no-args constructor." ), PROPERTYMAPPING_EXPRESSION_AND_CONDITION_QUALIFIED_BY_NAME_BOTH_DEFINED( "Expression and condition qualified by name are both defined in @Mapping, either define an expression or a condition qualified by name." ), + PROPERTYMAPPING_TARGET_HAS_NO_TARGET_PROPERTIES( "No target property found for target \"%s\".", Diagnostic.Kind.WARNING ), CONVERSION_LOSSY_WARNING( "%s has a possibly lossy conversion from %s to %s.", Diagnostic.Kind.WARNING ), CONVERSION_LOSSY_ERROR( "Can't map %s. It has a possibly lossy conversion from %s to %s." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java index 3687fe2bc0..eaa8832df7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/AnnotateWithTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.Mapper; +import org.mapstruct.ap.test.WithProperties; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -176,12 +177,12 @@ public void erroneousMapperWithAnnotationOnlyOnClass() { @Diagnostic( kind = javax.tools.Diagnostic.Kind.ERROR, type = ErroneousMapperWithClassOnMethod.class, - line = 17, + line = 18, message = "Annotation \"CustomClassOnlyAnnotation\" is not allowed on methods." ) } ) - @WithClasses({ ErroneousMapperWithClassOnMethod.class, CustomClassOnlyAnnotation.class }) + @WithClasses({ ErroneousMapperWithClassOnMethod.class, CustomClassOnlyAnnotation.class, WithProperties.class }) public void erroneousMapperWithClassOnMethod() { } diff --git a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithClassOnMethod.java b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithClassOnMethod.java index d3d53c910a..c34bce304f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithClassOnMethod.java +++ b/processor/src/test/java/org/mapstruct/ap/test/annotatewith/ErroneousMapperWithClassOnMethod.java @@ -7,6 +7,7 @@ import org.mapstruct.AnnotateWith; import org.mapstruct.Mapper; +import org.mapstruct.ap.test.WithProperties; /** * @author Ben Zegveld @@ -15,13 +16,6 @@ public interface ErroneousMapperWithClassOnMethod { @AnnotateWith( value = CustomClassOnlyAnnotation.class ) - Target toString(Source value); + WithProperties toString(String string); - class Source { - - } - - class Target { - - } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Mapper.java index a5a624e684..7cdfa82316 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Mapper.java @@ -24,8 +24,29 @@ public interface Issue1111Mapper { List> listList(List> in); - class Source { } + class Source { + private final String value; - class Target { } + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Test.java index 5619b64175..f5efb21e3a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1111/Issue1111Test.java @@ -27,7 +27,7 @@ public class Issue1111Test { @ProcessorTest public void shouldCompile() { - List> source = Arrays.asList( Arrays.asList( new Source() ) ); + List> source = Arrays.asList( Arrays.asList( new Source( "test" ) ) ); List> target = Issue1111Mapper.INSTANCE.listList( source ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1130/Issue1130Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1130/Issue1130Mapper.java index 042d5a2f8b..350d95eebc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1130/Issue1130Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1130/Issue1130Mapper.java @@ -30,6 +30,16 @@ public void setB(BEntity b) { } static class BEntity { + + private String id; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } } static class ADto { @@ -46,6 +56,7 @@ public void setB(BDto b) { class BDto { private final String passedViaConstructor; + private String id; BDto(String passedViaConstructor) { this.passedViaConstructor = passedViaConstructor; @@ -54,6 +65,14 @@ class BDto { String getPassedViaConstructor() { return passedViaConstructor; } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } } abstract void mergeA(AEntity source, @MappingTarget ADto target); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/ErroneousIssue1242MapperMultipleSources.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/ErroneousIssue1242MapperMultipleSources.java index 7259d343b8..7a8ad05542 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/ErroneousIssue1242MapperMultipleSources.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/ErroneousIssue1242MapperMultipleSources.java @@ -7,13 +7,14 @@ import org.mapstruct.Mapper; import org.mapstruct.ObjectFactory; +import org.mapstruct.ReportingPolicy; /** * Results in an ambiguous factory method error, as there are two methods with matching source types available. * * @author Andreas Gudian */ -@Mapper(uses = TargetFactories.class) +@Mapper(uses = TargetFactories.class, unmappedTargetPolicy = ReportingPolicy.IGNORE) public abstract class ErroneousIssue1242MapperMultipleSources { abstract TargetA toTargetA(SourceA source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Mapper.java index 0491b5ffe0..90ea60a2ee 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Mapper.java @@ -7,13 +7,14 @@ import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; +import org.mapstruct.ReportingPolicy; /** * Test mapper for properly resolving the best fitting factory method * * @author Andreas Gudian */ -@Mapper(uses = TargetFactories.class) +@Mapper(uses = TargetFactories.class, unmappedTargetPolicy = ReportingPolicy.IGNORE) public abstract class Issue1242Mapper { abstract TargetA toTargetA(SourceA source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java index f27051670c..036a63f312 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/Issue1242Test.java @@ -54,7 +54,7 @@ public void factoryMethodWithSourceParamIsChosen() { diagnostics = { @Diagnostic(type = ErroneousIssue1242MapperMultipleSources.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 20, + line = 21, message = "Ambiguous factory methods found for creating TargetB: " + "TargetB anotherTargetBCreator(SourceB source), " + "TargetB TargetFactories.createTargetB(SourceB source, @TargetType Class clazz), " + diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/TargetB.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/TargetB.java index ab4f639697..6a28adc828 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/TargetB.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1242/TargetB.java @@ -9,6 +9,7 @@ * @author Andreas Gudian */ class TargetB { + protected String value; private final String passedViaConstructor; TargetB(String passedViaConstructor) { @@ -18,4 +19,12 @@ class TargetB { String getPassedViaConstructor() { return passedViaConstructor; } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1345/Issue1345Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1345/Issue1345Mapper.java index 7699a8f7e9..b2d89c96e6 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1345/Issue1345Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1345/Issue1345Mapper.java @@ -27,8 +27,17 @@ public interface Issue1345Mapper { class A { + private String value; private String readOnlyProperty; + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + public String getReadOnlyProperty() { return readOnlyProperty; } @@ -36,8 +45,17 @@ public String getReadOnlyProperty() { class B { + private String value; private String property; + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + public String getProperty() { return property; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Mapper.java index 90ed83d28a..1d0d4fccbb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/Issue1460Mapper.java @@ -18,13 +18,13 @@ public abstract class Issue1460Mapper { public abstract Target map(Source source); - public abstract String forceUsageOfIssue1460Enum(Issue1460Enum source); + public abstract Value forceUsageOfIssue1460Enum(Issue1460Enum source); - public abstract String forceUsageOfLocale(Locale source); + public abstract Value forceUsageOfLocale(Locale source); - public abstract String forceUsageOfLocalDate(LocalDate source); + public abstract Value forceUsageOfLocalDate(LocalDate source); - public abstract String forceUsageOfDateTime(DateTime source); + public abstract Value forceUsageOfDateTime(DateTime source); public static class Issue1460Enum { } @@ -37,4 +37,17 @@ public static class LocalDate { public static class DateTime { } + + public static class Value { + + private final T source; + + public Value(T source) { + this.source = source; + } + + public T getSource() { + return source; + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/java8/Issue1460JavaTimeMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/java8/Issue1460JavaTimeMapper.java index 216cda21fd..b9bedfe451 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/java8/Issue1460JavaTimeMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1460/java8/Issue1460JavaTimeMapper.java @@ -18,8 +18,17 @@ public abstract class Issue1460JavaTimeMapper { public abstract Target map(Source source); - public abstract String forceUsageOfLocalDate(LocalDate source); + public abstract LocalTarget forceUsageOfLocalDate(LocalDate source); public static class LocalDate { } + + public static class LocalTarget { + + private final LocalDate source; + + public LocalTarget(LocalDate source) { + this.source = source; + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Book.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Book.java index 53355ee603..bfe79c1772 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Book.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Book.java @@ -6,4 +6,14 @@ package org.mapstruct.ap.test.bugs._1590; public class Book { + + private final String name; + + public Book(String name) { + this.name = name; + } + + public String getName() { + return name; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Issue1590Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Issue1590Test.java index d77add25de..0a9113797c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Issue1590Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1590/Issue1590Test.java @@ -28,7 +28,7 @@ public class Issue1590Test { @ProcessorTest public void shouldTestMappingLocalDates() { BookShelf source = new BookShelf(); - source.setBooks( Arrays.asList( new Book() ) ); + source.setBooks( Arrays.asList( new Book("Test") ) ); BookShelf target = BookShelfMapper.INSTANCE.map( source ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/C.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/C.java index 705f2e2d69..dbbd74ec95 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/C.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/C.java @@ -6,4 +6,14 @@ package org.mapstruct.ap.test.bugs._1650; public class C { + + private final int value; + + public C(int value) { + this.value = value; + } + + public int getValue() { + return value; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/CPrime.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/CPrime.java index 24e4376f88..db504189f8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/CPrime.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/CPrime.java @@ -6,4 +6,14 @@ package org.mapstruct.ap.test.bugs._1650; public class CPrime { + + private int value; + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/Issue1650Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/Issue1650Test.java index 2d1437eb77..33237e726c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/Issue1650Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1650/Issue1650Test.java @@ -27,7 +27,7 @@ public void shouldCompile() { A a = new A(); a.setB( new B() ); - a.getB().setC( new C() ); + a.getB().setC( new C( 10 ) ); APrime aPrime = new APrime(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Mapper.java index 08eccae32e..406fa7b71b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Mapper.java @@ -22,11 +22,31 @@ public interface Issue1821Mapper { ExtendedTarget mapExtended(Source source); class Target { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } } class ExtendedTarget extends Target { } class Source { + + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Test.java index 4481ed43e2..865ef8f6c1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1821/Issue1821Test.java @@ -15,7 +15,7 @@ public class Issue1821Test { @ProcessorTest public void shouldNotGiveNullPtr() { - Issue1821Mapper.INSTANCE.map( new Issue1821Mapper.Source() ); + Issue1821Mapper.INSTANCE.map( new Issue1821Mapper.Source( "test" ) ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java index 79a6e045ec..b77da90ad9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java @@ -43,8 +43,8 @@ public void mapperNestedInsideNestedClassIsFound() { */ @ProcessorTest public void rightMapperIsFound() { - SomeClass.InnerMapper.Source source1 = new SomeClass.InnerMapper.Source(); - SomeOtherClass.InnerMapper.Source source2 = new SomeOtherClass.InnerMapper.Source(); + SomeClass.InnerMapper.Source source1 = new SomeClass.InnerMapper.Source( "test" ); + SomeOtherClass.InnerMapper.Source source2 = new SomeOtherClass.InnerMapper.Source( "test2" ); SomeClass.InnerMapper.Target target1 = SomeClass.InnerMapper.INSTANCE.toTarget( source1 ); SomeOtherClass.InnerMapper.Target target2 = SomeOtherClass.InnerMapper.INSTANCE.toTarget( source2 ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeClass.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeClass.java index 0d41128711..e0fb59d4d7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeClass.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeClass.java @@ -19,9 +19,29 @@ public interface InnerMapper { Target toTarget(Source in); class Source { + + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } } class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } } } @@ -33,9 +53,29 @@ public interface InnerMapper { Target toTarget(Source in); class Source { + + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } } class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } } } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeOtherClass.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeOtherClass.java index e50187a82a..2ff2d0d831 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeOtherClass.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeOtherClass.java @@ -19,9 +19,29 @@ public interface InnerMapper { Target toTarget(Source in); class Source { + + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } } class Target { + + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } } } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java index 4f3d8b85f4..5d0ff919ec 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/BuilderInfoTargetTest.java @@ -9,6 +9,9 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.runner.GeneratedSource; import static org.assertj.core.api.Assertions.assertThat; @@ -80,6 +83,17 @@ public void testUpdateMutableWithBuilder() { } @ProcessorTest + @WithClasses( { + SimpleImmutableUpdateMapper.class + } ) + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = SimpleImmutableUpdateMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 18, + message = "No target property found for target \"SimpleImmutableTarget\"."), + }) + public void updatingTargetWithNoSettersShouldNotFail() { SimpleMutableSource source = new SimpleMutableSource(); @@ -90,7 +104,7 @@ public void updatingTargetWithNoSettersShouldNotFail() { .build(); assertThat( target.getAge() ).isEqualTo( 20 ); - SimpleBuilderMapper.INSTANCE.toImmutable( source, target ); + SimpleImmutableUpdateMapper.INSTANCE.toImmutable( source, target ); assertThat( target.getAge() ).isEqualTo( 20 ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/SimpleBuilderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/SimpleBuilderMapper.java index 9f7187c634..6185daee97 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/SimpleBuilderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/SimpleBuilderMapper.java @@ -21,9 +21,6 @@ public interface SimpleBuilderMapper { @Mapping(target = "builder.name", source = "source.fullName") void updateImmutable(SimpleMutableSource source, @MappingTarget SimpleImmutableTarget.Builder builder); - // This method is fine as if the mapping target has setters it would use them, otherwise it won't - void toImmutable(SimpleMutableSource source, @MappingTarget SimpleImmutableTarget target); - @Mapping(target = "name", source = "fullName") SimpleImmutableTarget toImmutable(SimpleMutableSource source); diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/SimpleImmutableUpdateMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/SimpleImmutableUpdateMapper.java new file mode 100644 index 0000000000..c6eccc96c8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/mappingTarget/simple/SimpleImmutableUpdateMapper.java @@ -0,0 +1,19 @@ +/* + * 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.builder.mappingTarget.simple; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SimpleImmutableUpdateMapper { + + SimpleImmutableUpdateMapper INSTANCE = Mappers.getMapper( SimpleImmutableUpdateMapper.class ); + + // This method is fine as if the mapping target has setters it would use them, otherwise it won't + void toImmutable(SimpleMutableSource source, @MappingTarget SimpleImmutableTarget target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java index de22436296..8097dfdfe9 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/immutabletarget/ImmutableProductTest.java @@ -11,6 +11,9 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import static org.assertj.core.api.Assertions.assertThat; @@ -41,6 +44,13 @@ public void shouldHandleImmutableTarget() { CupboardNoSetterMapper.class, CupboardEntityOnlyGetter.class }) + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = CupboardNoSetterMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 22, + message = "No target property found for target \"CupboardEntityOnlyGetter\"."), + }) public void shouldIgnoreImmutableTarget() { CupboardDto in = new CupboardDto(); in.setContent( Arrays.asList( "flour", "peas" ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/Idea.java b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/Idea.java index fc9b89b571..4e21b223db 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/Idea.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/Idea.java @@ -11,4 +11,13 @@ */ public class Idea { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/Plan.java b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/Plan.java index 29fcfc2f3a..00db02170c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/Plan.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/wildcard/Plan.java @@ -11,4 +11,13 @@ */ public class Plan { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java index b7a254296d..5eebaf5d05 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/constructor/visibility/ConstructorVisibilityTest.java @@ -10,6 +10,9 @@ import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import static org.assertj.core.api.Assertions.assertThat; @@ -44,6 +47,15 @@ public void shouldUseSinglePublicConstructorAlways() { @WithClasses({ SimpleWithPublicParameterlessConstructorMapper.class }) + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = SimpleWithPublicParameterlessConstructorMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 21, + message = "No target property found for target " + + "\"SimpleWithPublicParameterlessConstructorMapper.Person\"."), + }) + public void shouldUsePublicParameterConstructorIfPresent() { PersonDto source = new PersonDto(); source.setName( "Bob" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/AbstractDestinationClassNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/destination/AbstractDestinationClassNameMapper.java index 9498b832f7..8dd010cae3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/AbstractDestinationClassNameMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/AbstractDestinationClassNameMapper.java @@ -16,5 +16,5 @@ public abstract class AbstractDestinationClassNameMapper { public static final AbstractDestinationClassNameMapper INSTANCE = Mappers.getMapper( AbstractDestinationClassNameMapper.class ); - public abstract String intToString(Integer source); + public abstract Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/AbstractDestinationPackageNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/destination/AbstractDestinationPackageNameMapper.java index b59d5cd21b..e26a6bbe71 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/AbstractDestinationPackageNameMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/AbstractDestinationPackageNameMapper.java @@ -16,5 +16,5 @@ public abstract class AbstractDestinationPackageNameMapper { public static final AbstractDestinationPackageNameMapper INSTANCE = Mappers.getMapper( AbstractDestinationPackageNameMapper.class ); - public abstract String intToString(Integer source); + public abstract Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapper.java index a331096a63..99bd8915a5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapper.java @@ -15,5 +15,5 @@ public interface DestinationClassNameMapper { DestinationClassNameMapper INSTANCE = Mappers.getMapper( DestinationClassNameMapper.class ); - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperDecorated.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperDecorated.java index ef44767379..454e5e15bc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperDecorated.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperDecorated.java @@ -17,5 +17,5 @@ public interface DestinationClassNameMapperDecorated { DestinationClassNameMapperDecorated INSTANCE = Mappers.getMapper( DestinationClassNameMapperDecorated.class ); - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperDecorator.java index 216786da24..3ba1433e21 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperDecorator.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperDecorator.java @@ -16,7 +16,7 @@ protected DestinationClassNameMapperDecorator(DestinationClassNameMapperDecorate } @Override - public String intToString(Integer source) { - return delegate.intToString( source ); + public Target map(Integer source) { + return delegate.map( source ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperWithConfig.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperWithConfig.java index d76392d11d..d13675f0ab 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperWithConfig.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperWithConfig.java @@ -15,5 +15,5 @@ public interface DestinationClassNameMapperWithConfig { DestinationClassNameMapperWithConfig INSTANCE = Mappers.getMapper( DestinationClassNameMapperWithConfig.class ); - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperWithConfigOverride.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperWithConfigOverride.java index b218fff158..a3f845462b 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperWithConfigOverride.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameMapperWithConfigOverride.java @@ -16,5 +16,5 @@ public interface DestinationClassNameMapperWithConfigOverride { DestinationClassNameMapperWithConfigOverride INSTANCE = Mappers.getMapper( DestinationClassNameMapperWithConfigOverride.class ); - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java index 30611a8d60..1f6e255b95 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameTest.java @@ -16,6 +16,7 @@ /** * @author Christophe Labouisse on 27/05/2015. */ +@WithClasses( Target.class ) public class DestinationClassNameTest { @ProcessorTest @WithClasses({ DestinationClassNameMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameWithJsr330Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameWithJsr330Mapper.java index 92c72a877d..3358f68fd4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameWithJsr330Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationClassNameWithJsr330Mapper.java @@ -13,5 +13,5 @@ */ @Mapper(implementationName = "Jsr330Impl", componentModel = MappingConstants.ComponentModel.JSR330) public interface DestinationClassNameWithJsr330Mapper { - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapper.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapper.java index 8c4740e323..c049036a13 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapper.java @@ -15,5 +15,5 @@ public interface DestinationPackageNameMapper { DestinationPackageNameMapper INSTANCE = Mappers.getMapper( DestinationPackageNameMapper.class ); - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperDecorated.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperDecorated.java index 1dd371b48a..98f6236027 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperDecorated.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperDecorated.java @@ -17,5 +17,5 @@ public interface DestinationPackageNameMapperDecorated { DestinationPackageNameMapperDecorated INSTANCE = Mappers.getMapper( DestinationPackageNameMapperDecorated.class ); - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperDecorator.java index 4f6dd3242b..667bd99349 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperDecorator.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperDecorator.java @@ -16,7 +16,7 @@ protected DestinationPackageNameMapperDecorator(DestinationPackageNameMapperDeco } @Override - public String intToString(Integer source) { - return delegate.intToString( source ); + public Target map(Integer source) { + return delegate.map( source ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithConfig.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithConfig.java index fbe69ce43e..6a5a870520 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithConfig.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithConfig.java @@ -15,5 +15,5 @@ public interface DestinationPackageNameMapperWithConfig { DestinationPackageNameMapperWithConfig INSTANCE = Mappers.getMapper( DestinationPackageNameMapperWithConfig.class ); - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithConfigOverride.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithConfigOverride.java index b53dcaae42..79699006b1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithConfigOverride.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithConfigOverride.java @@ -16,5 +16,5 @@ public interface DestinationPackageNameMapperWithConfigOverride { DestinationPackageNameMapperWithConfigOverride INSTANCE = Mappers.getMapper( DestinationPackageNameMapperWithConfigOverride.class ); - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithSuffix.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithSuffix.java index 2a221e5f5b..068f30331d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithSuffix.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameMapperWithSuffix.java @@ -15,5 +15,5 @@ public interface DestinationPackageNameMapperWithSuffix { DestinationPackageNameMapperWithSuffix INSTANCE = Mappers.getMapper( DestinationPackageNameMapperWithSuffix.class ); - String intToString(Integer source); + Target map(Integer source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameTest.java index 63c7ece6d5..5e02c88d93 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/DestinationPackageNameTest.java @@ -15,6 +15,7 @@ * @author Christophe Labouisse on 27/05/2015. */ @IssueKey( "556" ) +@WithClasses( Target.class ) public class DestinationPackageNameTest { @ProcessorTest @WithClasses({ DestinationPackageNameMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/destination/Target.java b/processor/src/test/java/org/mapstruct/ap/test/destination/Target.java new file mode 100644 index 0000000000..5b3bb4ba34 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/destination/Target.java @@ -0,0 +1,19 @@ +/* + * 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.destination; + +public class Target { + + private final Integer source; + + public Target(Integer source) { + this.source = source; + } + + public Integer getSource() { + return source; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTarget.java b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTarget.java new file mode 100644 index 0000000000..f1b3a0f073 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTarget.java @@ -0,0 +1,9 @@ +/* + * 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.emptytarget; + +public class EmptyTarget { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTargetMapper.java new file mode 100644 index 0000000000..8c82f81b7b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTargetMapper.java @@ -0,0 +1,18 @@ +/* + * 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.emptytarget; + +import org.mapstruct.Mapper; + +@Mapper +public interface EmptyTargetMapper { + + TargetWithNoSetters mapToTargetWithSetters(Source source); + + EmptyTarget mapToEmptyTarget(Source source); + + Target mapToTarget(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTargetTest.java b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTargetTest.java new file mode 100644 index 0000000000..66fa65b796 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/EmptyTargetTest.java @@ -0,0 +1,39 @@ +/* + * 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.emptytarget; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +@IssueKey("1140") +@WithClasses({ + EmptyTarget.class, + EmptyTargetMapper.class, + Source.class, + Target.class, + TargetWithNoSetters.class, +}) +class EmptyTargetTest { + + @ProcessorTest + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = EmptyTargetMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 13, + message = "No target property found for target \"TargetWithNoSetters\"."), + @Diagnostic(type = EmptyTargetMapper.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 15, + message = "No target property found for target \"EmptyTarget\".") + }) + void shouldProvideWarnings() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/emptytarget/Source.java b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/Source.java new file mode 100644 index 0000000000..643cdccf3a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/Source.java @@ -0,0 +1,36 @@ +/* + * 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.emptytarget; + +public class Source { + private String label; + private double weight; + private Object content; + + public Object getContent() { + return content; + } + + public void setContent(Object content) { + this.content = content; + } + + public double getWeight() { + return weight; + } + + public void setWeight(double weight) { + this.weight = weight; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/emptytarget/Target.java b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/Target.java new file mode 100644 index 0000000000..488a1fa8a6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/Target.java @@ -0,0 +1,34 @@ +/* + * 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.emptytarget; + +/** + * @author Filip Hrisafov + */ +public class Target { + + private final String label; + private final double weight; + private final Object content; + + public Target(String label, double weight, Object content) { + this.label = label; + this.weight = weight; + this.content = content; + } + + public String getLabel() { + return label; + } + + public double getWeight() { + return weight; + } + + public Object getContent() { + return content; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/emptytarget/TargetWithNoSetters.java b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/TargetWithNoSetters.java new file mode 100644 index 0000000000..edeb80f0d5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/emptytarget/TargetWithNoSetters.java @@ -0,0 +1,15 @@ +/* + * 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.emptytarget; + +public class TargetWithNoSetters { + private int flightNumber; + private String airplaneName; + + public String getAirplaneName() { + return airplaneName; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/imports/SourceTargetMapper.java index 1380ca400a..845544ba9f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/imports/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/SourceTargetMapper.java @@ -25,7 +25,7 @@ public interface SourceTargetMapper { ParseException sourceToTarget(Named source); //custom types - Map listToMap(List list); + Value listToMap(List list); java.util.List namedsToExceptions(java.util.List source); @@ -36,4 +36,12 @@ public interface SourceTargetMapper { java.util.Map stringsToDates(java.util.Map stringDates); Target sourceToTarget(Source target); + + class Value { + private T value; + + public Value(List list) { + + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Idea.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Idea.java index bb8434fd63..063a5e5615 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Idea.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Idea.java @@ -11,4 +11,13 @@ */ public class Idea { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Plan.java b/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Plan.java index 8c55ebc273..7aa366ec95 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Plan.java +++ b/processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Plan.java @@ -11,4 +11,13 @@ */ public class Plan { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/ErroneousSourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/ErroneousSourceTargetMapper.java index 659867ca40..1196d773cb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/ErroneousSourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/ErroneousSourceTargetMapper.java @@ -8,14 +8,24 @@ import org.mapstruct.BeanMapping; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; -import org.mapstruct.factory.Mappers; @Mapper( unmappedTargetPolicy = ReportingPolicy.IGNORE, unmappedSourcePolicy = ReportingPolicy.IGNORE) public interface ErroneousSourceTargetMapper { - ErroneousSourceTargetMapper INSTANCE = Mappers.getMapper( ErroneousSourceTargetMapper.class ); @BeanMapping(ignoreUnmappedSourceProperties = "bar") - Object sourceToTarget(Object source); + Target map(Target source); + + class Target { + private final String value; + + public Target(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/MissingIgnoredSourceTest.java b/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/MissingIgnoredSourceTest.java index 77f80c5537..5794126604 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/MissingIgnoredSourceTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/missingignoredsource/MissingIgnoredSourceTest.java @@ -22,7 +22,7 @@ public class MissingIgnoredSourceTest { diagnostics = { @Diagnostic(type = ErroneousSourceTargetMapper.class, kind = Kind.ERROR, - line = 20, + line = 18, message = "Ignored unknown source property: \"bar\".") } ) diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java index 2ea183bfc9..5ea0a54cb2 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java @@ -7,10 +7,11 @@ import org.mapstruct.Mapper; import org.mapstruct.ObjectFactory; +import org.mapstruct.ReportingPolicy; import org.mapstruct.TargetType; import org.mapstruct.factory.Mappers; -@Mapper +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface ObjectFactoryMapper { ObjectFactoryMapper INSTANCE = Mappers.getMapper( ObjectFactoryMapper.class ); @@ -44,6 +45,16 @@ public boolean isA() { } abstract class Target { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } } class TargetA extends Target { diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentSource.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentSource.java index 7f9f19a2ab..295b4e2871 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentSource.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentSource.java @@ -7,4 +7,13 @@ public abstract class AbstractParentSource { + private String parentValue; + + public String getParentValue() { + return parentValue; + } + + public void setParentValue(String parentValue) { + this.parentValue = parentValue; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentTarget.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentTarget.java index 3215aee644..a95f58b260 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentTarget.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/AbstractParentTarget.java @@ -7,4 +7,13 @@ public abstract class AbstractParentTarget { + private String parentValue; + + public String getParentValue() { + return parentValue; + } + + public void setParentValue(String parentValue) { + this.parentValue = parentValue; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentSource.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentSource.java index 94dde481b0..a047ee2573 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentSource.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentSource.java @@ -7,4 +7,13 @@ public class ImplementedParentSource extends AbstractParentSource { + private String implementedParentValue; + + public String getImplementedParentValue() { + return implementedParentValue; + } + + public void setImplementedParentValue(String implementedParentValue) { + this.implementedParentValue = implementedParentValue; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentTarget.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentTarget.java index 7f3d254e21..a187a87091 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentTarget.java +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/fixture/ImplementedParentTarget.java @@ -7,4 +7,13 @@ public class ImplementedParentTarget extends AbstractParentTarget { + private String implementedParentValue; + + public String getImplementedParentValue() { + return implementedParentValue; + } + + public void setImplementedParentValue(String implementedParentValue) { + this.implementedParentValue = implementedParentValue; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SimpleMapper.java b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SimpleMapper.java index 3bcea83364..f40525538c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SimpleMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SimpleMapper.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.versioninfo; import org.mapstruct.Mapper; +import org.mapstruct.ap.test.WithProperties; /** * @author Andreas Gudian @@ -13,5 +14,5 @@ */ @Mapper public interface SimpleMapper { - Object toObject(Object object); + WithProperties toObject(WithProperties object); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapper.java b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapper.java index 02a7b467b1..c3f9d39d26 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapper.java @@ -6,11 +6,12 @@ package org.mapstruct.ap.test.versioninfo; import org.mapstruct.Mapper; +import org.mapstruct.ap.test.WithProperties; /** * @author Filip Hrisafov */ @Mapper(suppressTimestampInGenerated = true) public interface SuppressTimestampViaMapper { - Object toObject(Object object); + WithProperties toObject(WithProperties object); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapperConfig.java b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapperConfig.java index a471cad028..54dc6b2fb7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapperConfig.java +++ b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/SuppressTimestampViaMapperConfig.java @@ -7,13 +7,14 @@ import org.mapstruct.Mapper; import org.mapstruct.MapperConfig; +import org.mapstruct.ap.test.WithProperties; /** * @author Filip Hrisafov */ @Mapper(config = SuppressTimestampViaMapperConfig.Config.class) public interface SuppressTimestampViaMapperConfig { - Object toObject(Object object); + WithProperties toObject(WithProperties object); @MapperConfig(suppressTimestampInGenerated = true) interface Config { diff --git a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java index f3192b85ad..60781c399f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/versioninfo/VersionInfoTest.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.versioninfo; import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.WithProperties; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -18,7 +19,7 @@ * */ @IssueKey( "424" ) -@WithClasses( SimpleMapper.class ) +@WithClasses( { SimpleMapper.class, WithProperties.class } ) public class VersionInfoTest { @RegisterExtension final GeneratedSource generatedSource = new GeneratedSource(); diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java index a66b646f1e..b5dd2893c8 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassAbstractMapperImpl.java @@ -58,6 +58,8 @@ protected SubTarget subSourceToSubTarget(SubSource subSource) { SubTarget subTarget = new SubTarget(); + subTarget.setParentValue( subSource.getParentValue() ); + subTarget.setImplementedParentValue( subSource.getImplementedParentValue() ); subTarget.setValue( subSource.getValue() ); return subTarget; @@ -74,6 +76,9 @@ protected SubTargetOther subSourceOtherToSubTargetOther(SubSourceOther subSource SubTargetOther subTargetOther = new SubTargetOther( finalValue ); + subTargetOther.setParentValue( subSourceOther.getParentValue() ); + subTargetOther.setImplementedParentValue( subSourceOther.getImplementedParentValue() ); + return subTargetOther; } @@ -88,6 +93,9 @@ protected SubSourceSeparate subTargetSeparateToSubSourceSeparate(SubTargetSepara SubSourceSeparate subSourceSeparate = new SubSourceSeparate( separateValue ); + subSourceSeparate.setParentValue( subTargetSeparate.getParentValue() ); + subSourceSeparate.setImplementedParentValue( subTargetSeparate.getImplementedParentValue() ); + return subSourceSeparate; } @@ -102,6 +110,9 @@ protected SubSourceOverride subTargetOtherToSubSourceOverride(SubTargetOther sub SubSourceOverride subSourceOverride = new SubSourceOverride( finalValue ); + subSourceOverride.setParentValue( subTargetOther.getParentValue() ); + subSourceOverride.setImplementedParentValue( subTargetOther.getImplementedParentValue() ); + return subSourceOverride; } @@ -112,6 +123,8 @@ protected SubSource subTargetToSubSource(SubTarget subTarget) { SubSource subSource = new SubSource(); + subSource.setParentValue( subTarget.getParentValue() ); + subSource.setImplementedParentValue( subTarget.getImplementedParentValue() ); subSource.setValue( subTarget.getValue() ); return subSource; diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapperImpl.java index 444b012078..695ef03577 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassImplementedMapperImpl.java @@ -29,6 +29,9 @@ else if (item instanceof SubSourceOther) { else { ImplementedParentTarget implementedParentTarget = new ImplementedParentTarget(); + implementedParentTarget.setParentValue( item.getParentValue() ); + implementedParentTarget.setImplementedParentValue( item.getImplementedParentValue() ); + return implementedParentTarget; } } @@ -40,6 +43,8 @@ protected SubTarget subSourceToSubTarget(SubSource subSource) { SubTarget subTarget = new SubTarget(); + subTarget.setParentValue( subSource.getParentValue() ); + subTarget.setImplementedParentValue( subSource.getImplementedParentValue() ); subTarget.setValue( subSource.getValue() ); return subTarget; @@ -56,6 +61,9 @@ protected SubTargetOther subSourceOtherToSubTargetOther(SubSourceOther subSource SubTargetOther subTargetOther = new SubTargetOther( finalValue ); + subTargetOther.setParentValue( subSourceOther.getParentValue() ); + subTargetOther.setImplementedParentValue( subSourceOther.getImplementedParentValue() ); + return subTargetOther; } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapperImpl.java index 8366d53ea9..782831d6e3 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/subclassmapping/fixture/SubclassInterfaceMapperImpl.java @@ -38,6 +38,8 @@ protected SubTarget subSourceToSubTarget(SubSource subSource) { SubTarget subTarget = new SubTarget(); + subTarget.setParentValue( subSource.getParentValue() ); + subTarget.setImplementedParentValue( subSource.getImplementedParentValue() ); subTarget.setValue( subSource.getValue() ); return subTarget; @@ -54,6 +56,9 @@ protected SubTargetOther subSourceOtherToSubTargetOther(SubSourceOther subSource SubTargetOther subTargetOther = new SubTargetOther( finalValue ); + subTargetOther.setParentValue( subSourceOther.getParentValue() ); + subTargetOther.setImplementedParentValue( subSourceOther.getImplementedParentValue() ); + return subTargetOther; } } From 2fb5776350abf59f09cf26c727eb16ebfffadbc3 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 11 May 2025 20:07:53 +0200 Subject: [PATCH 482/597] Add release notes for next version --- NEXT_RELEASE_CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index e0f4cd31f0..20cbc8c3f8 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,10 +1,26 @@ ### Features +* Support for Java 21 Sequenced Collections (#3240) + + ### Enhancements +* Add support for locale parameter for numberFormat and dateFormat (#3628) +* Behaviour change: Warning when the target has no target properties (#1140) + + + ### Bugs +* Improve error message when mapping non-iterable to array (#3786) + ### Documentation ### Build +### Behaviour Change + +#### Warning when the target has no target properties + +With this change, if the target bean does not have any target properties, a warning will be shown. +This is like this to avoid potential mistakes by users, where they might think that the target bean has properties, but it does not. From fce73aee6ab655f3896dee440fa890a1971a281f Mon Sep 17 00:00:00 2001 From: roelmang Date: Sun, 11 May 2025 21:58:47 +0200 Subject: [PATCH 483/597] #3729 Support for using inner class Builder without using static factory method --- NEXT_RELEASE_CHANGELOG.md | 3 + .../chapter-3-defining-a-mapper.asciidoc | 7 +- .../ap/spi/DefaultBuilderProvider.java | 98 +++++++++++++--- .../ErroneousSimpleBuilderMapper.java | 5 +- .../{ => innerclass}/SimpleBuilderMapper.java | 5 +- ...lderThroughInnerClassConstructorTest.java} | 15 +-- ...ImmutablePersonWithInnerClassBuilder.java} | 14 +-- .../ErroneousSimpleBuilderMapper.java | 23 ++++ .../SimpleBuilderMapper.java | 23 ++++ ...BuilderThroughStaticFactoryMethodTest.java | 61 ++++++++++ ...ePersonWithStaticFactoryMethodBuilder.java | 105 ++++++++++++++++++ 11 files changed, 321 insertions(+), 38 deletions(-) rename processor/src/test/java/org/mapstruct/ap/test/builder/simple/{ => innerclass}/ErroneousSimpleBuilderMapper.java (72%) rename processor/src/test/java/org/mapstruct/ap/test/builder/simple/{ => innerclass}/SimpleBuilderMapper.java (74%) rename processor/src/test/java/org/mapstruct/ap/test/builder/simple/{SimpleImmutableBuilderTest.java => innerclass/SimpleImmutableBuilderThroughInnerClassConstructorTest.java} (78%) rename processor/src/test/java/org/mapstruct/ap/test/builder/simple/{SimpleImmutablePerson.java => innerclass/SimpleImmutablePersonWithInnerClassBuilder.java} (86%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/ErroneousSimpleBuilderMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleBuilderMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleImmutableBuilderThroughStaticFactoryMethodTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleImmutablePersonWithStaticFactoryMethodBuilder.java diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 20cbc8c3f8..2d0555cb24 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -6,6 +6,9 @@ ### Enhancements * Add support for locale parameter for numberFormat and dateFormat (#3628) +* Detect Builder without a factory method (#3729) - With this if there is an inner class that ends with `Builder` and has a constructor with parameters, +it will be treated as a potential builder. +Builders through static methods on the type have a precedence. * Behaviour change: Warning when the target has no target properties (#1140) diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index ea574603ea..81980a35ac 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -421,8 +421,11 @@ If a Builder exists for a certain type, then that builder will be used for the m The default implementation of the `BuilderProvider` assumes the following: -* The type has a parameterless public static builder creation method that returns a builder. -So for example `Person` has a public static method that returns `PersonBuilder`. +* The type has either +** A parameterless public static builder creation method that returns a builder. +e.g. `Person` has a public static method that returns `PersonBuilder`. +** A public static inner class with the name having the suffix "Builder", and a public no-args constructor +e.g. `Person` has an inner class `PersonBuilder` with a public no-args constructor. * The builder type has a parameterless public method (build method) that returns the type being built. In our example `PersonBuilder` has a method returning `Person`. * In case there are multiple build methods, MapStruct will look for a method called `build`, if such method exists diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java index 92cd1ed80d..afcee7d247 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java @@ -10,6 +10,8 @@ import java.util.Collections; import java.util.List; import java.util.regex.Pattern; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; @@ -184,32 +186,96 @@ protected BuilderInfo findBuilderInfo(TypeElement typeElement, boolean checkPare return null; } - List methods = ElementFilter.methodsIn( typeElement.getEnclosedElements() ); - List builderInfo = new ArrayList<>(); - for ( ExecutableElement method : methods ) { - if ( isPossibleBuilderCreationMethod( method, typeElement ) ) { - TypeElement builderElement = getTypeElement( method.getReturnType() ); - Collection buildMethods = findBuildMethods( builderElement, typeElement ); - if ( !buildMethods.isEmpty() ) { - builderInfo.add( new BuilderInfo.Builder() - .builderCreationMethod( method ) - .buildMethod( buildMethods ) - .build() - ); + // Builder infos which are determined by a static method on the type itself + List methodBuilderInfos = new ArrayList<>(); + // Builder infos which are determined by an inner builder class in the type itself + List innerClassBuilderInfos = new ArrayList<>(); + + for ( Element enclosedElement : typeElement.getEnclosedElements() ) { + if ( ElementKind.METHOD == enclosedElement.getKind() ) { + ExecutableElement method = (ExecutableElement) enclosedElement; + BuilderInfo builderInfo = determineMethodBuilderInfo( method, typeElement ); + if ( builderInfo != null ) { + methodBuilderInfos.add( builderInfo ); } } + else if ( ElementKind.CLASS == enclosedElement.getKind() ) { + if ( !methodBuilderInfos.isEmpty() ) { + // Small optimization to not check the inner classes + // if we already have at least one builder through a method + continue; + } + TypeElement classElement = (TypeElement) enclosedElement; + BuilderInfo builderInfo = determineInnerClassBuilderInfo( classElement, typeElement ); + if ( builderInfo != null ) { + innerClassBuilderInfos.add( builderInfo ); + } + } + } - if ( builderInfo.size() == 1 ) { - return builderInfo.get( 0 ); + if ( methodBuilderInfos.size() == 1 ) { + return methodBuilderInfos.get( 0 ); + } + else if ( methodBuilderInfos.size() > 1 ) { + throw new MoreThanOneBuilderCreationMethodException( typeElement.asType(), methodBuilderInfos ); } - else if ( builderInfo.size() > 1 ) { - throw new MoreThanOneBuilderCreationMethodException( typeElement.asType(), builderInfo ); + else if ( innerClassBuilderInfos.size() == 1 ) { + return innerClassBuilderInfos.get( 0 ); + } + else if ( innerClassBuilderInfos.size() > 1 ) { + throw new MoreThanOneBuilderCreationMethodException( typeElement.asType(), innerClassBuilderInfos ); } if ( checkParent ) { return findBuilderInfo( typeElement.getSuperclass() ); } + + return null; + } + + private BuilderInfo determineMethodBuilderInfo(ExecutableElement method, + TypeElement typeElement) { + if ( isPossibleBuilderCreationMethod( method, typeElement ) ) { + TypeElement builderElement = getTypeElement( method.getReturnType() ); + Collection buildMethods = findBuildMethods( builderElement, typeElement ); + if ( !buildMethods.isEmpty() ) { + return new BuilderInfo.Builder() + .builderCreationMethod( method ) + .buildMethod( buildMethods ) + .build(); + } + } + + return null; + } + + private BuilderInfo determineInnerClassBuilderInfo(TypeElement innerClassElement, + TypeElement typeElement) { + if ( innerClassElement.getModifiers().contains( Modifier.PUBLIC ) + && innerClassElement.getModifiers().contains( Modifier.STATIC ) + && innerClassElement.getSimpleName().toString().endsWith( "Builder" ) ) { + for ( Element element : innerClassElement.getEnclosedElements() ) { + if ( ElementKind.CONSTRUCTOR == element.getKind() ) { + ExecutableElement constructor = (ExecutableElement) element; + if ( constructor.getParameters().isEmpty() ) { + // We have a no-arg constructor + // Now check if we have build methods + Collection buildMethods = findBuildMethods( innerClassElement, typeElement ); + if ( !buildMethods.isEmpty() ) { + return new BuilderInfo.Builder() + .builderCreationMethod( constructor ) + .buildMethod( buildMethods ) + .build(); + } + // If we don't have any build methods + // then we can stop since we are only interested in the no-arg constructor + return null; + } + } + } + } + return null; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/ErroneousSimpleBuilderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/ErroneousSimpleBuilderMapper.java similarity index 72% rename from processor/src/test/java/org/mapstruct/ap/test/builder/simple/ErroneousSimpleBuilderMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/ErroneousSimpleBuilderMapper.java index 305ae77e80..18e82ed19c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/ErroneousSimpleBuilderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/ErroneousSimpleBuilderMapper.java @@ -3,12 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.builder.simple; +package org.mapstruct.ap.test.builder.simple.innerclass; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; import org.mapstruct.ReportingPolicy; +import org.mapstruct.ap.test.builder.simple.SimpleMutablePerson; @Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) public interface ErroneousSimpleBuilderMapper { @@ -18,5 +19,5 @@ public interface ErroneousSimpleBuilderMapper { @Mapping(target = "job", ignore = true ), @Mapping(target = "city", ignore = true ) }) - SimpleImmutablePerson toImmutable(SimpleMutablePerson source); + SimpleImmutablePersonWithInnerClassBuilder toImmutable(SimpleMutablePerson source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleBuilderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/SimpleBuilderMapper.java similarity index 74% rename from processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleBuilderMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/SimpleBuilderMapper.java index 0b0e961b30..e2c673401a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleBuilderMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/SimpleBuilderMapper.java @@ -3,12 +3,13 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.builder.simple; +package org.mapstruct.ap.test.builder.simple.innerclass; import org.mapstruct.CollectionMappingStrategy; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; +import org.mapstruct.ap.test.builder.simple.SimpleMutablePerson; @Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) public interface SimpleBuilderMapper { @@ -18,5 +19,5 @@ public interface SimpleBuilderMapper { @Mapping(target = "job", constant = "programmer"), @Mapping(target = "city", expression = "java(\"Bengalore\")") }) - SimpleImmutablePerson toImmutable(SimpleMutablePerson source); + SimpleImmutablePersonWithInnerClassBuilder toImmutable(SimpleMutablePerson source); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/SimpleImmutableBuilderThroughInnerClassConstructorTest.java similarity index 78% rename from processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java rename to processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/SimpleImmutableBuilderThroughInnerClassConstructorTest.java index 7f77621644..0a2c5287bc 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutableBuilderTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/SimpleImmutableBuilderThroughInnerClassConstructorTest.java @@ -3,11 +3,12 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.builder.simple; +package org.mapstruct.ap.test.builder.simple.innerclass; import java.util.Arrays; import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.builder.simple.SimpleMutablePerson; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; @@ -20,16 +21,16 @@ @WithClasses({ SimpleMutablePerson.class, - SimpleImmutablePerson.class + SimpleImmutablePersonWithInnerClassBuilder.class }) -public class SimpleImmutableBuilderTest { +public class SimpleImmutableBuilderThroughInnerClassConstructorTest { @RegisterExtension final GeneratedSource generatedSource = new GeneratedSource(); @ProcessorTest @WithClasses({ SimpleBuilderMapper.class }) - public void testSimpleImmutableBuilderHappyPath() { + public void testSimpleImmutableBuilderThroughInnerClassConstructorHappyPath() { SimpleBuilderMapper mapper = Mappers.getMapper( SimpleBuilderMapper.class ); SimpleMutablePerson source = new SimpleMutablePerson(); source.setAge( 3 ); @@ -37,7 +38,7 @@ public void testSimpleImmutableBuilderHappyPath() { source.setChildren( Arrays.asList( "Alice", "Tom" ) ); source.setAddress( "Plaza 1" ); - SimpleImmutablePerson targetObject = mapper.toImmutable( source ); + SimpleImmutablePersonWithInnerClassBuilder targetObject = mapper.toImmutable( source ); assertThat( targetObject.getAge() ).isEqualTo( 3 ); assertThat( targetObject.getName() ).isEqualTo( "Bob" ); @@ -53,8 +54,8 @@ public void testSimpleImmutableBuilderHappyPath() { diagnostics = @Diagnostic( kind = javax.tools.Diagnostic.Kind.ERROR, type = ErroneousSimpleBuilderMapper.class, - line = 21, + line = 22, message = "Unmapped target property: \"name\".")) - public void testSimpleImmutableBuilderMissingPropertyFailsToCompile() { + public void testSimpleImmutableBuilderThroughInnerClassConstructorMissingPropertyFailsToCompile() { } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutablePerson.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/SimpleImmutablePersonWithInnerClassBuilder.java similarity index 86% rename from processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutablePerson.java rename to processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/SimpleImmutablePersonWithInnerClassBuilder.java index 2fe2c8ded2..a9bda42d33 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/SimpleImmutablePerson.java +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/innerclass/SimpleImmutablePersonWithInnerClassBuilder.java @@ -3,12 +3,12 @@ * * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -package org.mapstruct.ap.test.builder.simple; +package org.mapstruct.ap.test.builder.simple.innerclass; import java.util.ArrayList; import java.util.List; -public class SimpleImmutablePerson { +public class SimpleImmutablePersonWithInnerClassBuilder { private final String name; private final int age; private final String job; @@ -16,7 +16,7 @@ public class SimpleImmutablePerson { private final String address; private final List children; - SimpleImmutablePerson(Builder builder) { + SimpleImmutablePersonWithInnerClassBuilder(Builder builder) { this.name = builder.name; this.age = builder.age; this.job = builder.job; @@ -25,10 +25,6 @@ public class SimpleImmutablePerson { this.children = new ArrayList<>(builder.children); } - public static Builder builder() { - return new Builder(); - } - public int getAge() { return age; } @@ -66,8 +62,8 @@ public Builder age(int age) { return this; } - public SimpleImmutablePerson build() { - return new SimpleImmutablePerson( this ); + public SimpleImmutablePersonWithInnerClassBuilder build() { + return new SimpleImmutablePersonWithInnerClassBuilder( this ); } public Builder name(String name) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/ErroneousSimpleBuilderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/ErroneousSimpleBuilderMapper.java new file mode 100644 index 0000000000..6fc51ace3d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/ErroneousSimpleBuilderMapper.java @@ -0,0 +1,23 @@ +/* + * 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.builder.simple.staticfactorymethod; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.ap.test.builder.simple.SimpleMutablePerson; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface ErroneousSimpleBuilderMapper { + + @Mappings({ + @Mapping(target = "address", ignore = true ), + @Mapping(target = "job", ignore = true ), + @Mapping(target = "city", ignore = true ) + }) + SimpleImmutablePersonWithStaticFactoryMethodBuilder toImmutable(SimpleMutablePerson source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleBuilderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleBuilderMapper.java new file mode 100644 index 0000000000..4bd200bc7c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleBuilderMapper.java @@ -0,0 +1,23 @@ +/* + * 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.builder.simple.staticfactorymethod; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.builder.simple.SimpleMutablePerson; + +@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface SimpleBuilderMapper { + + @Mappings({ + @Mapping(target = "name", source = "fullName"), + @Mapping(target = "job", constant = "programmer"), + @Mapping(target = "city", expression = "java(\"Bengalore\")") + }) + SimpleImmutablePersonWithStaticFactoryMethodBuilder toImmutable(SimpleMutablePerson source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleImmutableBuilderThroughStaticFactoryMethodTest.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleImmutableBuilderThroughStaticFactoryMethodTest.java new file mode 100644 index 0000000000..90fac6061e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleImmutableBuilderThroughStaticFactoryMethodTest.java @@ -0,0 +1,61 @@ +/* + * 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.builder.simple.staticfactorymethod; + +import java.util.Arrays; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.builder.simple.SimpleMutablePerson; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.GeneratedSource; +import org.mapstruct.factory.Mappers; + +import static org.assertj.core.api.Assertions.assertThat; + +@WithClasses({ + SimpleMutablePerson.class, + SimpleImmutablePersonWithStaticFactoryMethodBuilder.class +}) +public class SimpleImmutableBuilderThroughStaticFactoryMethodTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses({ SimpleBuilderMapper.class }) + public void testSimpleImmutableBuilderThroughStaticFactoryMethodHappyPath() { + SimpleBuilderMapper mapper = Mappers.getMapper( SimpleBuilderMapper.class ); + SimpleMutablePerson source = new SimpleMutablePerson(); + source.setAge( 3 ); + source.setFullName( "Bob" ); + source.setChildren( Arrays.asList( "Alice", "Tom" ) ); + source.setAddress( "Plaza 1" ); + + SimpleImmutablePersonWithStaticFactoryMethodBuilder targetObject = mapper.toImmutable( source ); + + assertThat( targetObject.getAge() ).isEqualTo( 3 ); + assertThat( targetObject.getName() ).isEqualTo( "Bob" ); + assertThat( targetObject.getJob() ).isEqualTo( "programmer" ); + assertThat( targetObject.getCity() ).isEqualTo( "Bengalore" ); + assertThat( targetObject.getAddress() ).isEqualTo( "Plaza 1" ); + assertThat( targetObject.getChildren() ).contains( "Alice", "Tom" ); + } + + @ProcessorTest + @WithClasses({ ErroneousSimpleBuilderMapper.class }) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = ErroneousSimpleBuilderMapper.class, + line = 22, + message = "Unmapped target property: \"name\".")) + public void testSimpleImmutableBuilderThroughStaticFactoryMethodMissingPropertyFailsToCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleImmutablePersonWithStaticFactoryMethodBuilder.java b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleImmutablePersonWithStaticFactoryMethodBuilder.java new file mode 100644 index 0000000000..c3dc590210 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/builder/simple/staticfactorymethod/SimpleImmutablePersonWithStaticFactoryMethodBuilder.java @@ -0,0 +1,105 @@ +/* + * 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.builder.simple.staticfactorymethod; + +import java.util.ArrayList; +import java.util.List; + +public class SimpleImmutablePersonWithStaticFactoryMethodBuilder { + private final String name; + private final int age; + private final String job; + private final String city; + private final String address; + private final List children; + + SimpleImmutablePersonWithStaticFactoryMethodBuilder(Builder builder) { + this.name = builder.name; + this.age = builder.age; + this.job = builder.job; + this.city = builder.city; + this.address = builder.address; + this.children = new ArrayList<>( builder.children ); + } + + public static Builder builder() { + return new Builder(); + } + + public int getAge() { + return age; + } + + public String getName() { + return name; + } + + public String getJob() { + return job; + } + + public String getCity() { + return city; + } + + public String getAddress() { + return address; + } + + public List getChildren() { + return children; + } + + public static class Builder { + private String name; + private int age; + private String job; + private String city; + private String address; + private List children = new ArrayList<>(); + + private Builder() { + } + + public Builder age(int age) { + this.age = age; + return this; + } + + public SimpleImmutablePersonWithStaticFactoryMethodBuilder build() { + return new SimpleImmutablePersonWithStaticFactoryMethodBuilder( this ); + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder job(String job) { + this.job = job; + return this; + } + + public Builder city(String city) { + this.city = city; + return this; + } + + public Builder address(String address) { + this.address = address; + return this; + } + + public List getChildren() { + throw new UnsupportedOperationException( "This is just a marker method" ); + } + + public Builder addChild(String child) { + this.children.add( child ); + return this; + } + } +} From 6e6fd01a2eb08177d5cc360a97e1721db511239d Mon Sep 17 00:00:00 2001 From: Yang Tang Date: Sun, 18 May 2025 00:40:51 +0800 Subject: [PATCH 484/597] #3821: Add support for custom exception for subclass exhaustive strategy for `@SubclassMapping` --------- Signed-off-by: TangYang --- .../main/java/org/mapstruct/BeanMapping.java | 12 +++ core/src/main/java/org/mapstruct/Mapper.java | 12 +++ .../main/java/org/mapstruct/MapperConfig.java | 12 +++ .../ap/internal/model/BeanMappingMethod.java | 17 ++++- .../model/source/BeanMappingOptions.java | 9 +++ .../internal/model/source/DefaultOptions.java | 4 + .../model/source/DelegatingOptions.java | 4 + .../model/source/MapperConfigOptions.java | 6 ++ .../internal/model/source/MapperOptions.java | 7 ++ .../ap/internal/model/BeanMappingMethod.ftl | 2 +- .../CustomExceptionSubclassMapper.java | 25 ++++++ .../CustomSubclassMappingException.java | 12 +++ .../CustomSubclassMappingExceptionTest.java | 76 +++++++++++++++++++ .../MapperConfigSubclassMapper.java | 29 +++++++ .../MapperSubclassMapper.java | 24 ++++++ 15 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomExceptionSubclassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomSubclassMappingException.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomSubclassMappingExceptionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MapperConfigSubclassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MapperSubclassMapper.java diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java index e94ff98f2d..309458f861 100644 --- a/core/src/main/java/org/mapstruct/BeanMapping.java +++ b/core/src/main/java/org/mapstruct/BeanMapping.java @@ -132,6 +132,18 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() */ SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR; + /** + * Specifies the exception type to be thrown when a missing subclass implementation is detected + * in combination with {@link SubclassMappings}, based on the {@link #subclassExhaustiveStrategy()}. + *

      + * This exception will only be thrown when the {@code subclassExhaustiveStrategy} is set to + * {@link SubclassExhaustiveStrategy#RUNTIME_EXCEPTION}. + * + * @return the exception class to throw when missing implementations are found. + * Defaults to {@link IllegalArgumentException}. + */ + Class subclassExhaustiveException() default IllegalArgumentException.class; + /** * Default ignore all mappings. All mappings have to be defined manually. No automatic mapping will take place. No * warning will be issued on missing source or target properties. diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 8a6f48dad9..398dc1870a 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -281,6 +281,18 @@ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default */ SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR; + /** + * Specifies the exception type to be thrown when a missing subclass implementation is detected + * in combination with {@link SubclassMappings}, based on the {@link #subclassExhaustiveStrategy()}. + *

      + * This exception will only be thrown when the {@code subclassExhaustiveStrategy} is set to + * {@link SubclassExhaustiveStrategy#RUNTIME_EXCEPTION}. + * + * @return the exception class to throw when missing implementations are found. + * Defaults to {@link IllegalArgumentException}. + */ + Class subclassExhaustiveException() default IllegalArgumentException.class; + /** * Determines whether to use field or constructor injection. This is only used on annotated based component models * such as CDI, Spring and JSR 330. diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java index 915f3dd120..8631562a56 100644 --- a/core/src/main/java/org/mapstruct/MapperConfig.java +++ b/core/src/main/java/org/mapstruct/MapperConfig.java @@ -249,6 +249,18 @@ MappingInheritanceStrategy mappingInheritanceStrategy() */ SubclassExhaustiveStrategy subclassExhaustiveStrategy() default COMPILE_ERROR; + /** + * Specifies the exception type to be thrown when a missing subclass implementation is detected + * in combination with {@link SubclassMappings}, based on the {@link #subclassExhaustiveStrategy()}. + *

      + * This exception will only be thrown when the {@code subclassExhaustiveStrategy} is set to + * {@link SubclassExhaustiveStrategy#RUNTIME_EXCEPTION}. + * + * @return the exception class to throw when missing implementations are found. + * Defaults to {@link IllegalArgumentException}. + */ + Class subclassExhaustiveException() default IllegalArgumentException.class; + /** * Determines whether to use field or constructor injection. This is only used on annotated based component models * such as CDI, Spring and JSR 330. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 08c8ceda59..34c1ea3ccb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -100,6 +100,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { private final String finalizedResultName; private final List beforeMappingReferencesWithFinalizedReturnType; private final List afterMappingReferencesWithFinalizedReturnType; + private final Type subclassExhaustiveException; private final MappingReferences mappingReferences; @@ -378,6 +379,11 @@ else if ( !method.isUpdateMethod() ) { } + TypeMirror subclassExhaustiveException = method.getOptions() + .getBeanMapping() + .getSubclassExhaustiveException(); + Type subclassExhaustiveExceptionType = ctx.getTypeFactory().getType( subclassExhaustiveException ); + List subclasses = new ArrayList<>(); for ( SubclassMappingOptions subclassMappingOptions : method.getOptions().getSubclassMappings() ) { subclasses.add( createSubclassMapping( subclassMappingOptions ) ); @@ -451,7 +457,8 @@ else if ( !sourceParameter.getType().isPrimitive() ) { finalizeMethod, mappingReferences, subclasses, - presenceChecksByParameter + presenceChecksByParameter, + subclassExhaustiveExceptionType ); } @@ -1954,7 +1961,8 @@ private BeanMappingMethod(Method method, MethodReference finalizerMethod, MappingReferences mappingReferences, List subclassMappings, - Map presenceChecksByParameter) { + Map presenceChecksByParameter, + Type subclassExhaustiveException) { super( method, annotations, @@ -1969,6 +1977,7 @@ private BeanMappingMethod(Method method, this.propertyMappings = propertyMappings; this.returnTypeBuilder = returnTypeBuilder; this.finalizerMethod = finalizerMethod; + this.subclassExhaustiveException = subclassExhaustiveException; if ( this.finalizerMethod != null ) { this.finalizedResultName = Strings.getSafeVariableName( getResultName() + "Result", existingVariableNames ); @@ -2017,6 +2026,10 @@ else if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) { this.subclassMappings = subclassMappings; } + public Type getSubclassExhaustiveException() { + return subclassExhaustiveException; + } + public List getConstantMappings() { return constantMappings; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java index ac27dfff00..73a4d7c12d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMappingOptions.java @@ -11,6 +11,7 @@ import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.gem.BeanMappingGem; import org.mapstruct.ap.internal.gem.BuilderGem; @@ -182,6 +183,14 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { .orElse( next().getSubclassExhaustiveStrategy() ); } + @Override + public TypeMirror getSubclassExhaustiveException() { + return Optional.ofNullable( beanMapping ).map( BeanMappingGem::subclassExhaustiveException ) + .filter( GemValue::hasValue ) + .map( GemValue::getValue ) + .orElse( next().getSubclassExhaustiveException() ); + } + @Override public ReportingPolicyGem unmappedTargetPolicy() { return Optional.ofNullable( beanMapping ).map( BeanMappingGem::unmappedTargetPolicy ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index c754d3c395..e1f04fd941 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -131,6 +131,10 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { return SubclassExhaustiveStrategyGem.valueOf( mapper.subclassExhaustiveStrategy().getDefaultValue() ); } + public TypeMirror getSubclassExhaustiveException() { + return mapper.subclassExhaustiveException().getDefaultValue(); + } + public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { NullValueMappingStrategyGem nullValueIterableMappingStrategy = options.getNullValueIterableMappingStrategy(); if ( nullValueIterableMappingStrategy != null ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java index 50c1d84541..34478969fa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DelegatingOptions.java @@ -106,6 +106,10 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { return next.getSubclassExhaustiveStrategy(); } + public TypeMirror getSubclassExhaustiveException() { + return next.getSubclassExhaustiveException(); + } + public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { return next.getNullValueIterableMappingStrategy(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java index e3ca6162a5..d606658878 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -141,6 +141,12 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { next().getSubclassExhaustiveStrategy(); } + public TypeMirror getSubclassExhaustiveException() { + return mapperConfig.subclassExhaustiveException().hasValue() ? + mapperConfig.subclassExhaustiveException().get() : + next().getSubclassExhaustiveException(); + } + @Override public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { if ( mapperConfig.nullValueIterableMappingStrategy().hasValue() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index ed1af34f79..9c2203efd5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -170,6 +170,13 @@ public SubclassExhaustiveStrategyGem getSubclassExhaustiveStrategy() { next().getSubclassExhaustiveStrategy(); } + @Override + public TypeMirror getSubclassExhaustiveException() { + return mapper.subclassExhaustiveException().hasValue() ? + mapper.subclassExhaustiveException().get() : + next().getSubclassExhaustiveException(); + } + @Override public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { if ( mapper.nullValueIterableMappingStrategy().hasValue() ) { diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 3036e4a2c8..da590bb506 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -42,7 +42,7 @@ else { <#if isAbstractReturnType()> - throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for " + ${subclassMappings[0].sourceArgument}.getClass()); + throw new <@includeModel object=subclassExhaustiveException />("Not all subclasses are supported for this mapping. Missing for " + ${subclassMappings[0].sourceArgument}.getClass()); <#else> <#if !existingInstanceMapping> <#if hasConstructorMappings()> diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomExceptionSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomExceptionSubclassMapper.java new file mode 100644 index 0000000000..52738ef6f4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomExceptionSubclassMapper.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.ap.test.subclassmapping.abstractsuperclass; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface CustomExceptionSubclassMapper { + CustomExceptionSubclassMapper INSTANCE = Mappers.getMapper( CustomExceptionSubclassMapper.class ); + + @BeanMapping(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION, + subclassExhaustiveException = CustomSubclassMappingException.class) + @SubclassMapping(source = Car.class, target = CarDto.class) + @SubclassMapping(source = Bike.class, target = BikeDto.class) + VehicleDto map(AbstractVehicle vehicle); + + VehicleCollectionDto mapInverse(VehicleCollection vehicles); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomSubclassMappingException.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomSubclassMappingException.java new file mode 100644 index 0000000000..abb675bbb4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomSubclassMappingException.java @@ -0,0 +1,12 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +public class CustomSubclassMappingException extends RuntimeException { + public CustomSubclassMappingException(String message) { + super( message ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomSubclassMappingExceptionTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomSubclassMappingExceptionTest.java new file mode 100644 index 0000000000..472c007269 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/CustomSubclassMappingExceptionTest.java @@ -0,0 +1,76 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +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.assertThatThrownBy; + +@IssueKey("3821") +@WithClasses({ + Bike.class, + BikeDto.class, + Car.class, + CarDto.class, + Motorcycle.class, + VehicleCollection.class, + VehicleCollectionDto.class, + AbstractVehicle.class, + VehicleDto.class, + CustomSubclassMappingException.class, + CustomExceptionSubclassMapper.class +}) +public class CustomSubclassMappingExceptionTest { + + private static final String EXPECTED_ERROR_MESSAGE = "Not all subclasses are supported for this mapping. " + + "Missing for class org.mapstruct.ap.test.subclassmapping.abstractsuperclass.Motorcycle"; + + @ProcessorTest + void customExceptionIsThrownForUnknownSubclass() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Car() ); + vehicles.getVehicles().add( new Motorcycle() ); // undefine subclass + + assertThatThrownBy( () -> CustomExceptionSubclassMapper.INSTANCE.mapInverse( vehicles ) ) + .isInstanceOf( CustomSubclassMappingException.class ) + .hasMessage( EXPECTED_ERROR_MESSAGE ); + } + + @ProcessorTest + void customExceptionIsThrownForSingleVehicle() { + AbstractVehicle vehicle = new Motorcycle(); // undefine subclass + + assertThatThrownBy( () -> CustomExceptionSubclassMapper.INSTANCE.map( vehicle ) ) + .isInstanceOf( CustomSubclassMappingException.class ) + .hasMessage( EXPECTED_ERROR_MESSAGE ); + } + + @ProcessorTest + @WithClasses({ MapperConfigSubclassMapper.class }) + void customExceptionIsThrownForMapperConfig() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Car() ); + vehicles.getVehicles().add( new Motorcycle() ); // undefined subclass + + assertThatThrownBy( () -> MapperConfigSubclassMapper.INSTANCE.mapInverse( vehicles ) ) + .isInstanceOf( CustomSubclassMappingException.class ) + .hasMessage( EXPECTED_ERROR_MESSAGE ); + } + + @ProcessorTest + @WithClasses({ MapperSubclassMapper.class }) + void customExceptionIsThrownForMapper() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Car() ); + vehicles.getVehicles().add( new Motorcycle() ); // undefined subclass + + assertThatThrownBy( () -> MapperSubclassMapper.INSTANCE.mapInverse( vehicles ) ) + .isInstanceOf( CustomSubclassMappingException.class ) + .hasMessage( EXPECTED_ERROR_MESSAGE ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MapperConfigSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MapperConfigSubclassMapper.java new file mode 100644 index 0000000000..b3c1a66bb6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MapperConfigSubclassMapper.java @@ -0,0 +1,29 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +import org.mapstruct.Mapper; +import org.mapstruct.MapperConfig; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper(config = MapperConfigSubclassMapper.Config.class) +public interface MapperConfigSubclassMapper { + + MapperConfigSubclassMapper INSTANCE = Mappers.getMapper( MapperConfigSubclassMapper.class ); + + @MapperConfig(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION, + subclassExhaustiveException = CustomSubclassMappingException.class) + interface Config { + } + + @SubclassMapping(source = Car.class, target = CarDto.class) + @SubclassMapping(source = Bike.class, target = BikeDto.class) + VehicleDto map(AbstractVehicle vehicle); + + VehicleCollectionDto mapInverse(VehicleCollection vehicles); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MapperSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MapperSubclassMapper.java new file mode 100644 index 0000000000..34ed3fde22 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/abstractsuperclass/MapperSubclassMapper.java @@ -0,0 +1,24 @@ +/* + * 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.subclassmapping.abstractsuperclass; + +import org.mapstruct.Mapper; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION, + subclassExhaustiveException = CustomSubclassMappingException.class) +public interface MapperSubclassMapper { + + MapperSubclassMapper INSTANCE = Mappers.getMapper( MapperSubclassMapper.class ); + + @SubclassMapping(source = Car.class, target = CarDto.class) + @SubclassMapping(source = Bike.class, target = BikeDto.class) + VehicleDto map(AbstractVehicle vehicle); + + VehicleCollectionDto mapInverse(VehicleCollection vehicles); +} From 05f27e96e2108344d69a2963b04ee973c83b0eb0 Mon Sep 17 00:00:00 2001 From: Dennis Melzer Date: Sun, 25 May 2025 14:58:23 +0200 Subject: [PATCH 485/597] #3852 Initialize Optionals with empty instead of null --- .../ap/internal/model/common/Type.java | 17 +++++ .../nullvalue/OptionalDefaultMapper.java | 20 ++++++ .../nullvalue/OptionalDefaultMapperTest.java | 53 ++++++++++++++++ .../nullvalue/SimpleConstructorMapper.java | 29 +++++++++ .../ap/test/optional/nullvalue/Source.java | 61 ++++++++++++++++++ .../ap/test/optional/nullvalue/Target.java | 63 +++++++++++++++++++ 6 files changed, 243 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapperTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/SimpleConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Target.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 6520834066..d0f65ce97a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -17,6 +17,10 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -367,6 +371,15 @@ public boolean isArrayType() { return componentType != null; } + private boolean isType(Class type) { + return type.getName().equals( getFullyQualifiedName() ); + } + + private boolean isOptionalType() { + return isType( Optional.class ) || isType( OptionalInt.class ) || isType( OptionalDouble.class ) || + isType( OptionalLong.class ); + } + public boolean isTypeVar() { return (typeMirror.getKind() == TypeKind.TYPEVAR); } @@ -1166,6 +1179,10 @@ else if ( !method.getModifiers().contains( Modifier.PUBLIC ) ) { * FTL. */ public String getNull() { + if ( isOptionalType() ) { + return createReferenceName() + ".empty()"; + } + if ( !isPrimitive() || isArrayType() ) { return "null"; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapper.java new file mode 100644 index 0000000000..7397c2ccd1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapper.java @@ -0,0 +1,20 @@ +/* + * 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.optional.nullvalue; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Dennis Melzer + */ +@Mapper +public interface OptionalDefaultMapper { + + OptionalDefaultMapper INSTANCE = Mappers.getMapper( OptionalDefaultMapper.class ); + + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapperTest.java new file mode 100644 index 0000000000..78482831b9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/OptionalDefaultMapperTest.java @@ -0,0 +1,53 @@ +/* + * 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.optional.nullvalue; + +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 Dennis Melzer + */ +@WithClasses({ + OptionalDefaultMapper.class, + Source.class, + Target.class +}) +@IssueKey("3852") +public class OptionalDefaultMapperTest { + + @ProcessorTest + public void shouldOptionalNotNull() { + Source source = new Source( null, null, null, null, null, null, null ); + Target target = OptionalDefaultMapper.INSTANCE.map( source ); + + assertThat( target.getSomeString() ).isEmpty(); + assertThat( target.getSomeInteger() ).isEmpty(); + assertThat( target.getSomeDouble() ).isEmpty(); + assertThat( target.getSomeBoolean() ).isEmpty(); + assertThat( target.getSomeIntValue() ).isEmpty(); + assertThat( target.getSomeDoubleValue() ).isEmpty(); + assertThat( target.getSomeLongValue() ).isEmpty(); + } + + @ProcessorTest + public void shouldMapOptional() { + Source source = new Source( "someString", 10, 11D, Boolean.TRUE, 10, 100D, 200L ); + Target target = OptionalDefaultMapper.INSTANCE.map( source ); + + assertThat( target.getSomeString() ).contains( "someString" ); + assertThat( target.getSomeInteger() ).contains( 10 ); + assertThat( target.getSomeDouble() ).contains( 11D ); + assertThat( target.getSomeBoolean() ).contains( Boolean.TRUE ); + assertThat( target.getSomeIntValue() ).hasValue( 10 ); + assertThat( target.getSomeDoubleValue() ).hasValue( 100 ); + assertThat( target.getSomeLongValue() ).hasValue( 200 ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/SimpleConstructorMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/SimpleConstructorMapper.java new file mode 100644 index 0000000000..158a01d3bd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/SimpleConstructorMapper.java @@ -0,0 +1,29 @@ +/* + * 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.optional.nullvalue; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.NullValueMappingStrategy; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.ap.test.nestedproperties.simple._target.TargetObject; +import org.mapstruct.ap.test.nestedproperties.simple.source.SourceRoot; +import org.mapstruct.factory.Mappers; + +/** + * @author Dennis Melzer + */ +@Mapper( + nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT, + injectionStrategy = InjectionStrategy.CONSTRUCTOR, unmappedTargetPolicy = ReportingPolicy.IGNORE +) +public interface SimpleConstructorMapper { + + SimpleConstructorMapper MAPPER = Mappers.getMapper( SimpleConstructorMapper.class ); + + TargetObject toTargetObject(SourceRoot sourceRoot); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Source.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Source.java new file mode 100644 index 0000000000..9746d4a152 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Source.java @@ -0,0 +1,61 @@ +/* + * 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.optional.nullvalue; + +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +public class Source { + + private final String someString; + private final Integer someInteger; + private final Double someDouble; + private final Boolean someBoolean; + private final Integer someIntValue; + private final Double someDoubleValue; + private final Long someLongValue; + + public Source(String someString, Integer someInteger, Double someDouble, Boolean someBoolean, Integer someIntValue, + Double someDoubleValue, Long someLongValue) { + this.someString = someString; + this.someInteger = someInteger; + this.someDouble = someDouble; + this.someBoolean = someBoolean; + this.someIntValue = someIntValue; + this.someDoubleValue = someDoubleValue; + this.someLongValue = someLongValue; + } + + public Optional getSomeString() { + return Optional.ofNullable( someString ); + } + + public Optional getSomeInteger() { + return Optional.ofNullable( someInteger ); + } + + public Optional getSomeDouble() { + return Optional.ofNullable( someDouble ); + } + + public Optional getSomeBoolean() { + return Optional.ofNullable( someBoolean ); + } + + public OptionalDouble getSomeDoubleValue() { + return someDouble != null ? OptionalDouble.of( someDoubleValue ) : OptionalDouble.empty(); + } + + public OptionalInt getSomeIntValue() { + return someIntValue != null ? OptionalInt.of( someIntValue ) : OptionalInt.empty(); + } + + public OptionalLong getSomeLongValue() { + return someLongValue != null ? OptionalLong.of( someLongValue ) : OptionalLong.empty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Target.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Target.java new file mode 100644 index 0000000000..5fd5d1730b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/Target.java @@ -0,0 +1,63 @@ +/* + * 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.optional.nullvalue; + +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class Target { + + private final Optional someString; + private final Optional someInteger; + private final Optional someDouble; + private final Optional someBoolean; + private final OptionalInt someIntValue; + private final OptionalDouble someDoubleValue; + private final OptionalLong someLongValue; + + public Target(Optional someString, Optional someInteger, Optional someDouble, + Optional someBoolean, OptionalInt someIntValue, OptionalDouble someDoubleValue, + OptionalLong someLongValue) { + this.someString = someString; + this.someInteger = someInteger; + this.someDouble = someDouble; + this.someBoolean = someBoolean; + this.someIntValue = someIntValue; + this.someDoubleValue = someDoubleValue; + this.someLongValue = someLongValue; + } + + public Optional getSomeString() { + return someString; + } + + public Optional getSomeInteger() { + return someInteger; + } + + public Optional getSomeDouble() { + return someDouble; + } + + public Optional getSomeBoolean() { + return someBoolean; + } + + public OptionalLong getSomeLongValue() { + return someLongValue; + } + + public OptionalDouble getSomeDoubleValue() { + return someDoubleValue; + } + + public OptionalInt getSomeIntValue() { + return someIntValue; + } +} From 42c87d1da99252e21699cde7d584538b5c961ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Eamil=20Can?= Date: Sun, 25 May 2025 15:22:47 +0200 Subject: [PATCH 486/597] #3848: Mark String to number as lossy conversion --- .../ap/internal/util/NativeTypes.java | 1 + .../conversion/lossy/CutleryInventoryDto.java | 9 +++++++++ .../lossy/CutleryInventoryEntity.java | 9 +++++++++ .../lossy/ErroneousKitchenDrawerMapper6.java | 20 +++++++++++++++++++ .../conversion/lossy/LossyConversionTest.java | 15 ++++++++++++++ .../lossy/OversizedKitchenDrawerDto.java | 8 ++++++++ .../lossy/RegularKitchenDrawerEntity.java | 8 ++++++++ 7 files changed, 70 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerMapper6.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java index 0a4ca0cde0..ff01ae0ef3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/NativeTypes.java @@ -474,6 +474,7 @@ private NativeTypes() { tmp3.put( Double.class.getName(), 6 ); tmp3.put( BigInteger.class.getName(), 50 ); tmp3.put( BigDecimal.class.getName(), 51 ); + tmp3.put( String.class.getName(), 51 ); NARROWING_LUT = Collections.unmodifiableMap( tmp3 ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/CutleryInventoryDto.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/CutleryInventoryDto.java index 5a96d6deb2..3bc144511e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/CutleryInventoryDto.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/CutleryInventoryDto.java @@ -10,6 +10,7 @@ public class CutleryInventoryDto { private short numberOfKnifes; private int numberOfForks; private byte numberOfSpoons; + private int drawerId; private float approximateKnifeLength; @@ -44,4 +45,12 @@ public float getApproximateKnifeLength() { public void setApproximateKnifeLength(float approximateKnifeLength) { this.approximateKnifeLength = approximateKnifeLength; } + + public int getDrawerId() { + return drawerId; + } + + public void setDrawerId(int drawerId) { + this.drawerId = drawerId; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/CutleryInventoryEntity.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/CutleryInventoryEntity.java index 9575ed99dc..755dbc3637 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/CutleryInventoryEntity.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/CutleryInventoryEntity.java @@ -10,6 +10,7 @@ public class CutleryInventoryEntity { private int numberOfKnifes; private Long numberOfForks; private short numberOfSpoons; + private String drawerId; private double approximateKnifeLength; @@ -44,4 +45,12 @@ public double getApproximateKnifeLength() { public void setApproximateKnifeLength(double approximateKnifeLength) { this.approximateKnifeLength = approximateKnifeLength; } + + public String getDrawerId() { + return drawerId; + } + + public void setDrawerId(String drawerId) { + this.drawerId = drawerId; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerMapper6.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerMapper6.java new file mode 100644 index 0000000000..c33f1bd662 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerMapper6.java @@ -0,0 +1,20 @@ +/* + * 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.conversion.lossy; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +@Mapper( typeConversionPolicy = ReportingPolicy.ERROR ) +public interface ErroneousKitchenDrawerMapper6 { + + @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "drawerId", source = "drawerId" ) + RegularKitchenDrawerEntity map( OversizedKitchenDrawerDto dto ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java index d27f04374a..629f727273 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java @@ -40,12 +40,14 @@ public void testNoErrorCase() { dto.setNumberOfKnifes( (short) 7 ); dto.setNumberOfSpoons( (byte) 3 ); dto.setApproximateKnifeLength( 3.7f ); + dto.setDrawerId( 1 ); CutleryInventoryEntity entity = CutleryInventoryMapper.INSTANCE.map( dto ); assertThat( entity.getNumberOfForks() ).isEqualTo( 5L ); assertThat( entity.getNumberOfKnifes() ).isEqualTo( 7 ); assertThat( entity.getNumberOfSpoons() ).isEqualTo( (short) 3 ); assertThat( entity.getApproximateKnifeLength() ).isCloseTo( 3.7d, withinPercentage( 0.0001d ) ); + assertThat( entity.getDrawerId() ).isEqualTo( "1" ); } @ProcessorTest @@ -74,6 +76,19 @@ public void testConversionFromLongToInt() { public void testConversionFromBigIntegerToInteger() { } + @ProcessorTest + @WithClasses(ErroneousKitchenDrawerMapper6.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousKitchenDrawerMapper6.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + message = "Can't map property \"String drawerId\". It has a possibly lossy conversion from " + + "String to int.") + }) + public void testConversionFromStringToInt() { + } + @ProcessorTest @WithClasses(ErroneousKitchenDrawerMapper3.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/OversizedKitchenDrawerDto.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/OversizedKitchenDrawerDto.java index 110bb2f07c..c42b24cc57 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/OversizedKitchenDrawerDto.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/OversizedKitchenDrawerDto.java @@ -21,6 +21,7 @@ public class OversizedKitchenDrawerDto { private Double depth; private BigDecimal length; private double height; + private String drawerId; public long getNumberOfForks() { return numberOfForks; @@ -70,4 +71,11 @@ public void setHeight(double height) { this.height = height; } + public String getDrawerId() { + return drawerId; + } + + public void setDrawerId(String drawerId) { + this.drawerId = drawerId; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/RegularKitchenDrawerEntity.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/RegularKitchenDrawerEntity.java index d49a89052d..e3ae10bc1f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/RegularKitchenDrawerEntity.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/RegularKitchenDrawerEntity.java @@ -17,6 +17,7 @@ public class RegularKitchenDrawerEntity { private float depth; private Float length; private VerySpecialNumber height; + private int drawerId; public int getNumberOfForks() { return numberOfForks; @@ -66,4 +67,11 @@ public void setHeight(VerySpecialNumber height) { this.height = height; } + public int getDrawerId() { + return drawerId; + } + + public void setDrawerId(int drawerId) { + this.drawerId = drawerId; + } } From 3a5c70224d60aaa834986c5ff88fcb568df9fc85 Mon Sep 17 00:00:00 2001 From: Yang Tang Date: Sun, 25 May 2025 21:40:43 +0800 Subject: [PATCH 487/597] #3809 Fix conditional mapping with `@TargetPropertyName` failing for nested update mappings Signed-off-by: TangYang --- .../model/assignment/UpdateWrapper.ftl | 15 +++- .../ap/test/bugs/_3809/Issue3809Mapper.java | 69 +++++++++++++++++++ .../ap/test/bugs/_3809/Issue3809Test.java | 20 ++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3809/Issue3809Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3809/Issue3809Test.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl index 9cdd07c230..ea8eed2438 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl @@ -10,7 +10,7 @@ <@lib.handleExceptions> <#if includeSourceNullCheck> <@lib.sourceLocalVarAssignment/> - if ( <#if sourcePresenceCheckerReference?? ><@includeModel object=sourcePresenceCheckerReference /><#else><#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference} != null ) { + if ( <@handleSourceReferenceNullCheck/> ) { <@assignToExistingTarget/> <@lib.handleAssignment/>; } @@ -32,3 +32,16 @@ ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.initTargetObject/>; } + +<#macro handleSourceReferenceNullCheck> + <@compress single_line=true> + <#if sourcePresenceCheckerReference?? > + <@includeModel object=sourcePresenceCheckerReference + targetPropertyName=ext.targetPropertyName + sourcePropertyName=ext.sourcePropertyName + targetType=ext.targetType/> + <#else> + <#if sourceLocalVarName??> ${sourceLocalVarName} <#else> ${sourceReference} != null + + + diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3809/Issue3809Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3809/Issue3809Mapper.java new file mode 100644 index 0000000000..10ff9e2628 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3809/Issue3809Mapper.java @@ -0,0 +1,69 @@ +/* + * 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._3809; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.TargetPropertyName; + +@Mapper +public interface Issue3809Mapper { + void updateMappingFails(Source source, @MappingTarget Target target); + + @Condition + default boolean canMap(Object source, @TargetPropertyName String propertyName) { + return true; + } + + class Source { + private NestedSource param; + + public NestedSource getParam() { + return param; + } + } + + class NestedSource { + private String param1; + + public String getParam1() { + return param1; + } + + public void setParam1(String param1) { + this.param1 = param1; + } + + } + + class Target { + + private NestedTarget param; + + public NestedTarget getParam() { + return param; + } + + public void setParam(NestedTarget param) { + this.param = param; + } + + } + + class NestedTarget { + private String param1; + + public String getParam1() { + return param1; + } + + public void setParam1(String param1) { + this.param1 = param1; + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3809/Issue3809Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3809/Issue3809Test.java new file mode 100644 index 0000000000..23d97b877c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3809/Issue3809Test.java @@ -0,0 +1,20 @@ +/* + * 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._3809; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +@WithClasses(Issue3809Mapper.class) +@IssueKey("3809") +public class Issue3809Test { + + @ProcessorTest + public void shouldCompileNoError() { + + } +} From 0badba70038875cb2fdf6d9ff328b6a9ba31d245 Mon Sep 17 00:00:00 2001 From: Yang Tang Date: Sun, 25 May 2025 22:35:38 +0800 Subject: [PATCH 488/597] #3849: Resolve duplicate invocation of overloaded lifecycle methods with inheritance Add compiler option `mapstruct.disableLifecycleOverloadDeduplicateSelector` to disable the deduplication if needed. Signed-off-by: TangYang --- .../org/mapstruct/ap/MappingProcessor.java | 8 +- .../model/LifecycleMethodResolver.java | 2 +- .../model/ObjectFactoryMethodResolver.java | 7 +- .../model/PresenceCheckMethodResolver.java | 3 +- .../LifecycleOverloadDeduplicateSelector.java | 129 +++++++++++++++ .../source/selector/MethodSelectors.java | 16 +- .../mapstruct/ap/internal/option/Options.java | 9 +- .../creation/MappingResolverImpl.java | 2 +- .../mapstruct/ap/test/bugs/_3849/Child.java | 12 ++ .../ap/test/bugs/_3849/ChildDto.java | 16 ++ .../bugs/_3849/DeduplicateBySourceMapper.java | 69 ++++++++ .../bugs/_3849/DeduplicateByTargetMapper.java | 69 ++++++++ .../DeduplicateForCompileArgsMapper.java | 69 ++++++++ .../bugs/_3849/DeduplicateGenericMapper.java | 51 ++++++ .../ap/test/bugs/_3849/Issue3849Test.java | 150 ++++++++++++++++++ .../mapstruct/ap/test/bugs/_3849/Parent.java | 21 +++ .../ap/test/bugs/_3849/ParentDto.java | 22 +++ 17 files changed, 642 insertions(+), 13 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/LifecycleOverloadDeduplicateSelector.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Child.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/ChildDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateBySourceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateByTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateForCompileArgsMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateGenericMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Issue3849Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Parent.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/ParentDto.java diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 407166f6fa..b34b133262 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -95,6 +95,7 @@ MappingProcessor.VERBOSE, MappingProcessor.NULL_VALUE_ITERABLE_MAPPING_STRATEGY, MappingProcessor.NULL_VALUE_MAP_MAPPING_STRATEGY, + MappingProcessor.DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR, }) public class MappingProcessor extends AbstractProcessor { @@ -115,6 +116,8 @@ public class MappingProcessor extends AbstractProcessor { protected static final String VERBOSE = "mapstruct.verbose"; protected static final String NULL_VALUE_ITERABLE_MAPPING_STRATEGY = "mapstruct.nullValueIterableMappingStrategy"; protected static final String NULL_VALUE_MAP_MAPPING_STRATEGY = "mapstruct.nullValueMapMappingStrategy"; + protected static final String DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR = + "mapstruct.disableLifecycleOverloadDeduplicateSelector"; private final Set additionalSupportedOptions; private final String additionalSupportedOptionsError; @@ -174,6 +177,8 @@ private Options createOptions() { String nullValueIterableMappingStrategy = processingEnv.getOptions() .get( NULL_VALUE_ITERABLE_MAPPING_STRATEGY ); String nullValueMapMappingStrategy = processingEnv.getOptions().get( NULL_VALUE_MAP_MAPPING_STRATEGY ); + String disableLifecycleOverloadDeduplicateSelector = processingEnv.getOptions() + .get( DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR ); return new Options( Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ), @@ -189,7 +194,8 @@ private Options createOptions() { NullValueMappingStrategyGem.valueOf( nullValueIterableMappingStrategy.toUpperCase( Locale.ROOT ) ) : null, nullValueMapMappingStrategy != null ? - NullValueMappingStrategyGem.valueOf( nullValueMapMappingStrategy.toUpperCase( Locale.ROOT ) ) : null + NullValueMappingStrategyGem.valueOf( nullValueMapMappingStrategy.toUpperCase( Locale.ROOT ) ) : null, + Boolean.parseBoolean( disableLifecycleOverloadDeduplicateSelector ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java index b713fa5f0c..cfe4f9f8b7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java @@ -133,7 +133,7 @@ private static List collectLifecycleCallbackMe MappingBuilderContext ctx, Set existingVariableNames) { MethodSelectors selectors = - new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager() ); + new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager(), ctx.getOptions() ); List> matchingMethods = selectors.getMatchingMethods( callbackMethods, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index 4cf653b46b..89f295981f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -5,12 +5,9 @@ */ package org.mapstruct.ap.internal.model; -import static org.mapstruct.ap.internal.util.Collections.first; - import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; - import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -26,6 +23,8 @@ import org.mapstruct.ap.internal.model.source.selector.SelectionContext; import org.mapstruct.ap.internal.util.Message; +import static org.mapstruct.ap.internal.util.Collections.first; + /** * * @author Sjaak Derksen @@ -126,7 +125,7 @@ public static List> getMatchingFactoryMethods( Meth MappingBuilderContext ctx) { MethodSelectors selectors = - new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager() ); + new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager(), null ); return selectors.getMatchingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java index 5906db8219..96826f8f66 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java @@ -119,7 +119,8 @@ private static List> findMatchingMethods( MethodSelectors selectors = new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), - ctx.getMessager() + ctx.getMessager(), + null ); return selectors.getMatchingMethods( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/LifecycleOverloadDeduplicateSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/LifecycleOverloadDeduplicateSelector.java new file mode 100644 index 0000000000..7ce23af186 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/LifecycleOverloadDeduplicateSelector.java @@ -0,0 +1,129 @@ +/* + * 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.internal.model.source.selector; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.stream.Collectors; + +import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.ParameterBinding; +import org.mapstruct.ap.internal.model.source.Method; + +/** + * Selector for deduplicating overloaded lifecycle callback methods + * whose parameter signatures differ only by type hierarchy. + *

      + * In the context of lifecycle callback method selection + * (such as @BeforeMapping or @AfterMapping), it is possible to have multiple overloaded methods + * whose parameter lists are structurally identical except for the specific types, + * where those types are related by inheritance (e.g., one parameter is a superclass or subclass of another). + *

      + * This selector groups such methods by their effective parameter signature + * (ignoring differences only in type hierarchy), and, within each group, + * retains only the method whose parameter types have the closest inheritance distance + * to the actual invocation types. + * This ensures that, for each group of nearly identical overloads, + * only the most specific and appropriate method is selected. + *

      + * Example (see Issue3849Test): + * + *

      {@code
      + * @AfterMapping
      + * default void afterMapping(Parent source, @MappingTarget ParentDto target) { ... }
      + * @AfterMapping
      + * default void afterMapping(Parent source, @MappingTarget ChildDto target) { ... }
      + * }
      + * When mapping a Child to a ChildDto, + * only the method with ChildDto is selected, even though both methods match by signature + * except for the target type's inheritance relationship. + */ +public class LifecycleOverloadDeduplicateSelector implements MethodSelector { + @Override + public List> getMatchingMethods(List> methods, + SelectionContext context) { + if ( !context.getSelectionCriteria().isLifecycleCallbackRequired() || methods.size() <= 1 ) { + return methods; + } + Collection>> methodSignatureGroups = + methods.stream() + .collect( Collectors.groupingBy( + LifecycleOverloadDeduplicateSelector::buildSignatureKey, + LinkedHashMap::new, + Collectors.toList() + ) ) + .values(); + List> deduplicatedMethods = new ArrayList<>( methods.size() ); + for ( List> signatureGroup : methodSignatureGroups ) { + if ( signatureGroup.size() == 1 ) { + deduplicatedMethods.add( signatureGroup.get( 0 ) ); + continue; + } + SelectedMethod bestInheritanceMethod = signatureGroup.get( 0 ); + for ( int i = 1; i < signatureGroup.size(); i++ ) { + SelectedMethod candidateMethod = signatureGroup.get( i ); + if ( isInheritanceBetter( candidateMethod, bestInheritanceMethod ) ) { + bestInheritanceMethod = candidateMethod; + } + } + deduplicatedMethods.add( bestInheritanceMethod ); + } + return deduplicatedMethods; + } + + /** + * Builds a grouping key for a method based on its defining type, + * method name, and a detailed breakdown of each parameter binding. + *

      + * The key consists of: + *

        + *
      • The type that defines the method
      • + *
      • The method name
      • + *
      • parameter bindings
      • + *
      + * This ensures that methods are grouped together only if all these aspects match, + * except for differences in type hierarchy, which are handled separately. + */ + private static List buildSignatureKey(SelectedMethod method) { + List key = new ArrayList<>(); + key.add( method.getMethod().getDefiningType() ); + key.add( method.getMethod().getName() ); + for ( ParameterBinding binding : method.getParameterBindings() ) { + key.add( binding.getType() ); + key.add( binding.getVariableName() ); + } + return key; + } + + /** + * Compare the inheritance distance of parameters between two methods to determine if candidateMethod is better. + * Compares parameters in order, returns as soon as a better one is found. + */ + private boolean isInheritanceBetter(SelectedMethod candidateMethod, + SelectedMethod currentBestMethod) { + List candidateBindings = candidateMethod.getParameterBindings(); + List bestBindings = currentBestMethod.getParameterBindings(); + List candidateParams = candidateMethod.getMethod().getParameters(); + List bestParams = currentBestMethod.getMethod().getParameters(); + int paramCount = candidateBindings.size(); + + for ( int i = 0; i < paramCount; i++ ) { + int candidateDistance = candidateBindings.get( i ) + .getType() + .distanceTo( candidateParams.get( i ).getType() ); + int bestDistance = bestBindings.get( i ).getType().distanceTo( bestParams.get( i ).getType() ); + if ( candidateDistance < bestDistance ) { + return true; + } + else if ( candidateDistance > bestDistance ) { + return false; + } + } + return false; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java index c14729a90f..774f25a8c3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MethodSelectors.java @@ -10,6 +10,7 @@ import java.util.List; import org.mapstruct.ap.internal.model.source.Method; +import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.TypeUtils; @@ -24,20 +25,27 @@ public class MethodSelectors { private final List selectors; public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils, - FormattingMessager messager) { - selectors = Arrays.asList( + FormattingMessager messager, Options options) { + List selectorList = new ArrayList<>( Arrays.asList( new MethodFamilySelector(), new TypeSelector( messager ), new QualifierSelector( typeUtils, elementUtils ), new TargetTypeSelector( typeUtils ), new JavaxXmlElementDeclSelector( typeUtils ), new JakartaXmlElementDeclSelector( typeUtils ), - new InheritanceSelector(), + new InheritanceSelector() + ) ); + if ( options != null && !options.isDisableLifecycleOverloadDeduplicateSelector() ) { + selectorList.add( new LifecycleOverloadDeduplicateSelector() ); + } + + selectorList.addAll( Arrays.asList( new CreateOrUpdateSelector(), new SourceRhsSelector(), new FactoryParameterSelector(), new MostSpecificResultTypeSelector() - ); + ) ); + this.selectors = selectorList; } /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java index a544374c0e..095c472c87 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java @@ -26,6 +26,7 @@ public class Options { private final boolean verbose; private final NullValueMappingStrategyGem nullValueIterableMappingStrategy; private final NullValueMappingStrategyGem nullValueMapMappingStrategy; + private final boolean disableLifecycleOverloadDeduplicateSelector; //CHECKSTYLE:OFF public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVersionComment, @@ -36,7 +37,8 @@ public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVers boolean disableBuilders, boolean verbose, NullValueMappingStrategyGem nullValueIterableMappingStrategy, - NullValueMappingStrategyGem nullValueMapMappingStrategy + NullValueMappingStrategyGem nullValueMapMappingStrategy, + boolean disableLifecycleOverloadDeduplicateSelector ) { //CHECKSTYLE:ON this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; @@ -50,6 +52,7 @@ public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVers this.verbose = verbose; this.nullValueIterableMappingStrategy = nullValueIterableMappingStrategy; this.nullValueMapMappingStrategy = nullValueMapMappingStrategy; + this.disableLifecycleOverloadDeduplicateSelector = disableLifecycleOverloadDeduplicateSelector; } public boolean isSuppressGeneratorTimestamp() { @@ -95,4 +98,8 @@ public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { public NullValueMappingStrategyGem getNullValueMapMappingStrategy() { return nullValueMapMappingStrategy; } + + public boolean isDisableLifecycleOverloadDeduplicateSelector() { + return disableLifecycleOverloadDeduplicateSelector; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index d84ba974db..ba903b6048 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -116,7 +116,7 @@ public MappingResolverImpl(FormattingMessager messager, ElementUtils elementUtil this.conversions = new Conversions( typeFactory ); this.builtInMethods = new BuiltInMappingMethods( typeFactory ); - this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, messager ); + this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, messager, null ); this.verboseLogging = verboseLogging; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Child.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Child.java new file mode 100644 index 0000000000..393cb16970 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Child.java @@ -0,0 +1,12 @@ +/* + * 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._3849; + +public class Child extends Parent { + public Child() { + super(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/ChildDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/ChildDto.java new file mode 100644 index 0000000000..e8cdae4d17 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/ChildDto.java @@ -0,0 +1,16 @@ +/* + * 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._3849; + +public class ChildDto extends ParentDto { + public ChildDto(String value) { + super( value ); + } + + public void setValue(String value) { + super.setValue( value ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateBySourceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateBySourceMapper.java new file mode 100644 index 0000000000..68450c7d9d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateBySourceMapper.java @@ -0,0 +1,69 @@ +/* + * 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._3849; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface DeduplicateBySourceMapper { + + DeduplicateBySourceMapper INSTANCE = Mappers.getMapper( DeduplicateBySourceMapper.class ); + List INVOKED_METHODS = new ArrayList<>(); + + ParentDto mapParent(Parent source, @Context MappingContext context); + + ParentDto mapChild(Child source, @Context MappingContext context); + + class MappingContext { + @BeforeMapping + void deduplicateBySourceForBefore(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "beforeMappingParentSourceInOtherClass" ); + } + + @BeforeMapping + void deduplicateBySourceForBefore(Child sourceChild, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "beforeMappingChildSourceInOtherClass" ); + } + + @AfterMapping + void deduplicateBySource(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "afterMappingParentSourceInOtherClass" ); + } + + @AfterMapping + void deduplicateBySource(Child sourceChild, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "afterMappingChildSourceInOtherClass" ); + } + } + + @BeforeMapping + default void deduplicateBySourceForBefore(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "beforeMappingParentSource" ); + } + + @BeforeMapping + default void deduplicateBySourceForBefore(Child sourceChild, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "beforeMappingChildSource" ); + } + + @AfterMapping + default void deduplicateBySource(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "afterMappingParentSource" ); + } + + @AfterMapping + default void deduplicateBySource(Child sourceChild, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "afterMappingChildSource" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateByTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateByTargetMapper.java new file mode 100644 index 0000000000..0aed8a2957 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateByTargetMapper.java @@ -0,0 +1,69 @@ +/* + * 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._3849; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface DeduplicateByTargetMapper { + + DeduplicateByTargetMapper INSTANCE = Mappers.getMapper( DeduplicateByTargetMapper.class ); + List INVOKED_METHODS = new ArrayList<>(); + + ParentDto mapParent(Parent source, @Context MappingContext context); + + ChildDto mapChild(Parent source, @Context MappingContext context); + + class MappingContext { + @BeforeMapping + void deduplicateByTargetForBefore(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "beforeMappingParentTargetInOtherClass" ); + } + + @BeforeMapping + void deduplicateByTargetForBefore(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "beforeMappingChildTargetInOtherClass" ); + } + + @AfterMapping + void deduplicateByTarget(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "afterMappingParentTargetInOtherClass" ); + } + + @AfterMapping + void deduplicateByTarget(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "afterMappingChildTargetInOtherClass" ); + } + } + + @BeforeMapping + default void deduplicateByTargetForBefore(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "beforeMappingParentTarget" ); + } + + @BeforeMapping + default void deduplicateByTargetForBefore(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "beforeMappingChildTarget" ); + } + + @AfterMapping + default void deduplicateByTarget(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "afterMappingParentTarget" ); + } + + @AfterMapping + default void deduplicateByTarget(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "afterMappingChildTarget" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateForCompileArgsMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateForCompileArgsMapper.java new file mode 100644 index 0000000000..276a2c4352 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateForCompileArgsMapper.java @@ -0,0 +1,69 @@ +/* + * 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._3849; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface DeduplicateForCompileArgsMapper { + + DeduplicateForCompileArgsMapper INSTANCE = Mappers.getMapper( DeduplicateForCompileArgsMapper.class ); + List INVOKED_METHODS = new ArrayList<>(); + + ParentDto mapParent(Parent source, @Context MappingContext context); + + ChildDto mapChild(Parent source, @Context MappingContext context); + + class MappingContext { + @BeforeMapping + void deduplicateByTargetForBefore(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "beforeMappingParentTargetInOtherClass" ); + } + + @BeforeMapping + void deduplicateByTargetForBefore(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "beforeMappingChildTargetInOtherClass" ); + } + + @AfterMapping + void deduplicateByTarget(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "afterMappingParentTargetInOtherClass" ); + } + + @AfterMapping + void deduplicateByTarget(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "afterMappingChildTargetInOtherClass" ); + } + } + + @BeforeMapping + default void deduplicateByTargetForBefore(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "beforeMappingParentTarget" ); + } + + @BeforeMapping + default void deduplicateByTargetForBefore(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "beforeMappingChildTarget" ); + } + + @AfterMapping + default void deduplicateByTarget(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "afterMappingParentTarget" ); + } + + @AfterMapping + default void deduplicateByTarget(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "afterMappingChildTarget" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateGenericMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateGenericMapper.java new file mode 100644 index 0000000000..662137f1a9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/DeduplicateGenericMapper.java @@ -0,0 +1,51 @@ +/* + * 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._3849; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface DeduplicateGenericMapper { + + DeduplicateGenericMapper INSTANCE = Mappers.getMapper( DeduplicateGenericMapper.class ); + List INVOKED_METHODS = new ArrayList<>(); + + ParentDto mapParent(Parent source); + + ChildDto mapChild(Parent source); + + @BeforeMapping + default void deduplicateBefore(Parent source, @MappingTarget T target) { + INVOKED_METHODS.add( "beforeMappingParentGeneric" ); + } + + @BeforeMapping + default void deduplicateBefore(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "beforeMappingChild" ); + } + + @AfterMapping + default void deduplicate(Parent source, @MappingTarget T target) { + INVOKED_METHODS.add( "afterMappingGeneric" ); + } + + @AfterMapping + default void deduplicate(Parent source, @MappingTarget ParentDto target) { + INVOKED_METHODS.add( "afterMappingParent" ); + } + + @AfterMapping + default void deduplicate(Parent source, @MappingTarget ChildDto target) { + INVOKED_METHODS.add( "afterMappingChild" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Issue3849Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Issue3849Test.java new file mode 100644 index 0000000000..a58c52c921 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Issue3849Test.java @@ -0,0 +1,150 @@ +/* + * 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._3849; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption; + +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("3849") +@WithClasses({ + Parent.class, + ParentDto.class, + Child.class, + ChildDto.class +}) +public class Issue3849Test { + + @ProcessorOption(name = "mapstruct.disableLifecycleOverloadDeduplicateSelector", value = "true") + @ProcessorTest() + @WithClasses(DeduplicateForCompileArgsMapper.class) + void lifecycleMappingOverloadSelectorDisableCompileArgs() { + Child child = new Child(); + Parent parent = new Parent(); + + DeduplicateForCompileArgsMapper.MappingContext mappingContext = + new DeduplicateForCompileArgsMapper.MappingContext(); + ParentDto parentDto = DeduplicateForCompileArgsMapper.INSTANCE.mapParent( parent, mappingContext ); + assertThat( DeduplicateForCompileArgsMapper.INVOKED_METHODS ) + .containsExactly( + "beforeMappingParentTargetInOtherClass", + "beforeMappingParentTarget", + "afterMappingParentTargetInOtherClass", + "afterMappingParentTarget" + ); + + DeduplicateForCompileArgsMapper.INVOKED_METHODS.clear(); + + ParentDto childDto = DeduplicateForCompileArgsMapper.INSTANCE.mapChild( child, mappingContext ); + + assertThat( DeduplicateForCompileArgsMapper.INVOKED_METHODS ) + .containsExactly( + "beforeMappingChildTargetInOtherClass", + "beforeMappingChildTargetInOtherClass", + "beforeMappingChildTarget", + "beforeMappingChildTarget", + "afterMappingChildTargetInOtherClass", + "afterMappingChildTargetInOtherClass", + "afterMappingChildTarget", + "afterMappingChildTarget" + ); + + DeduplicateForCompileArgsMapper.INVOKED_METHODS.clear(); + } + + @ProcessorTest() + @WithClasses( DeduplicateByTargetMapper.class ) + void lifecycleMappingOverloadByTarget() { + Child child = new Child(); + Parent parent = new Parent(); + + DeduplicateByTargetMapper.MappingContext mappingContext = new DeduplicateByTargetMapper.MappingContext(); + ParentDto parentDto = DeduplicateByTargetMapper.INSTANCE.mapParent( parent, mappingContext ); + assertThat( DeduplicateByTargetMapper.INVOKED_METHODS ) + .containsExactly( + "beforeMappingParentTargetInOtherClass", + "beforeMappingParentTarget", + "afterMappingParentTargetInOtherClass", + "afterMappingParentTarget" + ); + + DeduplicateByTargetMapper.INVOKED_METHODS.clear(); + + ParentDto childDto = DeduplicateByTargetMapper.INSTANCE.mapChild( child, mappingContext ); + + assertThat( DeduplicateByTargetMapper.INVOKED_METHODS ) + .containsExactly( + "beforeMappingChildTargetInOtherClass", + "beforeMappingChildTarget", + "afterMappingChildTargetInOtherClass", + "afterMappingChildTarget" + ); + + DeduplicateByTargetMapper.INVOKED_METHODS.clear(); + } + + @ProcessorTest + @WithClasses( DeduplicateBySourceMapper.class ) + void lifecycleMappingOverloadBySource() { + Child child = new Child(); + Parent parent = new Parent(); + + DeduplicateBySourceMapper.MappingContext mappingContext = new DeduplicateBySourceMapper.MappingContext(); + ParentDto parentDto = DeduplicateBySourceMapper.INSTANCE.mapParent( parent, mappingContext ); + assertThat( DeduplicateBySourceMapper.INVOKED_METHODS ) + .containsExactly( + "beforeMappingParentSourceInOtherClass", + "beforeMappingParentSource", + "afterMappingParentSourceInOtherClass", + "afterMappingParentSource" + ); + + DeduplicateBySourceMapper.INVOKED_METHODS.clear(); + + ParentDto childDto = DeduplicateBySourceMapper.INSTANCE.mapChild( child, mappingContext ); + + assertThat( DeduplicateBySourceMapper.INVOKED_METHODS ) + .containsExactly( + "beforeMappingChildSourceInOtherClass", + "beforeMappingChildSource", + "afterMappingChildSourceInOtherClass", + "afterMappingChildSource" + ); + + DeduplicateBySourceMapper.INVOKED_METHODS.clear(); + } + + @ProcessorTest + @WithClasses(DeduplicateGenericMapper.class) + void lifecycleMappingOverloadForGeneric() { + Child child = new Child(); + Parent parent = new Parent(); + + ParentDto parentDto = DeduplicateGenericMapper.INSTANCE.mapParent( parent ); + assertThat( DeduplicateGenericMapper.INVOKED_METHODS ) + .containsExactly( + "beforeMappingParentGeneric", + "afterMappingParent" + ); + + DeduplicateGenericMapper.INVOKED_METHODS.clear(); + + ParentDto childDto = DeduplicateGenericMapper.INSTANCE.mapChild( child ); + + assertThat( DeduplicateGenericMapper.INVOKED_METHODS ) + .containsExactly( + "beforeMappingChild", + "afterMappingChild" + ); + + DeduplicateGenericMapper.INVOKED_METHODS.clear(); + } +} + diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Parent.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Parent.java new file mode 100644 index 0000000000..4930677286 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/Parent.java @@ -0,0 +1,21 @@ +/* + * 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._3849; + +public class Parent { + private String value; + + public Parent() { + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/ParentDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/ParentDto.java new file mode 100644 index 0000000000..098917f001 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3849/ParentDto.java @@ -0,0 +1,22 @@ +/* + * 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._3849; + +public class ParentDto { + private String value; + + public ParentDto(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} From 6b6600c370cb50b2fbf40f56412238974575ae85 Mon Sep 17 00:00:00 2001 From: Aleksey Ivashin Date: Sun, 25 May 2025 18:05:18 +0300 Subject: [PATCH 489/597] #1958: Add support for ignoring multiple target properties at once --- core/src/main/java/org/mapstruct/Ignored.java | 67 ++++++++++++ .../main/java/org/mapstruct/IgnoredList.java | 54 ++++++++++ core/src/main/java/org/mapstruct/Mapping.java | 3 + .../chapter-5-data-type-conversions.asciidoc | 7 ++ .../ap/internal/gem/GemGenerator.java | 4 + .../processor/MethodRetrievalProcessor.java | 61 ++++++++++- .../org/mapstruct/ap/test/ignored/Animal.java | 64 +++++++++++ .../mapstruct/ap/test/ignored/AnimalDto.java | 63 +++++++++++ .../ap/test/ignored/AnimalMapper.java | 20 ++++ .../ap/test/ignored/ErroneousMapper.java | 26 +++++ .../ap/test/ignored/IgnoredPropertyTest.java | 102 ++++++++++++++++++ .../org/mapstruct/ap/test/ignored/Zoo.java | 48 +++++++++ .../org/mapstruct/ap/test/ignored/ZooDto.java | 48 +++++++++ .../mapstruct/ap/test/ignored/ZooMapper.java | 23 ++++ 14 files changed, 589 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/mapstruct/Ignored.java create mode 100644 core/src/main/java/org/mapstruct/IgnoredList.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignored/Animal.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignored/AnimalDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignored/AnimalMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignored/ErroneousMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignored/IgnoredPropertyTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignored/Zoo.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignored/ZooDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignored/ZooMapper.java diff --git a/core/src/main/java/org/mapstruct/Ignored.java b/core/src/main/java/org/mapstruct/Ignored.java new file mode 100644 index 0000000000..47e4961f43 --- /dev/null +++ b/core/src/main/java/org/mapstruct/Ignored.java @@ -0,0 +1,67 @@ +/* + * 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.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Configures the ignored of one bean attribute. + * + *

      + * The name all attributes of for ignored is to be specified via {@link #targets()}. + *

      + * + *

      + * Example 1: Implicitly mapping fields with the same name: + *

      + * + *
      
      + * // We need ignored Human.name and Human.lastName
      + * // we can use @Ignored with parameters "name", "lastName" {@link #targets()}
      + * @Mapper
      + * public interface HumanMapper {
      + *    @Ignored( targets = { "name", "lastName" } )
      + *    HumanDto toHumanDto(Human human)
      + * }
      + * 
      + *
      
      + * // generates:
      + * @Override
      + * public HumanDto toHumanDto(Human human) {
      + *    humanDto.setFullName( human.getFullName() );
      + *    // ...
      + * }
      + * 
      + * + * @author Ivashin Aleksey + */ +@Repeatable(IgnoredList.class) +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +public @interface Ignored { + + /** + * Whether the specified properties should be ignored by the generated mapping method. + * This can be useful when certain attributes should not be propagated from source to target or when properties in + * the target object are populated using a decorator and thus would be reported as unmapped target property by + * default. + * + * @return The target names of the configured properties that should be ignored + */ + String[] targets(); + + /** + * The prefix that should be applied to all the properties specified via {@link #targets()}. + * + * @return The target prefix to be applied to the defined properties + */ + String prefix() default ""; + +} diff --git a/core/src/main/java/org/mapstruct/IgnoredList.java b/core/src/main/java/org/mapstruct/IgnoredList.java new file mode 100644 index 0000000000..e47db6bac7 --- /dev/null +++ b/core/src/main/java/org/mapstruct/IgnoredList.java @@ -0,0 +1,54 @@ +/* + * 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; + +/** + * Configures the ignored list for several bean attributes. + *

      + * TIP: When using Java 8 or later, you can omit the {@code @IgnoredList} + * wrapper annotation and directly specify several {@code @Ignored} annotations on one method. + * + *

      These two examples are equal. + *

      + *
      
      + * // before Java 8
      + * @Mapper
      + * public interface MyMapper {
      + *     @IgnoredList({
      + *         @Ignored(targets = { "firstProperty" } ),
      + *         @Ignored(targets = { "secondProperty" } )
      + *     })
      + *     HumanDto toHumanDto(Human human);
      + * }
      + * 
      + *
      
      + * // Java 8 and later
      + * @Mapper
      + * public interface MyMapper {
      + *     @Ignored(targets = { "firstProperty" } ),
      + *     @Ignored(targets = { "secondProperty" } )
      + *     HumanDto toHumanDto(Human human);
      + * }
      + * 
      + * + * @author Ivashin Aleksey + */ +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +public @interface IgnoredList { + + /** + * The configuration of the bean attributes. + * + * @return The configuration of the bean attributes. + */ + Ignored[] value(); +} diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 6c95b0db2c..f15cfb75cc 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -306,6 +306,9 @@ * This can be useful when certain attributes should not be propagated from source to target or when properties in * the target object are populated using a decorator and thus would be reported as unmapped target property by * default. + *

      + * If you have multiple properties to ignore, + * you can use the {@link Ignored} annotation instead and group them all at once. * * @return {@code true} if the given property should be ignored, {@code false} otherwise */ diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index bc406cf0b0..ad49fe2960 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -280,6 +280,13 @@ This puts the configuration of the nested mapping into one place (method) where instead of re-configuring the same things on all of those upper methods. ==== +[TIP] +==== +When ignoring multiple properties instead of defining multiple `@Mapping` annotations, you can use the `@Ignored` annotation to group them together. +e.g. for the `FishTankMapperWithDocument` example above, you could write: +`@Ignored(targets = { "plant", "ornament", "material" })` +==== + [NOTE] ==== In some cases the `ReportingPolicy` that is going to be used for the generated nested method would be `IGNORE`. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java index 2ed9bd9a09..a8b62babe9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java @@ -26,6 +26,8 @@ import org.mapstruct.Mapper; import org.mapstruct.MapperConfig; import org.mapstruct.Mapping; +import org.mapstruct.Ignored; +import org.mapstruct.IgnoredList; import org.mapstruct.MappingTarget; import org.mapstruct.Mappings; import org.mapstruct.Named; @@ -53,6 +55,8 @@ @GemDefinition(AnnotateWiths.class) @GemDefinition(Mapper.class) @GemDefinition(Mapping.class) +@GemDefinition(Ignored.class) +@GemDefinition(IgnoredList.class) @GemDefinition(Mappings.class) @GemDefinition(IterableMapping.class) @GemDefinition(BeanMapping.class) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index c8c13e6bf9..6843ac5bbe 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -21,6 +21,8 @@ import org.mapstruct.ap.internal.gem.BeanMappingGem; import org.mapstruct.ap.internal.gem.ConditionGem; +import org.mapstruct.ap.internal.gem.IgnoredGem; +import org.mapstruct.ap.internal.gem.IgnoredListGem; import org.mapstruct.ap.internal.gem.IterableMappingGem; import org.mapstruct.ap.internal.gem.MapMappingGem; import org.mapstruct.ap.internal.gem.MappingGem; @@ -76,6 +78,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor getMappings(ExecutableElement method, BeanMappingOptions beanMapping) { - return new RepeatableMappings( beanMapping ).getProcessedAnnotations( method ); + Set processedAnnotations = new RepeatableMappings( beanMapping ) + .getProcessedAnnotations( method ); + processedAnnotations.addAll( new IgnoredConditions( processedAnnotations ) + .getProcessedAnnotations( method ) ); + return processedAnnotations; } /** @@ -823,4 +831,55 @@ protected void addInstance(ConditionGem gem, Element source, Set { + + protected final Set processedAnnotations; + + protected IgnoredConditions( Set processedAnnotations ) { + super( elementUtils, IGNORED_FQN, IGNORED_LIST_FQN ); + this.processedAnnotations = processedAnnotations; + } + + @Override + protected IgnoredGem singularInstanceOn(Element element) { + return IgnoredGem.instanceOn( element ); + } + + @Override + protected IgnoredListGem multipleInstanceOn(Element element) { + return IgnoredListGem.instanceOn( element ); + } + + @Override + protected void addInstance(IgnoredGem gem, Element method, Set mappings) { + IgnoredGem ignoredGem = IgnoredGem.instanceOn( method ); + if ( ignoredGem == null ) { + ignoredGem = gem; + } + String prefix = ignoredGem.prefix().get(); + for ( String target : ignoredGem.targets().get() ) { + String realTarget = target; + if ( !prefix.isEmpty() ) { + realTarget = prefix + "." + target; + } + MappingOptions mappingOptions = MappingOptions.forIgnore( realTarget ); + if ( processedAnnotations.contains( mappingOptions ) || mappings.contains( mappingOptions ) ) { + messager.printMessage( method, Message.PROPERTYMAPPING_DUPLICATE_TARGETS, realTarget ); + } + else { + mappings.add( mappingOptions ); + } + } + } + + @Override + protected void addInstances(IgnoredListGem gem, Element method, Set mappings) { + IgnoredListGem ignoredListGem = IgnoredListGem.instanceOn( method ); + for ( IgnoredGem ignoredGem : ignoredListGem.value().get() ) { + addInstance( ignoredGem, method, mappings ); + } + } + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignored/Animal.java b/processor/src/test/java/org/mapstruct/ap/test/ignored/Animal.java new file mode 100644 index 0000000000..223f99f705 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignored/Animal.java @@ -0,0 +1,64 @@ +/* + * 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.ignored; + +public class Animal { + + //CHECKSTYLE:OFF + public Integer publicAge; + public String publicColour; + //CHECKSTYLE:OFN + private String colour; + private String name; + private int size; + private Integer age; + + // private String colour; + public Animal() { + } + + public Animal(String name, int size, Integer age, String colour) { + this.name = name; + this.size = size; + this.publicAge = age; + this.age = age; + this.publicColour = colour; + this.colour = colour; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getColour() { + return colour; + } + + public void setColour( String colour ) { + this.colour = colour; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignored/AnimalDto.java b/processor/src/test/java/org/mapstruct/ap/test/ignored/AnimalDto.java new file mode 100644 index 0000000000..1651f9b56b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignored/AnimalDto.java @@ -0,0 +1,63 @@ +/* + * 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.ignored; + +public class AnimalDto { + + //CHECKSTYLE:OFF + public Integer publicAge; + public String publicColor; + //CHECKSTYLE:ON + private String name; + private Integer size; + private Integer age; + private String color; + + public AnimalDto() { + + } + + public AnimalDto(String name, Integer size, Integer age, String color) { + this.name = name; + this.size = size; + this.publicAge = age; + this.age = age; + this.publicColor = color; + this.color = color; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignored/AnimalMapper.java b/processor/src/test/java/org/mapstruct/ap/test/ignored/AnimalMapper.java new file mode 100644 index 0000000000..2753b36070 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignored/AnimalMapper.java @@ -0,0 +1,20 @@ +/* + * 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.ignored; + +import org.mapstruct.Ignored; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface AnimalMapper { + + AnimalMapper INSTANCE = Mappers.getMapper( AnimalMapper.class ); + + @Ignored( targets = { "publicAge", "age", "publicColor", "color" } ) + AnimalDto animalToDto( Animal animal ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignored/ErroneousMapper.java b/processor/src/test/java/org/mapstruct/ap/test/ignored/ErroneousMapper.java new file mode 100644 index 0000000000..b113e715aa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignored/ErroneousMapper.java @@ -0,0 +1,26 @@ +/* + * 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.ignored; + +import org.mapstruct.Ignored; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ErroneousMapper { + + ErroneousMapper INSTANCE = Mappers.getMapper( ErroneousMapper.class ); + + @Mapping(target = "name", ignore = true) + @Ignored(targets = { "name", "color", "publicColor" }) + AnimalDto ignoredAndMappingAnimalToDto( Animal animal ); + + @Mapping(target = "publicColor", source = "publicColour") + @Ignored(targets = { "publicColor", "color" }) + AnimalDto ignoredAndMappingAnimalToDtoMap( Animal animal ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignored/IgnoredPropertyTest.java b/processor/src/test/java/org/mapstruct/ap/test/ignored/IgnoredPropertyTest.java new file mode 100644 index 0000000000..bc6fc2f6b7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignored/IgnoredPropertyTest.java @@ -0,0 +1,102 @@ +/* + * 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.ignored; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for ignoring properties during the mapping. + * + * @author Ivashin Aleksey + */ +@WithClasses({ Animal.class, AnimalDto.class, Zoo.class, ZooDto.class, ZooMapper.class}) +public class IgnoredPropertyTest { + + @ProcessorTest + @IssueKey("1958") + @WithClasses( { AnimalMapper.class } ) + public void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() { + Animal animal = new Animal( "Bruno", 100, 23, "black" ); + + AnimalDto animalDto = AnimalMapper.INSTANCE.animalToDto( animal ); + + assertThat( animalDto ).isNotNull(); + assertThat( animalDto.getName() ).isEqualTo( "Bruno" ); + assertThat( animalDto.getSize() ).isEqualTo( 100 ); + assertThat( animalDto.getAge() ).isNull(); + assertThat( animalDto.publicAge ).isNull(); + assertThat( animalDto.getColor() ).isNull(); + assertThat( animalDto.publicColor ).isNull(); + } + + @ProcessorTest + @IssueKey("1958") + @WithClasses( { ErroneousMapper.class } ) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = "Target property \"name\" must not be mapped more than once." ), + @Diagnostic(type = ErroneousMapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 24, + message = "Target property \"publicColor\" must not be mapped more than once." ) + } + ) + public void shouldFailToGenerateMappings() { + } + + @ProcessorTest + @IssueKey("1958") + @WithClasses( { AnimalMapper.class } ) + public void shouldNotPropagateIgnoredInnerPropertyGivenViaTargetAttribute() { + Animal animal = new Animal( "Bruno", 100, 23, "black" ); + Zoo zoo = new Zoo(animal, "Test name", "test address"); + + ZooDto zooDto = ZooMapper.INSTANCE.zooToDto( zoo ); + + assertThat( zooDto ).isNotNull(); + assertThat( zooDto.getName() ).isEqualTo( "Test name" ); + assertThat( zooDto.getAddress() ).isEqualTo( "test address" ); + assertThat( zooDto.getAnimal() ).isNotNull(); + assertThat( zooDto.getAnimal().getName() ).isEqualTo( "Bruno" ); + assertThat( zooDto.getAnimal().getAge() ).isNull(); + assertThat( zooDto.getAnimal().publicAge ).isNull(); + assertThat( zooDto.getAnimal().getColor() ).isNull(); + assertThat( zooDto.getAnimal().publicColor ).isNull(); + assertThat( zooDto.getAnimal().getSize() ).isNull(); + } + + @ProcessorTest + @IssueKey("1958") + @WithClasses( { AnimalMapper.class } ) + public void shouldNotPropagateIgnoredInnerPropertyGivenViaTargetAttribute2() { + Animal animal = new Animal( "Bruno", 100, 23, "black" ); + Zoo zoo = new Zoo(animal, "Test name", "test address"); + + ZooDto zooDto = ZooMapper.INSTANCE.zooToDto2( zoo ); + + assertThat( zooDto ).isNotNull(); + assertThat( zooDto.getName() ).isEqualTo( "Test name" ); + assertThat( zooDto.getAddress() ).isNull(); + assertThat( zooDto.getAnimal() ).isNotNull(); + assertThat( zooDto.getAnimal().getName() ).isEqualTo( "Bruno" ); + assertThat( zooDto.getAnimal().getAge() ).isNull(); + assertThat( zooDto.getAnimal().publicAge ).isNull(); + assertThat( zooDto.getAnimal().getColor() ).isNull(); + assertThat( zooDto.getAnimal().publicColor ).isNull(); + assertThat( zooDto.getAnimal().getSize() ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignored/Zoo.java b/processor/src/test/java/org/mapstruct/ap/test/ignored/Zoo.java new file mode 100644 index 0000000000..377e03b877 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignored/Zoo.java @@ -0,0 +1,48 @@ +/* + * 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.ignored; + +public class Zoo { + + private Animal animal; + + private String name; + + private String address; + + public Zoo() { + } + + public Zoo(Animal animal, String name, String address ) { + this.animal = animal; + this.name = name; + this.address = address; + } + + public Animal getAnimal() { + return animal; + } + + public void setAnimal(Animal animal) { + this.animal = animal; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignored/ZooDto.java b/processor/src/test/java/org/mapstruct/ap/test/ignored/ZooDto.java new file mode 100644 index 0000000000..7c062cab4c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignored/ZooDto.java @@ -0,0 +1,48 @@ +/* + * 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.ignored; + +public class ZooDto { + + private AnimalDto animal; + + private String name; + + private String address; + + public ZooDto() { + } + + public ZooDto(AnimalDto animal, String name, String address) { + this.animal = animal; + this.name = name; + this.address = address; + } + + public AnimalDto getAnimal() { + return animal; + } + + public void setAnimal(AnimalDto animal) { + this.animal = animal; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/ignored/ZooMapper.java b/processor/src/test/java/org/mapstruct/ap/test/ignored/ZooMapper.java new file mode 100644 index 0000000000..f05e045b5f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/ignored/ZooMapper.java @@ -0,0 +1,23 @@ +/* + * 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.ignored; + +import org.mapstruct.Ignored; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ZooMapper { + + ZooMapper INSTANCE = Mappers.getMapper( ZooMapper.class ); + + @Ignored( prefix = "animal", targets = { "publicAge", "size", "publicColor", "age", "color" } ) + ZooDto zooToDto( Zoo zoo ); + + @Ignored( targets = { "address" } ) + @Ignored( prefix = "animal", targets = { "publicAge", "size", "publicColor", "age", "color" } ) + ZooDto zooToDto2( Zoo zoo ); +} From 5464c3cff805a235e908976fe5d55d0162d2a323 Mon Sep 17 00:00:00 2001 From: Yang Tang Date: Sat, 31 May 2025 17:10:24 +0800 Subject: [PATCH 490/597] #3711: Support generic `@Context` Signed-off-by: TangYang --- .../processor/MethodRetrievalProcessor.java | 25 +++++++++------ .../ap/test/bugs/_3711/BaseMapper.java | 12 +++++++ .../ap/test/bugs/_3711/Issue3711Test.java | 31 +++++++++++++++++++ .../ap/test/bugs/_3711/JpaContext.java | 31 +++++++++++++++++++ .../ap/test/bugs/_3711/ParentDto.java | 18 +++++++++++ .../ap/test/bugs/_3711/ParentEntity.java | 18 +++++++++++ .../test/bugs/_3711/SourceTargetMapper.java | 16 ++++++++++ 7 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/BaseMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/Issue3711Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/JpaContext.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/ParentDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/ParentEntity.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/SourceTargetMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java index 6843ac5bbe..d7a0329aeb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java @@ -114,7 +114,12 @@ public List process(ProcessorContext context, TypeElement mapperTy } List prototypeMethods = retrievePrototypeMethods( mapperTypeElement, mapperOptions ); - return retrieveMethods( mapperTypeElement, mapperTypeElement, mapperOptions, prototypeMethods ); + return retrieveMethods( + typeFactory.getType( mapperTypeElement.asType() ), + mapperTypeElement, + mapperOptions, + prototypeMethods + ); } @Override @@ -166,19 +171,21 @@ private List retrievePrototypeMethods(TypeElement mapperTypeElemen /** * Retrieves the mapping methods declared by the given mapper type. * - * @param usedMapper The type of interest (either the mapper to implement or a used mapper via @uses annotation) + * @param usedMapperType The type of interest (either the mapper to implement, a used mapper via @uses annotation, + * or a parameter type annotated with @Context) * @param mapperToImplement the top level type (mapper) that requires implementation * @param mapperOptions the mapper config * @param prototypeMethods prototype methods defined in mapper config type * @return All mapping methods declared by the given type */ - private List retrieveMethods(TypeElement usedMapper, TypeElement mapperToImplement, + private List retrieveMethods(Type usedMapperType, TypeElement mapperToImplement, MapperOptions mapperOptions, List prototypeMethods) { List methods = new ArrayList<>(); + TypeElement usedMapper = usedMapperType.getTypeElement(); for ( ExecutableElement executable : elementUtils.getAllEnclosedExecutableElements( usedMapper ) ) { SourceMethod method = getMethod( - usedMapper, + usedMapperType, executable, mapperToImplement, mapperOptions, @@ -195,7 +202,7 @@ private List retrieveMethods(TypeElement usedMapper, TypeElement m TypeElement usesMapperElement = asTypeElement( mapper ); if ( !mapperToImplement.equals( usesMapperElement ) ) { methods.addAll( retrieveMethods( - usesMapperElement, + typeFactory.getType( mapper ), mapperToImplement, mapperOptions, prototypeMethods ) ); @@ -218,13 +225,13 @@ private TypeElement asTypeElement(DeclaredType type) { return (TypeElement) type.asElement(); } - private SourceMethod getMethod(TypeElement usedMapper, + private SourceMethod getMethod(Type usedMapperType, ExecutableElement method, TypeElement mapperToImplement, MapperOptions mapperOptions, List prototypeMethods) { - - ExecutableType methodType = typeFactory.getMethodType( (DeclaredType) usedMapper.asType(), method ); + TypeElement usedMapper = usedMapperType.getTypeElement(); + ExecutableType methodType = typeFactory.getMethodType( (DeclaredType) usedMapperType.getTypeMirror(), method ); List parameters = typeFactory.getParameters( methodType, method ); Type returnType = typeFactory.getReturnType( methodType ); @@ -357,7 +364,7 @@ private ParameterProvidedMethods retrieveContextProvidedMethods( continue; } List contextParamMethods = retrieveMethods( - contextParam.getType().getTypeElement(), + contextParam.getType(), mapperToImplement, mapperConfig, Collections.emptyList() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/BaseMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/BaseMapper.java new file mode 100644 index 0000000000..e2a04fe336 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/BaseMapper.java @@ -0,0 +1,12 @@ +/* + * 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._3711; + +import org.mapstruct.Context; + +interface BaseMapper { + E toEntity(T s, @Context JpaContext ctx); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/Issue3711Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/Issue3711Test.java new file mode 100644 index 0000000000..7b141e9e14 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/Issue3711Test.java @@ -0,0 +1,31 @@ +/* + * 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._3711; + +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; + +@WithClasses({ + ParentEntity.class, + ParentDto.class, + JpaContext.class, + SourceTargetMapper.class, + BaseMapper.class, +}) +@IssueKey("3711") +class Issue3711Test { + @ProcessorTest + void shouldGenerateContextMethod() { + JpaContext jpaContext = new JpaContext<>(); + SourceTargetMapper.INSTANCE.toEntity( new ParentDto(), jpaContext ); + + assertThat( jpaContext.getInvokedMethods() ) + .containsExactly( "beforeMapping", "afterMapping" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/JpaContext.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/JpaContext.java new file mode 100644 index 0000000000..b21bf639d7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/JpaContext.java @@ -0,0 +1,31 @@ +/* + * 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._3711; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.MappingTarget; + +public class JpaContext { + private final List invokedMethods = new ArrayList<>(); + + @BeforeMapping + void beforeMapping(@MappingTarget T parentEntity) { + invokedMethods.add( "beforeMapping" ); + } + + @AfterMapping + void afterMapping(@MappingTarget T parentEntity) { + invokedMethods.add( "afterMapping" ); + } + + public List getInvokedMethods() { + return invokedMethods; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/ParentDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/ParentDto.java new file mode 100644 index 0000000000..664d6e58ab --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/ParentDto.java @@ -0,0 +1,18 @@ +/* + * 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._3711; + +public class ParentDto { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/ParentEntity.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/ParentEntity.java new file mode 100644 index 0000000000..aaefa949fe --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/ParentEntity.java @@ -0,0 +1,18 @@ +/* + * 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._3711; + +public class ParentEntity { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/SourceTargetMapper.java new file mode 100644 index 0000000000..b09333fd56 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3711/SourceTargetMapper.java @@ -0,0 +1,16 @@ +/* + * 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._3711; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SourceTargetMapper extends BaseMapper { + SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + + ParentEntity toDTO(ParentDto dto); +} From 8fc97f5f62743cc72280f5334dbebf9fdc39bfee Mon Sep 17 00:00:00 2001 From: Yang Tang Date: Sat, 31 May 2025 17:13:50 +0800 Subject: [PATCH 491/597] #3806: Properly apply `NullValuePropertyMappingStrategy.IGNORE` for collections / maps without setters Signed-off-by: TangYang --- .../model/CollectionAssignmentBuilder.java | 1 + .../GetterWrapperForCollectionsAndMaps.java | 17 ++++ .../GetterWrapperForCollectionsAndMaps.ftl | 5 +- .../ap/test/bugs/_3806/Issue3806Mapper.java | 63 ++++++++++++++ .../ap/test/bugs/_3806/Issue3806Test.java | 86 +++++++++++++++++++ 5 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3806/Issue3806Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3806/Issue3806Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index 9a0a025845..76e56cd976 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -240,6 +240,7 @@ else if ( hasNoArgsConstructor() ) { result, method.getThrownTypes(), targetType, + nvpms, targetAccessorType.isFieldAssignment() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java index d29a80b420..e1c0c8cb20 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java @@ -9,9 +9,12 @@ import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; + /** * This wrapper handles the situation were an assignment must be done via a target getter method because there * is no setter available. @@ -26,6 +29,14 @@ * @author Sjaak Derksen */ public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAndMaps { + private final boolean ignoreMapNull; + + public GetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, + List thrownTypesToExclude, + Type targetType, + boolean fieldAssignment) { + this( decoratedAssignment, thrownTypesToExclude, targetType, null, fieldAssignment ); + } /** * @param decoratedAssignment source RHS @@ -36,6 +47,7 @@ public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAnd public GetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, List thrownTypesToExclude, Type targetType, + NullValuePropertyMappingStrategyGem nvpms, boolean fieldAssignment) { super( @@ -44,6 +56,7 @@ public GetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, targetType, fieldAssignment ); + this.ignoreMapNull = nvpms == IGNORE; } @Override @@ -54,4 +67,8 @@ public Set getImportTypes() { } return imported; } + + public boolean isIgnoreMapNull() { + return ignoreMapNull; + } } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.ftl index 4ef55f3b40..f47a37106e 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.ftl @@ -10,10 +10,13 @@ <@lib.sourceLocalVarAssignment/> if ( ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWriteAccesing /> != null ) { <@lib.handleExceptions> - <#if ext.existingInstanceMapping> + <#if ext.existingInstanceMapping && !ignoreMapNull> ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWriteAccesing />.clear(); <@lib.handleLocalVarNullCheck needs_explicit_local_var=false> + <#if ext.existingInstanceMapping && ignoreMapNull> + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWriteAccesing />.clear(); + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWriteAccesing />.<#if ext.targetType.collectionType>addAll<#else>putAll( <@lib.handleWithAssignmentOrNullCheckVar/> ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3806/Issue3806Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3806/Issue3806Mapper.java new file mode 100644 index 0000000000..89f325dfbb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3806/Issue3806Mapper.java @@ -0,0 +1,63 @@ +/* + * 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._3806; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) +public interface Issue3806Mapper { + + Issue3806Mapper INSTANCE = Mappers.getMapper( Issue3806Mapper.class ); + + void update(@MappingTarget Target target, Target source); + + class Target { + + private final Collection authors; + private final Map booksByAuthor; + + protected Collection books; + protected Map booksByPublisher; + + public Target(Collection authors, Map booksByAuthor) { + this.authors = authors != null ? new ArrayList<>( authors ) : null; + this.booksByAuthor = booksByAuthor != null ? new HashMap<>( booksByAuthor ) : null; + } + + public Collection getAuthors() { + return authors; + } + + public Map getBooksByAuthor() { + return booksByAuthor; + } + + public Collection getBooks() { + return books; + } + + public void setBooks(Collection books) { + this.books = books; + } + + public Map getBooksByPublisher() { + return booksByPublisher; + } + + public void setBooksByPublisher(Map booksByPublisher) { + this.booksByPublisher = booksByPublisher; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3806/Issue3806Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3806/Issue3806Test.java new file mode 100644 index 0000000000..1df3318c22 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3806/Issue3806Test.java @@ -0,0 +1,86 @@ +/* + * 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._3806; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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; +import static org.assertj.core.api.Assertions.entry; + +@WithClasses(Issue3806Mapper.class) +@IssueKey("3806") +class Issue3806Test { + + @ProcessorTest + void shouldNotClearGetterOnlyCollectionsInUpdateMapping() { + Map booksByAuthor = new HashMap<>(); + booksByAuthor.put( "author1", "book1" ); + booksByAuthor.put( "author2", "book2" ); + List authors = new ArrayList<>(); + authors.add( "author1" ); + authors.add( "author2" ); + + List books = new ArrayList<>(); + books.add( "book1" ); + books.add( "book2" ); + Map booksByPublisher = new HashMap<>(); + booksByPublisher.put( "publisher1", "book1" ); + booksByPublisher.put( "publisher2", "book2" ); + Issue3806Mapper.Target target = new Issue3806Mapper.Target( authors, booksByAuthor ); + target.setBooks( books ); + target.setBooksByPublisher( booksByPublisher ); + + Issue3806Mapper.Target source = new Issue3806Mapper.Target( null, null ); + Issue3806Mapper.INSTANCE.update( target, source ); + + assertThat( target.getAuthors() ).containsExactly( "author1", "author2" ); + assertThat( target.getBooksByAuthor() ) + .containsOnly( + entry( "author1", "book1" ), + entry( "author2", "book2" ) + ); + + assertThat( target.getBooks() ).containsExactly( "book1", "book2" ); + assertThat( target.getBooksByPublisher() ) + .containsOnly( + entry( "publisher1", "book1" ), + entry( "publisher2", "book2" ) + ); + + booksByAuthor = new HashMap<>(); + booksByAuthor.put( "author3", "book3" ); + authors = new ArrayList<>(); + authors.add( "author3" ); + + books = new ArrayList<>(); + books.add( "book3" ); + booksByPublisher = new HashMap<>(); + booksByPublisher.put( "publisher3", "book3" ); + source = new Issue3806Mapper.Target( authors, booksByAuthor ); + source.setBooks( books ); + source.setBooksByPublisher( booksByPublisher ); + Issue3806Mapper.INSTANCE.update( target, source ); + + assertThat( target.getAuthors() ).containsExactly( "author3" ); + assertThat( target.getBooksByAuthor() ) + .containsOnly( + entry( "author3", "book3" ) + ); + + assertThat( target.getBooks() ).containsExactly( "book3" ); + assertThat( target.getBooksByPublisher() ) + .containsOnly( + entry( "publisher3", "book3" ) + ); + } +} From bff88297e367ec856a1c5a5f68fcc9a62662ee70 Mon Sep 17 00:00:00 2001 From: Yang Tang Date: Sat, 31 May 2025 19:29:39 +0800 Subject: [PATCH 492/597] #3807: Properly recognize the type of public generic fields Signed-off-by: TangYang --- .../ap/internal/model/BeanMappingMethod.java | 4 +- .../mapstruct/ap/internal/util/Filters.java | 23 +++++---- .../util/accessor/AbstractAccessor.java | 41 ---------------- .../util/accessor/ElementAccessor.java | 46 ++++++++++++++---- .../accessor/ExecutableElementAccessor.java | 41 ---------------- .../accessor/ParameterElementAccessor.java | 48 ------------------- .../internal/util/accessor/ReadAccessor.java | 10 ++-- .../util/accessor/RecordElementAccessor.java | 38 --------------- .../ap/test/bugs/_3807/Issue3807Mapper.java | 48 +++++++++++++++++++ .../ap/test/bugs/_3807/Issue3807Test.java | 32 +++++++++++++ 10 files changed, 140 insertions(+), 191 deletions(-) delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ExecutableElementAccessor.java delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/accessor/RecordElementAccessor.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 34c1ea3ccb..b5208d302b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -65,7 +65,7 @@ import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; -import org.mapstruct.ap.internal.util.accessor.ParameterElementAccessor; +import org.mapstruct.ap.internal.util.accessor.ElementAccessor; import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; import org.mapstruct.ap.internal.util.accessor.ReadAccessor; @@ -1067,7 +1067,7 @@ private Accessor createConstructorAccessor(Element element, TypeMirror accessedT existingVariableNames ); existingVariableNames.add( safeParameterName ); - return new ParameterElementAccessor( element, accessedType, safeParameterName ); + return new ElementAccessor( element, accessedType, safeParameterName ); } private boolean hasDefaultAnnotationFromAnyPackage(Element element) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java index 5f7fe74bf2..6492270705 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Filters.java @@ -12,7 +12,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.function.Function; +import java.util.function.BiFunction; import java.util.stream.Collectors; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -23,7 +23,7 @@ import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.util.accessor.Accessor; -import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; +import org.mapstruct.ap.internal.util.accessor.ElementAccessor; import org.mapstruct.ap.internal.util.accessor.ReadAccessor; import static org.mapstruct.ap.internal.util.Collections.first; @@ -64,7 +64,7 @@ public Filters(AccessorNamingUtils accessorNaming, TypeUtils typeUtils, TypeMirr public List getterMethodsIn(List elements) { return elements.stream() .filter( accessorNaming::isGetterMethod ) - .map( method -> ReadAccessor.fromGetter( method, getReturnType( method ) ) ) + .map( method -> ReadAccessor.fromGetter( method, getReturnType( method ) ) ) .collect( Collectors.toCollection( LinkedList::new ) ); } @@ -90,7 +90,10 @@ public Map recordAccessorsIn(Collection recordCom for ( Element recordComponent : recordComponents ) { recordAccessors.put( recordComponent.getSimpleName().toString(), - ReadAccessor.fromRecordComponent( recordComponent ) + ReadAccessor.fromRecordComponent( + recordComponent, + typeUtils.asMemberOf( (DeclaredType) typeMirror, recordComponent ) + ) ); } @@ -101,10 +104,10 @@ private TypeMirror getReturnType(ExecutableElement executableElement) { return getWithinContext( executableElement ).getReturnType(); } - public List fieldsIn(List accessors, Function creator) { + public List fieldsIn(List accessors, BiFunction creator) { return accessors.stream() .filter( Fields::isFieldAccessor ) - .map( creator ) + .map( variableElement -> creator.apply( variableElement, getWithinContext( variableElement ) ) ) .collect( Collectors.toCollection( LinkedList::new ) ); } @@ -117,7 +120,7 @@ public List presenceCheckMethodsIn(List el public List setterMethodsIn(List elements) { return elements.stream() .filter( accessorNaming::isSetterMethod ) - .map( method -> new ExecutableElementAccessor( method, getFirstParameter( method ), SETTER ) ) + .map( method -> new ElementAccessor( method, getFirstParameter( method ), SETTER ) ) .collect( Collectors.toCollection( LinkedList::new ) ); } @@ -129,10 +132,14 @@ private ExecutableType getWithinContext( ExecutableElement executableElement ) { return (ExecutableType) typeUtils.asMemberOf( (DeclaredType) typeMirror, executableElement ); } + private TypeMirror getWithinContext( VariableElement variableElement ) { + return typeUtils.asMemberOf( (DeclaredType) typeMirror, variableElement ); + } + public List adderMethodsIn(List elements) { return elements.stream() .filter( accessorNaming::isAdderMethod ) - .map( method -> new ExecutableElementAccessor( method, getFirstParameter( method ), ADDER ) ) + .map( method -> new ElementAccessor( method, getFirstParameter( method ), ADDER ) ) .collect( Collectors.toCollection( LinkedList::new ) ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java deleted file mode 100644 index 04c4c99992..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/AbstractAccessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.internal.util.accessor; - -import java.util.Set; - -import javax.lang.model.element.Element; -import javax.lang.model.element.Modifier; - -/** - * This is an abstract implementation of an {@link Accessor} that provides the common implementation. - * - * @author Filip Hrisafov - */ -abstract class AbstractAccessor implements Accessor { - - protected final T element; - - AbstractAccessor(T element) { - this.element = element; - } - - @Override - public String getSimpleName() { - return element.getSimpleName().toString(); - } - - @Override - public Set getModifiers() { - return element.getModifiers(); - } - - @Override - public T getElement() { - return element; - } - -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ElementAccessor.java index 24e71cc85f..4b363815b7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ElementAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ElementAccessor.java @@ -5,31 +5,61 @@ */ package org.mapstruct.ap.internal.util.accessor; +import java.util.Set; import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; /** - * An {@link Accessor} that wraps a {@link VariableElement}. - * + * An {@link Accessor} that wraps a {@link Element}. + * Used for getter, setter, filed, constructor, record-class, etc. * @author Filip Hrisafov + * @author Tang Yang */ -public class ElementAccessor extends AbstractAccessor { +public class ElementAccessor implements Accessor { + private final Element element; + private final String name; private final AccessorType accessorType; + private final TypeMirror accessedType; + + public ElementAccessor(VariableElement variableElement, TypeMirror accessedType) { + this( variableElement, accessedType, AccessorType.FIELD ); + } - public ElementAccessor(VariableElement variableElement) { - this( variableElement, AccessorType.FIELD ); + public ElementAccessor(Element element, TypeMirror accessedType, String name) { + this.element = element; + this.name = name; + this.accessedType = accessedType; + this.accessorType = AccessorType.PARAMETER; } - public ElementAccessor(Element element, AccessorType accessorType) { - super( element ); + public ElementAccessor(Element element, TypeMirror accessedType, AccessorType accessorType) { + this.element = element; + this.accessedType = accessedType; this.accessorType = accessorType; + this.name = null; } @Override public TypeMirror getAccessedType() { - return element.asType(); + return accessedType != null ? accessedType : element.asType(); + } + + @Override + public String getSimpleName() { + return name != null ? name : element.getSimpleName().toString(); + } + + @Override + public Set getModifiers() { + return element.getModifiers(); + } + + @Override + public Element getElement() { + return element; } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ExecutableElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ExecutableElementAccessor.java deleted file mode 100644 index af37acbce1..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ExecutableElementAccessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.internal.util.accessor; - -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.type.TypeMirror; - -/** - * An {@link Accessor} that wraps an {@link ExecutableElement}. - * - * @author Filip Hrisafov - */ -public class ExecutableElementAccessor extends AbstractAccessor { - - private final TypeMirror accessedType; - private final AccessorType accessorType; - - public ExecutableElementAccessor(ExecutableElement element, TypeMirror accessedType, AccessorType accessorType) { - super( element ); - this.accessedType = accessedType; - this.accessorType = accessorType; - } - - @Override - public TypeMirror getAccessedType() { - return accessedType; - } - - @Override - public String toString() { - return element.toString(); - } - - @Override - public AccessorType getAccessorType() { - return accessorType; - } -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java deleted file mode 100644 index 9991370059..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.internal.util.accessor; - -import javax.lang.model.element.Element; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; - -/** - * An {@link Accessor} that wraps a {@link VariableElement}. - * - * @author Filip Hrisafov - */ -public class ParameterElementAccessor extends AbstractAccessor { - - protected final String name; - protected final TypeMirror accessedType; - - public ParameterElementAccessor(Element element, TypeMirror accessedType, String name) { - super( element ); - this.name = name; - this.accessedType = accessedType; - } - - @Override - public String getSimpleName() { - return name != null ? name : super.getSimpleName(); - } - - @Override - public TypeMirror getAccessedType() { - return accessedType; - } - - @Override - public String toString() { - return element.toString(); - } - - @Override - public AccessorType getAccessorType() { - return AccessorType.PARAMETER; - } - -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java index 5177bfc75b..31edf3a6da 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java @@ -17,8 +17,8 @@ public interface ReadAccessor extends Accessor { String getReadValueSource(); - static ReadAccessor fromField(VariableElement variableElement) { - return new ReadDelegateAccessor( new ElementAccessor( variableElement ) ) { + static ReadAccessor fromField(VariableElement variableElement, TypeMirror accessedType) { + return new ReadDelegateAccessor( new ElementAccessor( variableElement, accessedType ) ) { @Override public String getReadValueSource() { return getSimpleName(); @@ -26,8 +26,8 @@ public String getReadValueSource() { }; } - static ReadAccessor fromRecordComponent(Element element) { - return new ReadDelegateAccessor( new ElementAccessor( element, AccessorType.GETTER ) ) { + static ReadAccessor fromRecordComponent(Element element, TypeMirror accessedType) { + return new ReadDelegateAccessor( new ElementAccessor( element, accessedType, AccessorType.GETTER ) ) { @Override public String getReadValueSource() { return getSimpleName() + "()"; @@ -36,7 +36,7 @@ public String getReadValueSource() { } static ReadAccessor fromGetter(ExecutableElement element, TypeMirror accessedType) { - return new ReadDelegateAccessor( new ExecutableElementAccessor( element, accessedType, AccessorType.GETTER ) ) { + return new ReadDelegateAccessor( new ElementAccessor( element, accessedType, AccessorType.GETTER ) ) { @Override public String getReadValueSource() { return getSimpleName() + "()"; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/RecordElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/RecordElementAccessor.java deleted file mode 100644 index d163f462f9..0000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/RecordElementAccessor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.internal.util.accessor; - -import javax.lang.model.element.Element; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; - -/** - * An {@link Accessor} that wraps a {@link VariableElement}. - * - * @author Filip Hrisafov - */ -public class RecordElementAccessor extends AbstractAccessor { - - public RecordElementAccessor(Element element) { - super( element ); - } - - @Override - public TypeMirror getAccessedType() { - return element.asType(); - } - - @Override - public String toString() { - return element.toString(); - } - - @Override - public AccessorType getAccessorType() { - return AccessorType.GETTER; - } - -} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Mapper.java new file mode 100644 index 0000000000..83ee1f32bd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Mapper.java @@ -0,0 +1,48 @@ +/* + * 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._3807; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface Issue3807Mapper { + Issue3807Mapper INSTANCE = Mappers.getMapper( Issue3807Mapper.class ); + + TargetWithoutSetter mapNoSetter(Source target); + + NormalTarget mapNormalSource(Source target); + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + //CHECKSTYLE:OFF + class TargetWithoutSetter { + public T value; + } + //CHECKSTYLE:ON + + class NormalTarget { + private T value; + + public T getValue() { + return value; + } + + public void setValue(T value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Test.java new file mode 100644 index 0000000000..3b895f3e11 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Test.java @@ -0,0 +1,32 @@ +/* + * 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._3807; + +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; + +@WithClasses(Issue3807Mapper.class) +@IssueKey("3087") +class Issue3807Test { + + @ProcessorTest + void fieldAndSetterShouldWorkWithGeneric() { + Issue3807Mapper.Source source = new Issue3807Mapper.Source( "value" ); + Issue3807Mapper.TargetWithoutSetter targetWithoutSetter = + Issue3807Mapper.INSTANCE.mapNoSetter( source ); + + assertThat( targetWithoutSetter ).isNotNull(); + assertThat( targetWithoutSetter.value ).isEqualTo( "value" ); + + Issue3807Mapper.NormalTarget normalTarget = Issue3807Mapper.INSTANCE.mapNormalSource( source ); + + assertThat( normalTarget ).isNotNull(); + assertThat( normalTarget.getValue() ).isEqualTo( "value" ); + } +} From ce84c81de2ee809c1457dfb9078ec1ae785ad323 Mon Sep 17 00:00:00 2001 From: Yang Tang Date: Sat, 31 May 2025 23:52:05 +0800 Subject: [PATCH 493/597] #3659: Support `@AnnotatedWith` on decorators Signed-off-by: TangYang --- .../ap/internal/model/Decorator.java | 20 ++- ...nnotationBasedComponentModelProcessor.java | 6 +- .../processor/JakartaComponentProcessor.java | 3 +- .../processor/Jsr330ComponentProcessor.java | 3 +- .../processor/MapperCreationProcessor.java | 5 + .../processor/SpringComponentProcessor.java | 130 ++++++++++++------ .../ap/test/decorator/AnnotatedMapper.java | 43 ++++++ .../decorator/AnnotatedMapperDecorator.java | 18 +++ .../DecoratedWithAnnotatedWithTest.java | 32 +++++ .../ap/test/decorator/TestAnnotation.java | 20 +++ .../JakartaAnnotateWithMapper.java | 40 ++++++ ...akartaAnnotateWithWithMapperDecorator.java | 31 +++++ .../JakartaDecoratorAnnotateWithTest.java | 59 ++++++++ .../decorator/jsr330/Jsr330DecoratorTest.java | 10 +- .../Jsr330AnnotateWithMapper.java | 28 ++++ .../Jsr330AnnotateWithMapperDecorator.java | 32 +++++ .../Jsr330DecoratorAnnotateWithTest.java | 95 +++++++++++++ .../spring/annotatewith/AnnotateMapper.java | 25 ++++ .../annotatewith/AnnotateMapperDecorator.java | 30 ++++ .../annotatewith/CustomAnnotateMapper.java | 25 ++++ .../CustomAnnotateMapperDecorator.java | 28 ++++ .../spring/annotatewith/CustomComponent.java | 20 +++ .../spring/annotatewith/CustomPrimary.java | 19 +++ .../SpringDecoratorAnnotateWithTest.java | 118 ++++++++++++++++ 24 files changed, 791 insertions(+), 49 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/AnnotatedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/AnnotatedMapperDecorator.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratedWithAnnotatedWithTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/TestAnnotation.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaAnnotateWithMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaAnnotateWithWithMapperDecorator.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaDecoratorAnnotateWithTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330AnnotateWithMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330AnnotateWithMapperDecorator.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330DecoratorAnnotateWithTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/AnnotateMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/AnnotateMapperDecorator.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomAnnotateMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomAnnotateMapperDecorator.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomComponent.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomPrimary.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/SpringDecoratorAnnotateWithTest.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java index b7dc0effcb..df7940da31 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java @@ -7,14 +7,15 @@ import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.SortedSet; import javax.lang.model.element.TypeElement; +import org.mapstruct.ap.internal.gem.DecoratedWithGem; import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.option.Options; -import org.mapstruct.ap.internal.gem.DecoratedWithGem; import org.mapstruct.ap.internal.version.VersionInformation; /** @@ -33,6 +34,7 @@ public static class Builder extends GeneratedTypeBuilder { private String implName; private String implPackage; private boolean suppressGeneratorTimestamp; + private Set customAnnotations; public Builder() { super( Builder.class ); @@ -68,6 +70,11 @@ public Builder suppressGeneratorTimestamp(boolean suppressGeneratorTimestamp) { return this; } + public Builder additionalAnnotations(Set customAnnotations) { + this.customAnnotations = customAnnotations; + return this; + } + public Decorator build() { String implementationName = implName.replace( Mapper.CLASS_NAME_PLACEHOLDER, Mapper.getFlatName( mapperElement ) ); @@ -95,7 +102,8 @@ public Decorator build() { suppressGeneratorTimestamp, Accessibility.fromModifiers( mapperElement.getModifiers() ), extraImportedTypes, - decoratorConstructor + decoratorConstructor, + customAnnotations ); } } @@ -110,7 +118,8 @@ private Decorator(TypeFactory typeFactory, String packageName, String name, Type Options options, VersionInformation versionInformation, boolean suppressGeneratorTimestamp, Accessibility accessibility, SortedSet extraImports, - DecoratorConstructor decoratorConstructor) { + DecoratorConstructor decoratorConstructor, + Set customAnnotations) { super( typeFactory, packageName, @@ -128,6 +137,11 @@ private Decorator(TypeFactory typeFactory, String packageName, String name, Type this.decoratorType = decoratorType; this.mapperType = mapperType; + + // Add custom annotations + if ( customAnnotations != null ) { + customAnnotations.forEach( this::addAnnotation ); + } } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java index 014ceda58e..bbd695412a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/AnnotationBasedComponentModelProcessor.java @@ -14,6 +14,7 @@ import java.util.stream.Collectors; import javax.lang.model.element.TypeElement; +import org.mapstruct.ap.internal.gem.InjectionStrategyGem; import org.mapstruct.ap.internal.model.AnnotatedConstructor; import org.mapstruct.ap.internal.model.AnnotatedSetter; import org.mapstruct.ap.internal.model.Annotation; @@ -24,7 +25,6 @@ import org.mapstruct.ap.internal.model.MapperReference; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.gem.InjectionStrategyGem; import org.mapstruct.ap.internal.model.source.MapperOptions; /** @@ -88,7 +88,7 @@ else if ( injectionStrategy == InjectionStrategyGem.SETTER ) { protected void adjustDecorator(Mapper mapper, InjectionStrategyGem injectionStrategy) { Decorator decorator = mapper.getDecorator(); - for ( Annotation typeAnnotation : getDecoratorAnnotations() ) { + for ( Annotation typeAnnotation : getDecoratorAnnotations( decorator ) ) { decorator.addAnnotation( typeAnnotation ); } @@ -308,7 +308,7 @@ protected Field replacementMapperReference(Field originalReference, List getDecoratorAnnotations() { + protected List getDecoratorAnnotations(Decorator decorator) { return Collections.emptyList(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java index 999c0a51df..86c95d612b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/JakartaComponentProcessor.java @@ -11,6 +11,7 @@ import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; +import org.mapstruct.ap.internal.model.Decorator; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.annotation.AnnotationElement; import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType; @@ -39,7 +40,7 @@ protected List getTypeAnnotations(Mapper mapper) { } @Override - protected List getDecoratorAnnotations() { + protected List getDecoratorAnnotations(Decorator decorator) { return Arrays.asList( singleton(), named() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java index 00ba72a079..7770eff16c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/Jsr330ComponentProcessor.java @@ -11,6 +11,7 @@ import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; +import org.mapstruct.ap.internal.model.Decorator; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.annotation.AnnotationElement; import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType; @@ -42,7 +43,7 @@ protected List getTypeAnnotations(Mapper mapper) { } @Override - protected List getDecoratorAnnotations() { + protected List getDecoratorAnnotations(Decorator decorator) { return Arrays.asList( singleton(), named() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 3b38a05b59..4fbed6cbec 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -33,6 +33,7 @@ import org.mapstruct.ap.internal.gem.MappingInheritanceStrategyGem; import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.model.AdditionalAnnotationsBuilder; +import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.BeanMappingMethod; import org.mapstruct.ap.internal.model.ContainerMappingMethod; import org.mapstruct.ap.internal.model.ContainerMappingMethodBuilder; @@ -287,6 +288,9 @@ else if ( constructor.getParameters().size() == 1 ) { messager.printMessage( element, decoratedWith.mirror(), Message.DECORATOR_CONSTRUCTOR ); } + // Get annotations from the decorator class + Set decoratorAnnotations = additionalAnnotationsBuilder.getProcessedAnnotations( decoratorElement ); + Decorator decorator = new Decorator.Builder() .elementUtils( elementUtils ) .typeFactory( typeFactory ) @@ -300,6 +304,7 @@ else if ( constructor.getParameters().size() == 1 ) { .implPackage( mapperOptions.implementationPackage() ) .extraImports( getExtraImports( element, mapperOptions ) ) .suppressGeneratorTimestamp( mapperOptions.suppressTimestampInGenerated() ) + .additionalAnnotations( decoratorAnnotations ) .build(); return decorator; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java index 84a672bcc9..c31ac51f06 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/SpringComponentProcessor.java @@ -9,20 +9,22 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.model.Annotation; +import org.mapstruct.ap.internal.model.Decorator; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.annotation.AnnotationElement; import org.mapstruct.ap.internal.model.annotation.AnnotationElement.AnnotationElementType; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; - import static javax.lang.model.element.ElementKind.PACKAGE; /** @@ -35,6 +37,9 @@ */ public class SpringComponentProcessor extends AnnotationBasedComponentModelProcessor { + private static final String SPRING_COMPONENT_ANNOTATION = "org.springframework.stereotype.Component"; + private static final String SPRING_PRIMARY_ANNOTATION = "org.springframework.context.annotation.Primary"; + @Override protected String getComponentModelIdentifier() { return MappingConstantsGem.ComponentModelGem.SPRING; @@ -54,12 +59,37 @@ protected List getTypeAnnotations(Mapper mapper) { return typeAnnotations; } + /** + * Returns the annotations that need to be added to the generated decorator, filtering out any annotations + * that are already present or represented as meta-annotations. + * + * @param decorator the decorator to process + * @return A list of annotations that should be added to the generated decorator. + */ @Override - protected List getDecoratorAnnotations() { - return Arrays.asList( - component(), - primary() - ); + protected List getDecoratorAnnotations(Decorator decorator) { + Set desiredAnnotationNames = new LinkedHashSet<>(); + desiredAnnotationNames.add( SPRING_COMPONENT_ANNOTATION ); + desiredAnnotationNames.add( SPRING_PRIMARY_ANNOTATION ); + List decoratorAnnotations = decorator.getAnnotations(); + if ( !decoratorAnnotations.isEmpty() ) { + Set handledElements = new HashSet<>(); + for ( Annotation annotation : decoratorAnnotations ) { + removeAnnotationsPresentOnElement( + annotation.getType().getTypeElement(), + desiredAnnotationNames, + handledElements + ); + if ( desiredAnnotationNames.isEmpty() ) { + // If all annotations are removed, we can stop further processing + return Collections.emptyList(); + } + } + } + + return desiredAnnotationNames.stream() + .map( this::createAnnotation ) + .collect( Collectors.toList() ); } @Override @@ -82,8 +112,12 @@ protected boolean requiresGenerationOfDecoratorClass() { return true; } + private Annotation createAnnotation(String canonicalName) { + return new Annotation( getTypeFactory().getType( canonicalName ) ); + } + private Annotation autowired() { - return new Annotation( getTypeFactory().getType( "org.springframework.beans.factory.annotation.Autowired" ) ); + return createAnnotation( "org.springframework.beans.factory.annotation.Autowired" ); } private Annotation qualifierDelegate() { @@ -96,34 +130,51 @@ private Annotation qualifierDelegate() { ) ) ); } - private Annotation primary() { - return new Annotation( getTypeFactory().getType( "org.springframework.context.annotation.Primary" ) ); - } - private Annotation component() { - return new Annotation( getTypeFactory().getType( "org.springframework.stereotype.Component" ) ); + return createAnnotation( SPRING_COMPONENT_ANNOTATION ); } private boolean isAlreadyAnnotatedAsSpringStereotype(Mapper mapper) { - Set handledElements = new HashSet<>(); - return mapper.getAnnotations() - .stream() - .anyMatch( - annotation -> isOrIncludesComponentAnnotation( annotation, handledElements ) - ); - } + Set desiredAnnotationNames = new LinkedHashSet<>(); + desiredAnnotationNames.add( SPRING_COMPONENT_ANNOTATION ); + + List mapperAnnotations = mapper.getAnnotations(); + if ( !mapperAnnotations.isEmpty() ) { + Set handledElements = new HashSet<>(); + for ( Annotation annotation : mapperAnnotations ) { + removeAnnotationsPresentOnElement( + annotation.getType().getTypeElement(), + desiredAnnotationNames, + handledElements + ); + if ( desiredAnnotationNames.isEmpty() ) { + // If all annotations are removed, we can stop further processing + return true; + } + } + } - private boolean isOrIncludesComponentAnnotation(Annotation annotation, Set handledElements) { - return isOrIncludesComponentAnnotation( - annotation.getType().getTypeElement(), handledElements - ); + return false; } - private boolean isOrIncludesComponentAnnotation(Element element, Set handledElements) { - if ( "org.springframework.stereotype.Component".equals( - ( (TypeElement) element ).getQualifiedName().toString() - )) { - return true; + /** + * Removes all the annotations and meta-annotations from {@code annotations} which are on the given element. + * + * @param element the element to check + * @param annotations the annotations to check for + * @param handledElements set of already handled elements to avoid infinite recursion + */ + private void removeAnnotationsPresentOnElement(Element element, Set annotations, + Set handledElements) { + if ( annotations.isEmpty() ) { + return; + } + if ( element instanceof TypeElement && + annotations.remove( ( (TypeElement) element ).getQualifiedName().toString() ) ) { + if ( annotations.isEmpty() ) { + // If all annotations are removed, we can stop further processing + return; + } } for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) { @@ -132,17 +183,16 @@ private boolean isOrIncludesComponentAnnotation(Element element, Set ha if ( !isAnnotationInPackage( annotationMirrorElement, "java.lang.annotation" ) && !handledElements.contains( annotationMirrorElement ) ) { handledElements.add( annotationMirrorElement ); - boolean isOrIncludesComponentAnnotation = isOrIncludesComponentAnnotation( - annotationMirrorElement, handledElements - ); - - if ( isOrIncludesComponentAnnotation ) { - return true; + if ( annotations.remove( ( (TypeElement) annotationMirrorElement ).getQualifiedName().toString() ) ) { + if ( annotations.isEmpty() ) { + // If all annotations are removed, we can stop further processing + return; + } } + + removeAnnotationsPresentOnElement( element, annotations, handledElements ); } } - - return false; } private PackageElement getPackageOf( Element element ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/AnnotatedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/AnnotatedMapper.java new file mode 100644 index 0000000000..6da0d70c93 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/AnnotatedMapper.java @@ -0,0 +1,43 @@ +/* + * 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.decorator; + +import org.mapstruct.DecoratedWith; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +@DecoratedWith(AnnotatedMapperDecorator.class) +public interface AnnotatedMapper { + + AnnotatedMapper INSTANCE = Mappers.getMapper( AnnotatedMapper.class ); + + Target toTarget(Source source); + + class Source { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class Target { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/AnnotatedMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/AnnotatedMapperDecorator.java new file mode 100644 index 0000000000..6bf7a1fd66 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/AnnotatedMapperDecorator.java @@ -0,0 +1,18 @@ +/* + * 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.decorator; + +import org.mapstruct.AnnotateWith; + +@AnnotateWith(value = TestAnnotation.class, elements = @AnnotateWith.Element(strings = "decoratorValue")) +public abstract class AnnotatedMapperDecorator implements AnnotatedMapper { + + private final AnnotatedMapper delegate; + + public AnnotatedMapperDecorator(AnnotatedMapper delegate) { + this.delegate = delegate; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratedWithAnnotatedWithTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratedWithAnnotatedWithTest.java new file mode 100644 index 0000000000..1e3d574a6d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratedWithAnnotatedWithTest.java @@ -0,0 +1,32 @@ +/* + * 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.decorator; + +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; + +/** + * Test for the application of @AnnotatedWith on decorator classes. + */ +@IssueKey("3659") +@WithClasses({ + TestAnnotation.class, + AnnotatedMapper.class, + AnnotatedMapperDecorator.class +}) +public class DecoratedWithAnnotatedWithTest { + + @ProcessorTest + public void shouldApplyAnnotationFromDecorator() { + Class implementationClass = AnnotatedMapper.INSTANCE.getClass(); + + assertThat( implementationClass ).hasAnnotation( TestAnnotation.class ); + assertThat( implementationClass.getAnnotation( TestAnnotation.class ).value() ).isEqualTo( "decoratorValue" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/TestAnnotation.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/TestAnnotation.java new file mode 100644 index 0000000000..742184d261 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/TestAnnotation.java @@ -0,0 +1,20 @@ +/* + * 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.decorator; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Test annotation for testing @AnnotatedWith on decorators. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface TestAnnotation { + String value() default ""; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaAnnotateWithMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaAnnotateWithMapper.java new file mode 100644 index 0000000000..a1a754158b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaAnnotateWithMapper.java @@ -0,0 +1,40 @@ +/* + * 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.decorator.jakarta.annotatewith; + +import org.mapstruct.DecoratedWith; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; + +/** + * A mapper using Jakarta component model with a decorator. + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA) +@DecoratedWith(JakartaAnnotateWithWithMapperDecorator.class) +public interface JakartaAnnotateWithMapper { + + /** + * Maps a person to a person DTO. + * + * @param person the person to map + * @return the person DTO + */ + @Mapping(target = "name", ignore = true) + PersonDto personToPersonDto(Person person); + + /** + * Maps an address to an address DTO. + * + * @param address the address to map + * @return the address DTO + */ + AddressDto addressToAddressDto(Address address); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaAnnotateWithWithMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaAnnotateWithWithMapperDecorator.java new file mode 100644 index 0000000000..89a42aaf6e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaAnnotateWithWithMapperDecorator.java @@ -0,0 +1,31 @@ +/* + * 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.decorator.jakarta.annotatewith; + +import jakarta.inject.Inject; +import jakarta.inject.Named; +import org.mapstruct.AnnotateWith; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.mapstruct.ap.test.decorator.TestAnnotation; + +/** + * A decorator for {@link JakartaAnnotateWithMapper}. + */ +@AnnotateWith(value = TestAnnotation.class) +public abstract class JakartaAnnotateWithWithMapperDecorator implements JakartaAnnotateWithMapper { + + @Inject + @Named("org.mapstruct.ap.test.decorator.jakarta.annotatewith.JakartaAnnotateWithMapperImpl_") + private JakartaAnnotateWithMapper delegate; + + @Override + public PersonDto personToPersonDto(Person person) { + PersonDto dto = delegate.personToPersonDto( person ); + dto.setName( person.getFirstName() + " " + person.getLastName() ); + return dto; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaDecoratorAnnotateWithTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaDecoratorAnnotateWithTest.java new file mode 100644 index 0000000000..d98e26b217 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jakarta/annotatewith/JakartaDecoratorAnnotateWithTest.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.decorator.jakarta.annotatewith; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.mapstruct.ap.test.decorator.TestAnnotation; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJakartaInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static java.lang.System.lineSeparator; + +/** + * Test for the application of @AnnotateWith on decorator classes with Jakarta component model. + */ +@IssueKey("3659") +@WithClasses({ + Person.class, + PersonDto.class, + Address.class, + AddressDto.class, + JakartaAnnotateWithMapper.class, + TestAnnotation.class, + JakartaAnnotateWithWithMapperDecorator.class +}) +@WithJakartaInject +public class JakartaDecoratorAnnotateWithTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + public void hasCorrectImports() { + // check the decorator + generatedSource.forMapper( JakartaAnnotateWithMapper.class ) + .content() + .contains( "import jakarta.inject.Inject;" ) + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "@TestAnnotation" ) + .contains( "@Singleton" + lineSeparator() + "@Named" ) + .doesNotContain( "javax.inject" ); + // check the plain mapper + generatedSource.forDecoratedMapper( JakartaAnnotateWithMapper.class ).content() + .contains( "import jakarta.inject.Named;" ) + .contains( "import jakarta.inject.Singleton;" ) + .contains( "@Singleton" + lineSeparator() + "@Named" ) + .doesNotContain( "javax.inject" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java index be1a67cf09..a29310b9ed 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/Jsr330DecoratorTest.java @@ -18,6 +18,7 @@ import org.mapstruct.ap.test.decorator.AddressDto; import org.mapstruct.ap.test.decorator.Person; import org.mapstruct.ap.test.decorator.PersonDto; +import org.mapstruct.ap.test.decorator.jsr330.annotatewith.Jsr330DecoratorAnnotateWithTest; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; @@ -27,6 +28,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; import static java.lang.System.lineSeparator; import static org.assertj.core.api.Assertions.assertThat; @@ -45,7 +47,13 @@ PersonMapperDecorator.class }) @IssueKey("592") -@ComponentScan(basePackageClasses = Jsr330DecoratorTest.class) +@ComponentScan( + basePackageClasses = Jsr330DecoratorTest.class, + excludeFilters = @ComponentScan.Filter( + type = FilterType.ASSIGNABLE_TYPE, + classes = { Jsr330DecoratorAnnotateWithTest.class } + ) +) @Configuration @WithJavaxInject @DisabledOnJre(JRE.OTHER) diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330AnnotateWithMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330AnnotateWithMapper.java new file mode 100644 index 0000000000..7e6ab8b7bd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330AnnotateWithMapper.java @@ -0,0 +1,28 @@ +/* + * 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.decorator.jsr330.annotatewith; + +import org.mapstruct.DecoratedWith; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; + +/** + * A mapper using JSR-330 component model with a decorator. + */ +@Mapper(componentModel = MappingConstants.ComponentModel.JSR330) +@DecoratedWith(Jsr330AnnotateWithMapperDecorator.class) +public interface Jsr330AnnotateWithMapper { + + @Mapping(target = "name", ignore = true) + PersonDto personToPersonDto(Person person); + + AddressDto addressToAddressDto(Address address); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330AnnotateWithMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330AnnotateWithMapperDecorator.java new file mode 100644 index 0000000000..96cee9f824 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330AnnotateWithMapperDecorator.java @@ -0,0 +1,32 @@ +/* + * 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.decorator.jsr330.annotatewith; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.mapstruct.ap.test.decorator.TestAnnotation; + +/** + * A decorator for {@link Jsr330AnnotateWithMapper}. + */ +@AnnotateWith(value = TestAnnotation.class) +public abstract class Jsr330AnnotateWithMapperDecorator implements Jsr330AnnotateWithMapper { + + @Inject + @Named("org.mapstruct.ap.test.decorator.jsr330.annotatewith.Jsr330AnnotateWithMapperImpl_") + private Jsr330AnnotateWithMapper delegate; + + @Override + public PersonDto personToPersonDto(Person person) { + PersonDto dto = delegate.personToPersonDto( person ); + dto.setName( person.getFirstName() + " " + person.getLastName() ); + return dto; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330DecoratorAnnotateWithTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330DecoratorAnnotateWithTest.java new file mode 100644 index 0000000000..19d77dd5cb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/jsr330/annotatewith/Jsr330DecoratorAnnotateWithTest.java @@ -0,0 +1,95 @@ +/* + * 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.decorator.jsr330.annotatewith; + +import java.util.Calendar; +import javax.inject.Inject; +import javax.inject.Named; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.mapstruct.ap.test.decorator.TestAnnotation; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJavaxInject; +import org.mapstruct.ap.testutil.runner.GeneratedSource; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for the application of @AnnotateWith on decorator classes with JSR-330 component model. + */ +@IssueKey("3659") +@WithClasses({ + Person.class, + PersonDto.class, + Address.class, + AddressDto.class, + Jsr330AnnotateWithMapper.class, + Jsr330AnnotateWithMapperDecorator.class, + TestAnnotation.class +}) +@ComponentScan(basePackageClasses = Jsr330DecoratorAnnotateWithTest.class) +@Configuration +@WithJavaxInject +@DisabledOnJre(JRE.OTHER) +public class Jsr330DecoratorAnnotateWithTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @Inject + @Named + private Jsr330AnnotateWithMapper jsr330AnnotateWithMapper; + + private ConfigurableApplicationContext context; + + @BeforeEach + public void springUp() { + context = new AnnotationConfigApplicationContext( getClass() ); + context.getAutowireCapableBeanFactory().autowireBean( this ); + } + + @AfterEach + public void springDown() { + if ( context != null ) { + context.close(); + } + } + + @ProcessorTest + public void shouldContainCustomAnnotation() { + generatedSource.forMapper( Jsr330AnnotateWithMapper.class ) + .content() + .contains( "@TestAnnotation" ); + } + + @ProcessorTest + public void shouldInvokeDecoratorMethods() { + Calendar birthday = Calendar.getInstance(); + birthday.set( 1928, Calendar.MAY, 23 ); + Person person = new Person( "Gary", "Crant", birthday.getTime(), new Address( "42 Ocean View Drive" ) ); + + PersonDto personDto = jsr330AnnotateWithMapper.personToPersonDto( person ); + + assertThat( personDto ).isNotNull(); + assertThat( personDto.getName() ).isEqualTo( "Gary Crant" ); + assertThat( personDto.getAddress() ).isNotNull(); + assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/AnnotateMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/AnnotateMapper.java new file mode 100644 index 0000000000..0ba8e88299 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/AnnotateMapper.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.ap.test.decorator.spring.annotatewith; + +import org.mapstruct.DecoratedWith; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; + +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +@DecoratedWith(AnnotateMapperDecorator.class) +public interface AnnotateMapper { + + @Mapping(target = "name", ignore = true) + PersonDto personToPersonDto(Person person); + + AddressDto addressToAddressDto(Address address); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/AnnotateMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/AnnotateMapperDecorator.java new file mode 100644 index 0000000000..cff0a4c148 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/AnnotateMapperDecorator.java @@ -0,0 +1,30 @@ +/* + * 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.decorator.spring.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +@AnnotateWith(value = Component.class, elements = @AnnotateWith.Element(strings = "decoratorComponent")) +@AnnotateWith(value = Primary.class) +public abstract class AnnotateMapperDecorator implements AnnotateMapper { + + @Autowired + @Qualifier("delegate") + private AnnotateMapper delegate; + + @Override + public PersonDto personToPersonDto(Person person) { + PersonDto dto = delegate.personToPersonDto( person ); + dto.setName( person.getFirstName() + " " + person.getLastName() ); + return dto; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomAnnotateMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomAnnotateMapper.java new file mode 100644 index 0000000000..9e2e35676c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomAnnotateMapper.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.ap.test.decorator.spring.annotatewith; + +import org.mapstruct.DecoratedWith; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; + +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +@DecoratedWith(CustomAnnotateMapperDecorator.class) +public interface CustomAnnotateMapper { + + @Mapping(target = "name", ignore = true) + PersonDto personToPersonDto(Person person); + + AddressDto addressToAddressDto(Address address); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomAnnotateMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomAnnotateMapperDecorator.java new file mode 100644 index 0000000000..3668d98b04 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomAnnotateMapperDecorator.java @@ -0,0 +1,28 @@ +/* + * 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.decorator.spring.annotatewith; + +import org.mapstruct.AnnotateWith; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +@AnnotateWith(value = CustomComponent.class, elements = @AnnotateWith.Element(strings = "customComponentDecorator")) +@AnnotateWith(value = CustomPrimary.class) +public abstract class CustomAnnotateMapperDecorator implements CustomAnnotateMapper { + + @Autowired + @Qualifier("delegate") + private CustomAnnotateMapper delegate; + + @Override + public PersonDto personToPersonDto(Person person) { + PersonDto dto = delegate.personToPersonDto( person ); + dto.setName( person.getFirstName() + " " + person.getLastName() ); + return dto; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomComponent.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomComponent.java new file mode 100644 index 0000000000..be71249f2a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomComponent.java @@ -0,0 +1,20 @@ +/* + * 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.decorator.spring.annotatewith; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.stereotype.Component; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Component +public @interface CustomComponent { + String value() default ""; +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomPrimary.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomPrimary.java new file mode 100644 index 0000000000..4e18bdbc7d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/CustomPrimary.java @@ -0,0 +1,19 @@ +/* + * 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.decorator.spring.annotatewith; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Primary; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Primary +public @interface CustomPrimary { +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/SpringDecoratorAnnotateWithTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/SpringDecoratorAnnotateWithTest.java new file mode 100644 index 0000000000..7b3911c22b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/spring/annotatewith/SpringDecoratorAnnotateWithTest.java @@ -0,0 +1,118 @@ +/* + * 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.decorator.spring.annotatewith; + +import java.util.Calendar; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.decorator.Address; +import org.mapstruct.ap.test.decorator.AddressDto; +import org.mapstruct.ap.test.decorator.Person; +import org.mapstruct.ap.test.decorator.PersonDto; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithSpring; +import org.mapstruct.ap.testutil.runner.GeneratedSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for the application of @AnnotateWith on decorator classes with Spring component model. + */ +@IssueKey("3659") +@WithClasses({ + Person.class, + PersonDto.class, + Address.class, + AddressDto.class, + AnnotateMapper.class, + AnnotateMapperDecorator.class, + CustomComponent.class, + CustomPrimary.class, + CustomAnnotateMapper.class, + CustomAnnotateMapperDecorator.class +}) +@WithSpring +@ComponentScan(basePackageClasses = SpringDecoratorAnnotateWithTest.class) +@Configuration +public class SpringDecoratorAnnotateWithTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @Autowired + private AnnotateMapper annotateMapper; + + @Autowired + private CustomAnnotateMapper customAnnotateMapper; + + private ConfigurableApplicationContext context; + + @BeforeEach + public void springUp() { + context = new AnnotationConfigApplicationContext( getClass() ); + context.getAutowireCapableBeanFactory().autowireBean( this ); + } + + @AfterEach + public void springDown() { + if ( context != null ) { + context.close(); + } + } + + @ProcessorTest + public void shouldNotDuplicateComponentAnnotation() { + generatedSource.forMapper( AnnotateMapper.class ) + .content() + .contains( "@Component(value = \"decoratorComponent\")", "@Primary" ) + .doesNotContain( "@Component" + System.lineSeparator() ); + } + + @ProcessorTest + public void shouldNotDuplicateCustomComponentAnnotation() { + generatedSource.forMapper( CustomAnnotateMapper.class ) + .content() + .contains( "@CustomComponent(value = \"customComponentDecorator\")", "@CustomPrimary" ) + .doesNotContain( "@Component" ); + } + + @ProcessorTest + public void shouldInvokeAnnotateDecoratorMethods() { + Calendar birthday = Calendar.getInstance(); + birthday.set( 1928, Calendar.MAY, 23 ); + Person person = new Person( "Gary", "Crant", birthday.getTime(), new Address( "42 Ocean View Drive" ) ); + + PersonDto personDto = annotateMapper.personToPersonDto( person ); + + assertThat( personDto ).isNotNull(); + assertThat( personDto.getName() ).isEqualTo( "Gary Crant" ); + assertThat( personDto.getAddress() ).isNotNull(); + assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); + } + + @ProcessorTest + public void shouldInvokeCustomAnnotateDecoratorMethods() { + Calendar birthday = Calendar.getInstance(); + birthday.set( 1928, Calendar.MAY, 23 ); + Person person = new Person( "Gary", "Crant", birthday.getTime(), new Address( "42 Ocean View Drive" ) ); + + PersonDto personDto = customAnnotateMapper.personToPersonDto( person ); + + assertThat( personDto ).isNotNull(); + assertThat( personDto.getName() ).isEqualTo( "Gary Crant" ); + assertThat( personDto.getAddress() ).isNotNull(); + assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" ); + } +} From 9847eaf195cb891c5a47df96db2b90a1b335b75b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 31 May 2025 18:14:13 +0200 Subject: [PATCH 494/597] #3876: Move Windows and Mac OS builds outside of the main workflow --- .github/workflows/macos.yml | 20 ++++++++++++++++++++ .github/workflows/main.yml | 24 ------------------------ .github/workflows/windows.yml | 20 ++++++++++++++++++++ 3 files changed, 40 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/macos.yml create mode 100644 .github/workflows/windows.yml diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000000..833bb6ba39 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,20 @@ +name: Mac OS CI + +on: push + +env: + MAVEN_ARGS: -V -B --no-transfer-progress -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 + +jobs: + mac: + name: 'Mac OS' + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: 'Set up JDK 21' + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + - name: 'Test' + run: ./mvnw ${MAVEN_ARGS} install diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 166466348d..b6a2ef371e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -58,27 +58,3 @@ jobs: java-version: ${{ matrix.java }} - name: 'Run integration tests' run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest - windows: - name: 'Windows' - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - name: 'Set up JDK 21' - uses: actions/setup-java@v4 - with: - distribution: 'zulu' - java-version: 21 - - name: 'Test' - run: ./mvnw %MAVEN_ARGS% install - mac: - name: 'Mac OS' - runs-on: macos-latest - steps: - - uses: actions/checkout@v3 - - name: 'Set up JDK 21' - uses: actions/setup-java@v4 - with: - distribution: 'zulu' - java-version: 21 - - name: 'Test' - run: ./mvnw ${MAVEN_ARGS} install diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000000..bda38f8783 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,20 @@ +name: Windows CI + +on: push + +env: + MAVEN_ARGS: -V -B --no-transfer-progress -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 + +jobs: + windows: + name: 'Windows' + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: 'Set up JDK 21' + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + - name: 'Test' + run: ./mvnw %MAVEN_ARGS% install From 46ce011e4bd9b9fbcc0f0d337d5ad631f5d214b2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 1 Jun 2025 07:52:18 +0200 Subject: [PATCH 495/597] Refactor options and add an enum (#3877) --- .../org/mapstruct/ap/MappingProcessor.java | 101 ++++++------------ .../internal/model/source/DefaultOptions.java | 20 ++-- .../ap/internal/option/MappingOption.java | 43 ++++++++ .../mapstruct/ap/internal/option/Options.java | 89 +++++++-------- 4 files changed, 128 insertions(+), 125 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/option/MappingOption.java diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index b34b133262..a66762d739 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -13,17 +13,16 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; -import javax.annotation.processing.SupportedOptions; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -35,9 +34,8 @@ import javax.tools.Diagnostic.Kind; import org.mapstruct.ap.internal.gem.MapperGem; -import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; -import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.model.Mapper; +import org.mapstruct.ap.internal.option.MappingOption; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext; import org.mapstruct.ap.internal.processor.ModelElementProcessor; @@ -84,19 +82,6 @@ * @author Gunnar Morling */ @SupportedAnnotationTypes("org.mapstruct.Mapper") -@SupportedOptions({ - MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP, - MappingProcessor.SUPPRESS_GENERATOR_VERSION_INFO_COMMENT, - MappingProcessor.UNMAPPED_TARGET_POLICY, - MappingProcessor.UNMAPPED_SOURCE_POLICY, - MappingProcessor.DEFAULT_COMPONENT_MODEL, - MappingProcessor.DEFAULT_INJECTION_STRATEGY, - MappingProcessor.DISABLE_BUILDERS, - MappingProcessor.VERBOSE, - MappingProcessor.NULL_VALUE_ITERABLE_MAPPING_STRATEGY, - MappingProcessor.NULL_VALUE_MAP_MAPPING_STRATEGY, - MappingProcessor.DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR, -}) public class MappingProcessor extends AbstractProcessor { /** @@ -104,20 +89,32 @@ public class MappingProcessor extends AbstractProcessor { */ private static final boolean ANNOTATIONS_CLAIMED_EXCLUSIVELY = false; - protected static final String SUPPRESS_GENERATOR_TIMESTAMP = "mapstruct.suppressGeneratorTimestamp"; - protected static final String SUPPRESS_GENERATOR_VERSION_INFO_COMMENT = - "mapstruct.suppressGeneratorVersionInfoComment"; - protected static final String UNMAPPED_TARGET_POLICY = "mapstruct.unmappedTargetPolicy"; - protected static final String UNMAPPED_SOURCE_POLICY = "mapstruct.unmappedSourcePolicy"; - protected static final String DEFAULT_COMPONENT_MODEL = "mapstruct.defaultComponentModel"; - protected static final String DEFAULT_INJECTION_STRATEGY = "mapstruct.defaultInjectionStrategy"; - protected static final String ALWAYS_GENERATE_SERVICE_FILE = "mapstruct.alwaysGenerateServicesFile"; - protected static final String DISABLE_BUILDERS = "mapstruct.disableBuilders"; - protected static final String VERBOSE = "mapstruct.verbose"; - protected static final String NULL_VALUE_ITERABLE_MAPPING_STRATEGY = "mapstruct.nullValueIterableMappingStrategy"; - protected static final String NULL_VALUE_MAP_MAPPING_STRATEGY = "mapstruct.nullValueMapMappingStrategy"; - protected static final String DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR = - "mapstruct.disableLifecycleOverloadDeduplicateSelector"; + // CHECKSTYLE:OFF + // Deprecated options, kept for backwards compatibility. + // They will be removed in a future release. + @Deprecated + protected static final String SUPPRESS_GENERATOR_TIMESTAMP = MappingOption.SUPPRESS_GENERATOR_TIMESTAMP.getOptionName(); + @Deprecated + protected static final String SUPPRESS_GENERATOR_VERSION_INFO_COMMENT = MappingOption.SUPPRESS_GENERATOR_VERSION_INFO_COMMENT.getOptionName(); + @Deprecated + protected static final String UNMAPPED_TARGET_POLICY = MappingOption.UNMAPPED_TARGET_POLICY.getOptionName(); + @Deprecated + protected static final String UNMAPPED_SOURCE_POLICY = MappingOption.UNMAPPED_SOURCE_POLICY.getOptionName(); + @Deprecated + protected static final String DEFAULT_COMPONENT_MODEL = MappingOption.DEFAULT_COMPONENT_MODEL.getOptionName(); + @Deprecated + protected static final String DEFAULT_INJECTION_STRATEGY = MappingOption.DEFAULT_INJECTION_STRATEGY.getOptionName(); + @Deprecated + protected static final String ALWAYS_GENERATE_SERVICE_FILE = MappingOption.ALWAYS_GENERATE_SERVICE_FILE.getOptionName(); + @Deprecated + protected static final String DISABLE_BUILDERS = MappingOption.DISABLE_BUILDERS.getOptionName(); + @Deprecated + protected static final String VERBOSE = MappingOption.VERBOSE.getOptionName(); + @Deprecated + protected static final String NULL_VALUE_ITERABLE_MAPPING_STRATEGY = MappingOption.NULL_VALUE_ITERABLE_MAPPING_STRATEGY.getOptionName(); + @Deprecated + protected static final String NULL_VALUE_MAP_MAPPING_STRATEGY = MappingOption.NULL_VALUE_MAP_MAPPING_STRATEGY.getOptionName(); + // CHECKSTYLE:ON private final Set additionalSupportedOptions; private final String additionalSupportedOptionsError; @@ -156,7 +153,7 @@ public MappingProcessor() { public synchronized void init(ProcessingEnvironment processingEnv) { super.init( processingEnv ); - options = createOptions(); + options = new Options( processingEnv.getOptions() ); annotationProcessorContext = new AnnotationProcessorContext( processingEnv.getElementUtils(), processingEnv.getTypeUtils(), @@ -171,34 +168,6 @@ public synchronized void init(ProcessingEnvironment processingEnv) { } } - private Options createOptions() { - String unmappedTargetPolicy = processingEnv.getOptions().get( UNMAPPED_TARGET_POLICY ); - String unmappedSourcePolicy = processingEnv.getOptions().get( UNMAPPED_SOURCE_POLICY ); - String nullValueIterableMappingStrategy = processingEnv.getOptions() - .get( NULL_VALUE_ITERABLE_MAPPING_STRATEGY ); - String nullValueMapMappingStrategy = processingEnv.getOptions().get( NULL_VALUE_MAP_MAPPING_STRATEGY ); - String disableLifecycleOverloadDeduplicateSelector = processingEnv.getOptions() - .get( DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR ); - - return new Options( - Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ), - Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_VERSION_INFO_COMMENT ) ), - unmappedTargetPolicy != null ? ReportingPolicyGem.valueOf( unmappedTargetPolicy.toUpperCase() ) : null, - unmappedSourcePolicy != null ? ReportingPolicyGem.valueOf( unmappedSourcePolicy.toUpperCase() ) : null, - processingEnv.getOptions().get( DEFAULT_COMPONENT_MODEL ), - processingEnv.getOptions().get( DEFAULT_INJECTION_STRATEGY ), - Boolean.parseBoolean( processingEnv.getOptions().get( ALWAYS_GENERATE_SERVICE_FILE ) ), - Boolean.parseBoolean( processingEnv.getOptions().get( DISABLE_BUILDERS ) ), - Boolean.parseBoolean( processingEnv.getOptions().get( VERBOSE ) ), - nullValueIterableMappingStrategy != null ? - NullValueMappingStrategyGem.valueOf( nullValueIterableMappingStrategy.toUpperCase( Locale.ROOT ) ) : - null, - nullValueMapMappingStrategy != null ? - NullValueMappingStrategyGem.valueOf( nullValueMapMappingStrategy.toUpperCase( Locale.ROOT ) ) : null, - Boolean.parseBoolean( disableLifecycleOverloadDeduplicateSelector ) - ); - } - @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); @@ -259,13 +228,11 @@ else if ( !deferredMappers.isEmpty() ) { @Override public Set getSupportedOptions() { - Set supportedOptions = super.getSupportedOptions(); - if ( additionalSupportedOptions.isEmpty() ) { - return supportedOptions; - } - Set allSupportedOptions = new HashSet<>( supportedOptions ); - allSupportedOptions.addAll( additionalSupportedOptions ); - return allSupportedOptions; + return Stream.concat( + Stream.of( MappingOption.values() ).map( MappingOption::getOptionName ), + additionalSupportedOptions.stream() + ) + .collect( Collectors.toSet() ); } /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java index e1f04fd941..8d76e65c75 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/DefaultOptions.java @@ -56,16 +56,18 @@ public Set imports() { @Override public ReportingPolicyGem unmappedTargetPolicy() { - if ( options.getUnmappedTargetPolicy() != null ) { - return options.getUnmappedTargetPolicy(); + ReportingPolicyGem unmappedTargetPolicy = options.getUnmappedTargetPolicy(); + if ( unmappedTargetPolicy != null ) { + return unmappedTargetPolicy; } return ReportingPolicyGem.valueOf( mapper.unmappedTargetPolicy().getDefaultValue() ); } @Override public ReportingPolicyGem unmappedSourcePolicy() { - if ( options.getUnmappedSourcePolicy() != null ) { - return options.getUnmappedSourcePolicy(); + ReportingPolicyGem unmappedSourcePolicy = options.getUnmappedSourcePolicy(); + if ( unmappedSourcePolicy != null ) { + return unmappedSourcePolicy; } return ReportingPolicyGem.valueOf( mapper.unmappedSourcePolicy().getDefaultValue() ); } @@ -77,8 +79,9 @@ public ReportingPolicyGem typeConversionPolicy() { @Override public String componentModel() { - if ( options.getDefaultComponentModel() != null ) { - return options.getDefaultComponentModel(); + String defaultComponentModel = options.getDefaultComponentModel(); + if ( defaultComponentModel != null ) { + return defaultComponentModel; } return mapper.componentModel().getDefaultValue(); } @@ -97,8 +100,9 @@ public MappingInheritanceStrategyGem getMappingInheritanceStrategy() { @Override public InjectionStrategyGem getInjectionStrategy() { - if ( options.getDefaultInjectionStrategy() != null ) { - return InjectionStrategyGem.valueOf( options.getDefaultInjectionStrategy().toUpperCase() ); + String defaultInjectionStrategy = options.getDefaultInjectionStrategy(); + if ( defaultInjectionStrategy != null ) { + return InjectionStrategyGem.valueOf( defaultInjectionStrategy.toUpperCase() ); } return InjectionStrategyGem.valueOf( mapper.injectionStrategy().getDefaultValue() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/option/MappingOption.java b/processor/src/main/java/org/mapstruct/ap/internal/option/MappingOption.java new file mode 100644 index 0000000000..d35b2c0dc6 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/option/MappingOption.java @@ -0,0 +1,43 @@ +/* + * 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.internal.option; + +/** + * @author Filip Hrisafov + */ +public enum MappingOption { + + // CHECKSTYLE:OFF + SUPPRESS_GENERATOR_TIMESTAMP( "mapstruct.suppressGeneratorTimestamp" ), + SUPPRESS_GENERATOR_VERSION_INFO_COMMENT( "mapstruct.suppressGeneratorVersionInfoComment" ), + UNMAPPED_TARGET_POLICY("mapstruct.unmappedTargetPolicy"), + UNMAPPED_SOURCE_POLICY("mapstruct.unmappedSourcePolicy"), + DEFAULT_COMPONENT_MODEL("mapstruct.defaultComponentModel"), + DEFAULT_INJECTION_STRATEGY("mapstruct.defaultInjectionStrategy"), + ALWAYS_GENERATE_SERVICE_FILE("mapstruct.alwaysGenerateServicesFile"), + DISABLE_BUILDERS("mapstruct.disableBuilders"), + VERBOSE("mapstruct.verbose"), + NULL_VALUE_ITERABLE_MAPPING_STRATEGY("mapstruct.nullValueIterableMappingStrategy"), + NULL_VALUE_MAP_MAPPING_STRATEGY("mapstruct.nullValueMapMappingStrategy"), + DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR("mapstruct.disableLifecycleOverloadDeduplicateSelector"), + ; + // CHECKSTYLE:ON + + private final String optionName; + + MappingOption(String optionName) { + this.optionName = optionName; + } + + /** + * Returns the name of the option, which can be used in the compiler arguments. + * + * @return the name of the option + */ + public String getOptionName() { + return optionName; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java index 095c472c87..38474baf9c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/option/Options.java @@ -5,6 +5,9 @@ */ package org.mapstruct.ap.internal.option; +import java.util.Locale; +import java.util.Map; + import org.mapstruct.ap.internal.gem.NullValueMappingStrategyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; @@ -13,93 +16,79 @@ * * @author Andreas Gudian * @author Gunnar Morling + * @author Filip Hrisafov */ public class Options { - private final boolean suppressGeneratorTimestamp; - private final boolean suppressGeneratorVersionComment; - private final ReportingPolicyGem unmappedTargetPolicy; - private final ReportingPolicyGem unmappedSourcePolicy; - private final boolean alwaysGenerateSpi; - private final String defaultComponentModel; - private final String defaultInjectionStrategy; - private final boolean disableBuilders; - private final boolean verbose; - private final NullValueMappingStrategyGem nullValueIterableMappingStrategy; - private final NullValueMappingStrategyGem nullValueMapMappingStrategy; - private final boolean disableLifecycleOverloadDeduplicateSelector; - - //CHECKSTYLE:OFF - public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVersionComment, - ReportingPolicyGem unmappedTargetPolicy, - ReportingPolicyGem unmappedSourcePolicy, - String defaultComponentModel, String defaultInjectionStrategy, - boolean alwaysGenerateSpi, - boolean disableBuilders, - boolean verbose, - NullValueMappingStrategyGem nullValueIterableMappingStrategy, - NullValueMappingStrategyGem nullValueMapMappingStrategy, - boolean disableLifecycleOverloadDeduplicateSelector - ) { - //CHECKSTYLE:ON - this.suppressGeneratorTimestamp = suppressGeneratorTimestamp; - this.suppressGeneratorVersionComment = suppressGeneratorVersionComment; - this.unmappedTargetPolicy = unmappedTargetPolicy; - this.unmappedSourcePolicy = unmappedSourcePolicy; - this.defaultComponentModel = defaultComponentModel; - this.defaultInjectionStrategy = defaultInjectionStrategy; - this.alwaysGenerateSpi = alwaysGenerateSpi; - this.disableBuilders = disableBuilders; - this.verbose = verbose; - this.nullValueIterableMappingStrategy = nullValueIterableMappingStrategy; - this.nullValueMapMappingStrategy = nullValueMapMappingStrategy; - this.disableLifecycleOverloadDeduplicateSelector = disableLifecycleOverloadDeduplicateSelector; + + private final Map options; + + public Options(Map options) { + this.options = options; } public boolean isSuppressGeneratorTimestamp() { - return suppressGeneratorTimestamp; + return parseBoolean( MappingOption.SUPPRESS_GENERATOR_TIMESTAMP ); } public boolean isSuppressGeneratorVersionComment() { - return suppressGeneratorVersionComment; + return parseBoolean( MappingOption.SUPPRESS_GENERATOR_VERSION_INFO_COMMENT ); } public ReportingPolicyGem getUnmappedTargetPolicy() { - return unmappedTargetPolicy; + return parseEnum( MappingOption.UNMAPPED_TARGET_POLICY, ReportingPolicyGem.class ); } public ReportingPolicyGem getUnmappedSourcePolicy() { - return unmappedSourcePolicy; + return parseEnum( MappingOption.UNMAPPED_SOURCE_POLICY, ReportingPolicyGem.class ); } public String getDefaultComponentModel() { - return defaultComponentModel; + return options.get( MappingOption.DEFAULT_COMPONENT_MODEL.getOptionName() ); } public String getDefaultInjectionStrategy() { - return defaultInjectionStrategy; + return options.get( MappingOption.DEFAULT_INJECTION_STRATEGY.getOptionName() ); } public boolean isAlwaysGenerateSpi() { - return alwaysGenerateSpi; + return parseBoolean( MappingOption.ALWAYS_GENERATE_SERVICE_FILE ); } public boolean isDisableBuilders() { - return disableBuilders; + return parseBoolean( MappingOption.DISABLE_BUILDERS ); } public boolean isVerbose() { - return verbose; + return parseBoolean( MappingOption.VERBOSE ); } public NullValueMappingStrategyGem getNullValueIterableMappingStrategy() { - return nullValueIterableMappingStrategy; + return parseEnum( MappingOption.NULL_VALUE_ITERABLE_MAPPING_STRATEGY, NullValueMappingStrategyGem.class ); } public NullValueMappingStrategyGem getNullValueMapMappingStrategy() { - return nullValueMapMappingStrategy; + return parseEnum( MappingOption.NULL_VALUE_MAP_MAPPING_STRATEGY, NullValueMappingStrategyGem.class ); } public boolean isDisableLifecycleOverloadDeduplicateSelector() { - return disableLifecycleOverloadDeduplicateSelector; + return parseBoolean( MappingOption.DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR ); + } + + private boolean parseBoolean(MappingOption option) { + if ( options.isEmpty() ) { + return false; + } + return Boolean.parseBoolean( options.get( option.getOptionName() ) ); + } + + private > E parseEnum(MappingOption option, Class enumType) { + if ( options.isEmpty() ) { + return null; + } + String value = options.get( option.getOptionName() ); + if ( value == null ) { + return null; + } + return Enum.valueOf( enumType, value.toUpperCase( Locale.ROOT ) ); } } From d68819a233030b5d863e4d4645787a84e7db7d52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 20:02:23 +0000 Subject: [PATCH 496/597] Bump org.springframework:spring-context from 6.2.2 to 6.2.7 in /parent Bumps [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) from 6.2.2 to 6.2.7. - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.7) --- updated-dependencies: - dependency-name: org.springframework:spring-context dependency-version: 6.2.7 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index f6ad60a22a..35fc24e96b 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -34,7 +34,7 @@ 3.4.1 3.2.2 3.1.0 - 6.2.2 + 6.2.7 1.6.0 8.36.1 5.10.1 From f4d18181719b821bc319f53314b7c8e19e929e16 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 14 Jun 2025 21:33:19 +0200 Subject: [PATCH 497/597] Fix issue key in Issue3807Test --- .../java/org/mapstruct/ap/test/bugs/_3807/Issue3807Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Test.java index 3b895f3e11..db82472f98 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3807/Issue3807Test.java @@ -12,7 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat; @WithClasses(Issue3807Mapper.class) -@IssueKey("3087") +@IssueKey("3807") class Issue3807Test { @ProcessorTest From c90c93630e8580e330b09bcdd6f2bc99b08bdee6 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 14 Jun 2025 22:13:56 +0200 Subject: [PATCH 498/597] #3886: Records do not have property write accessors (apart from the record components) --- processor/pom.xml | 1 + .../ap/internal/model/common/Type.java | 4 +++ .../bugs/_3886/jdk21/Issue3886Mapper.java | 31 +++++++++++++++++++ .../test/bugs/_3886/jdk21/Issue3886Test.java | 25 +++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3886/jdk21/Issue3886Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3886/jdk21/Issue3886Test.java diff --git a/processor/pom.xml b/processor/pom.xml index 2341c4ddb0..87c3517018 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -286,6 +286,7 @@ org.apache.maven.plugins maven-compiler-plugin + ${minimum.java.version} org.mapstruct.tools.gem diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index d0f65ce97a..24f23ecea8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -796,6 +796,10 @@ public Map getPropertyPresenceCheckers() { * @return an unmodifiable map of all write accessors indexed by property name */ public Map getPropertyWriteAccessors( CollectionMappingStrategyGem cmStrategy ) { + if ( isRecord() ) { + // Records do not have setters, so we return an empty map + return Collections.emptyMap(); + } // collect all candidate target accessors List candidates = new ArrayList<>( getSetters() ); candidates.addAll( getAlternativeTargetAccessors() ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3886/jdk21/Issue3886Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3886/jdk21/Issue3886Mapper.java new file mode 100644 index 0000000000..aba305a0bf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3886/jdk21/Issue3886Mapper.java @@ -0,0 +1,31 @@ +/* + * 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._3886.jdk21; + +import java.time.LocalDate; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface Issue3886Mapper { + + RangeRecord map(LocalDate validFrom); + + record RangeRecord(LocalDate validFrom) { + + public RangeRecord restrictTo(RangeRecord other) { + return null; + } + + public void setName(String name) { + // This method is here to ensure that MapStruct won't treat it as a setter + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3886/jdk21/Issue3886Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3886/jdk21/Issue3886Test.java new file mode 100644 index 0000000000..93c068cbc7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3886/jdk21/Issue3886Test.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.ap.test.bugs._3886.jdk21; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.Compiler; + +/** + * @author Filip Hrisafov + */ +@WithClasses(Issue3886Mapper.class) +@IssueKey("3886") +class Issue3886Test { + + // The current version of the Eclipse compiler we use does not support records + @ProcessorTest(Compiler.JDK) + void shouldCompile() { + + } +} From e4bc1cdf1e39a3ff25f0a55b44bdb25837d49a69 Mon Sep 17 00:00:00 2001 From: Yang Tang Date: Sun, 15 Jun 2025 14:29:45 +0800 Subject: [PATCH 499/597] #3884 Ensure `NullValuePropertyMappingStrategy.SET_TO_DEFAULT` initializes empty collection/map when target is null Signed-off-by: TangYang --- ...anceSetterWrapperForCollectionsAndMaps.ftl | 4 + .../ap/test/bugs/_3884/DestinationType.java | 22 ++++ .../ap/test/bugs/_3884/Issue3884Mapper.java | 21 ++++ .../ap/test/bugs/_3884/Issue3884Test.java | 116 ++++++++++++++++++ .../ap/test/bugs/_3884/SourceType.java | 37 ++++++ .../DomainDtoWithNvmsDefaultMapperImpl.java | 24 ++++ .../DomainDtoWithNvmsDefaultMapperImpl.java | 24 ++++ 7 files changed, 248 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/DestinationType.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/Issue3884Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/Issue3884Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/SourceType.java diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl index cee722e2d3..95662c4c79 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl @@ -30,6 +30,10 @@ <@lib.handleLocalVarNullCheck needs_explicit_local_var=directAssignment> ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if directAssignment><@wrapLocalVarInCollectionInitializer/><#else><@lib.handleWithAssignmentOrNullCheckVar/>; + <#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && mapNullToDefault>else { + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.initTargetObject/>; + } + <#-- wraps the local variable in a collection initializer (new collection, or EnumSet.copyOf) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/DestinationType.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/DestinationType.java new file mode 100644 index 0000000000..467b58990c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/DestinationType.java @@ -0,0 +1,22 @@ +/* + * 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._3884; + +import java.util.List; +import java.util.Map; + +/** + * Destination type interface for testing null value property mapping strategy with Map properties. + */ +public interface DestinationType { + Map getAttributes(); + + void setAttributes(Map attributes); + + List getItems(); + + void setItems(List items); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/Issue3884Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/Issue3884Mapper.java new file mode 100644 index 0000000000..6f6f4f924d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/Issue3884Mapper.java @@ -0,0 +1,21 @@ +/* + * 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._3884; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +/** + * Mapper for testing null value property mapping strategy with Map properties. + */ +@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT) +public interface Issue3884Mapper { + Issue3884Mapper INSTANCE = Mappers.getMapper( Issue3884Mapper.class ); + + void update(@MappingTarget DestinationType destination, SourceType source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/Issue3884Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/Issue3884Test.java new file mode 100644 index 0000000000..6c789a0c29 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/Issue3884Test.java @@ -0,0 +1,116 @@ +/* + * 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._3884; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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; +import static org.assertj.core.api.Assertions.entry; + +/** + * Test for issue 3884: NullValuePropertyMappingStrategy.SET_TO_DEFAULT should set target Map/Collection to default + * when source and target are all null. + */ +@IssueKey("3884") +@WithClasses({ + DestinationType.class, + SourceType.class, + Issue3884Mapper.class +}) +public class Issue3884Test { + + @ProcessorTest + public void shouldSetTargetToDefaultWhenBothSourceAndTargetAreNull() { + DestinationType target = new SourceType(); + SourceType source = new SourceType(); + + assertThat( source.getAttributes() ).isNull(); + assertThat( target.getAttributes() ).isNull(); + assertThat( source.getItems() ).isNull(); + assertThat( target.getItems() ).isNull(); + + Issue3884Mapper.INSTANCE.update( target, source ); + + assertThat( target.getAttributes() ).isEmpty(); + assertThat( target.getItems() ).isEmpty(); + } + + @ProcessorTest + public void shouldClearTargetWhenSourceIsNullAndTargetIsInitialized() { + DestinationType target = new SourceType(); + SourceType source = new SourceType(); + + Map targetAttributes = new HashMap<>(); + targetAttributes.put( "targetKey", "targetValue" ); + target.setAttributes( targetAttributes ); + + List targetItems = new ArrayList<>(); + targetItems.add( "targetItem" ); + target.setItems( targetItems ); + + assertThat( source.getAttributes() ).isNull(); + assertThat( target.getAttributes() ).isNotEmpty(); + assertThat( source.getItems() ).isNull(); + assertThat( target.getItems() ).isNotEmpty(); + + Issue3884Mapper.INSTANCE.update( target, source ); + + assertThat( target.getAttributes() ).isEmpty(); + assertThat( target.getItems() ).isEmpty(); + } + + @ProcessorTest + public void shouldCopySourceToTargetWhenSourceIsInitializedAndTargetIsNull() { + DestinationType target = new SourceType(); + SourceType source = new SourceType(); + + source.setAttributes( Map.of( "sourceKey", "sourceValue" ) ); + source.setItems( List.of( "sourceItem" ) ); + + assertThat( source.getAttributes() ).isNotEmpty(); + assertThat( target.getAttributes() ).isNull(); + assertThat( source.getItems() ).isNotEmpty(); + assertThat( target.getItems() ).isNull(); + + Issue3884Mapper.INSTANCE.update( target, source ); + + assertThat( target.getAttributes() ).containsOnly( entry( "sourceKey", "sourceValue" ) ); + assertThat( target.getItems() ).containsExactly( "sourceItem" ); + } + + @ProcessorTest + public void shouldCopySourceToTargetWhenBothSourceAndTargetAreInitialized() { + DestinationType target = new SourceType(); + SourceType source = new SourceType(); + + source.setAttributes( Map.of( "sourceKey", "sourceValue" ) ); + source.setItems( List.of( "sourceItem" ) ); + + Map targetAttributes = new HashMap<>(); + targetAttributes.put( "targetKey", "targetValue" ); + target.setAttributes( targetAttributes ); + List targetItems = new ArrayList<>(); + targetItems.add( "targetItem" ); + target.setItems( targetItems ); + + assertThat( source.getAttributes() ).isNotEmpty(); + assertThat( target.getAttributes() ).isNotEmpty(); + assertThat( source.getItems() ).isNotEmpty(); + assertThat( target.getItems() ).isNotEmpty(); + + Issue3884Mapper.INSTANCE.update( target, source ); + + assertThat( target.getAttributes() ).containsOnly( entry( "sourceKey", "sourceValue" ) ); + assertThat( target.getItems() ).containsExactly( "sourceItem" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/SourceType.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/SourceType.java new file mode 100644 index 0000000000..be5c19e01c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3884/SourceType.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.bugs._3884; + +import java.util.List; +import java.util.Map; + +/** + * Source type class implementing DestinationType for testing null value property mapping strategy with Map properties. + */ +public class SourceType implements DestinationType { + private Map attributes; + private List items; + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + @Override + public List getItems() { + return items; + } + + @Override + public void setItems(List items) { + this.items = items; + } +} diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java index 76de94378b..f36e2e437d 100644 --- a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java @@ -67,6 +67,9 @@ public void update(Dto source, Domain target) { if ( list != null ) { target.setStrings( new LinkedHashSet( list ) ); } + else { + target.setStrings( new LinkedHashSet() ); + } } if ( target.getLongs() != null ) { Set set = stringListToLongSet( source.getStrings() ); @@ -83,6 +86,9 @@ public void update(Dto source, Domain target) { if ( set != null ) { target.setLongs( set ); } + else { + target.setLongs( new LinkedHashSet() ); + } } if ( target.getStringsInitialized() != null ) { List list1 = source.getStringsInitialized(); @@ -99,6 +105,9 @@ public void update(Dto source, Domain target) { if ( list1 != null ) { target.setStringsInitialized( new LinkedHashSet( list1 ) ); } + else { + target.setStringsInitialized( new LinkedHashSet() ); + } } if ( target.getLongsInitialized() != null ) { Set set1 = stringListToLongSet( source.getStringsInitialized() ); @@ -115,6 +124,9 @@ public void update(Dto source, Domain target) { if ( set1 != null ) { target.setLongsInitialized( set1 ); } + else { + target.setLongsInitialized( new LinkedHashSet() ); + } } if ( target.getStringsWithDefault() != null ) { List list2 = source.getStringsWithDefault(); @@ -157,6 +169,9 @@ public Domain updateWithReturn(Dto source, Domain target) { if ( list != null ) { target.setStrings( new LinkedHashSet( list ) ); } + else { + target.setStrings( new LinkedHashSet() ); + } } if ( target.getLongs() != null ) { Set set = stringListToLongSet( source.getStrings() ); @@ -173,6 +188,9 @@ public Domain updateWithReturn(Dto source, Domain target) { if ( set != null ) { target.setLongs( set ); } + else { + target.setLongs( new LinkedHashSet() ); + } } if ( target.getStringsInitialized() != null ) { List list1 = source.getStringsInitialized(); @@ -189,6 +207,9 @@ public Domain updateWithReturn(Dto source, Domain target) { if ( list1 != null ) { target.setStringsInitialized( new LinkedHashSet( list1 ) ); } + else { + target.setStringsInitialized( new LinkedHashSet() ); + } } if ( target.getLongsInitialized() != null ) { Set set1 = stringListToLongSet( source.getStringsInitialized() ); @@ -205,6 +226,9 @@ public Domain updateWithReturn(Dto source, Domain target) { if ( set1 != null ) { target.setLongsInitialized( set1 ); } + else { + target.setLongsInitialized( new LinkedHashSet() ); + } } if ( target.getStringsWithDefault() != null ) { List list2 = source.getStringsWithDefault(); diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java index f1bf0fa78f..87d2fccb26 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java @@ -67,6 +67,9 @@ public void update(Dto source, Domain target) { if ( list != null ) { target.setStrings( new LinkedHashSet( list ) ); } + else { + target.setStrings( new LinkedHashSet() ); + } } if ( target.getLongs() != null ) { Set set = stringListToLongSet( source.getStrings() ); @@ -83,6 +86,9 @@ public void update(Dto source, Domain target) { if ( set != null ) { target.setLongs( set ); } + else { + target.setLongs( new LinkedHashSet() ); + } } if ( target.getStringsInitialized() != null ) { List list1 = source.getStringsInitialized(); @@ -99,6 +105,9 @@ public void update(Dto source, Domain target) { if ( list1 != null ) { target.setStringsInitialized( new LinkedHashSet( list1 ) ); } + else { + target.setStringsInitialized( new LinkedHashSet() ); + } } if ( target.getLongsInitialized() != null ) { Set set1 = stringListToLongSet( source.getStringsInitialized() ); @@ -115,6 +124,9 @@ public void update(Dto source, Domain target) { if ( set1 != null ) { target.setLongsInitialized( set1 ); } + else { + target.setLongsInitialized( new LinkedHashSet() ); + } } if ( target.getStringsWithDefault() != null ) { List list2 = source.getStringsWithDefault(); @@ -157,6 +169,9 @@ public Domain updateWithReturn(Dto source, Domain target) { if ( list != null ) { target.setStrings( new LinkedHashSet( list ) ); } + else { + target.setStrings( new LinkedHashSet() ); + } } if ( target.getLongs() != null ) { Set set = stringListToLongSet( source.getStrings() ); @@ -173,6 +188,9 @@ public Domain updateWithReturn(Dto source, Domain target) { if ( set != null ) { target.setLongs( set ); } + else { + target.setLongs( new LinkedHashSet() ); + } } if ( target.getStringsInitialized() != null ) { List list1 = source.getStringsInitialized(); @@ -189,6 +207,9 @@ public Domain updateWithReturn(Dto source, Domain target) { if ( list1 != null ) { target.setStringsInitialized( new LinkedHashSet( list1 ) ); } + else { + target.setStringsInitialized( new LinkedHashSet() ); + } } if ( target.getLongsInitialized() != null ) { Set set1 = stringListToLongSet( source.getStringsInitialized() ); @@ -205,6 +226,9 @@ public Domain updateWithReturn(Dto source, Domain target) { if ( set1 != null ) { target.setLongsInitialized( set1 ); } + else { + target.setLongsInitialized( new LinkedHashSet() ); + } } if ( target.getStringsWithDefault() != null ) { List list2 = source.getStringsWithDefault(); From 46a9c29fcabc7ade34f39a92741449c874d7547a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=ED=98=84=EC=88=98?= Date: Thu, 31 Jul 2025 04:37:50 +0900 Subject: [PATCH 500/597] #3902: Validate unknown properties in @Ignored --- .../ap/internal/model/BeanMappingMethod.java | 60 +++++++++------- .../mapstruct/ap/internal/util/Message.java | 1 + .../bugs/_3902/ErroneousIssue3902Mapper.java | 69 +++++++++++++++++++ .../ap/test/bugs/_3902/Issue3902Test.java | 57 +++++++++++++++ 4 files changed, 164 insertions(+), 23 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/ErroneousIssue3902Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/Issue3902Test.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index b5208d302b..c81fc51800 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1280,40 +1280,54 @@ else if ( inheritContext.isReversed() ) { return false; } } - Set readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet(); - String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); Message msg; String[] args; - if ( targetRef.getPathProperties().isEmpty() ) { - msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE; + Element elementForMessage = mapping.getElement(); + if ( elementForMessage == null ) { + elementForMessage = method.getExecutable(); + } + + if ( mapping.isIgnored() && mapping.getElement() == null ) { + msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_IGNORED; args = new String[] { targetPropertyName, - resultTypeToMap.describe(), - mostSimilarProperty + resultTypeToMap.describe() }; } else { - List pathProperties = new ArrayList<>( targetRef.getPathProperties() ); - pathProperties.add( mostSimilarProperty ); - msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE; - args = new String[] { - targetPropertyName, - resultTypeToMap.describe(), - mapping.getTargetName(), - Strings.join( pathProperties, "." ) - }; + Set readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet(); + String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); + + if ( targetRef.getPathProperties().isEmpty() ) { + msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE; + args = new String[] { + targetPropertyName, + resultTypeToMap.describe(), + mostSimilarProperty + }; + } + else { + List pathProperties = new ArrayList<>( targetRef.getPathProperties() ); + pathProperties.add( mostSimilarProperty ); + msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE; + args = new String[] { + targetPropertyName, + resultTypeToMap.describe(), + mapping.getTargetName(), + Strings.join( pathProperties, "." ) + }; + } } - ctx.getMessager() - .printMessage( - mapping.getElement(), - mapping.getMirror(), - mapping.getTargetAnnotationValue(), - msg, - args - ); + ctx.getMessager().printMessage( + elementForMessage, + mapping.getMirror(), + mapping.getTargetAnnotationValue(), + msg, + args + ); return true; } else if ( mapping.getInheritContext() != null && mapping.getInheritContext().isReversed() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 883b3e3792..8534758285 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -45,6 +45,7 @@ public enum Message { BEANMAPPING_CYCLE_BETWEEN_PROPERTIES( "Cycle(s) between properties given via dependsOn(): %s." ), BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON( "\"%s\" is no property of the method return type." ), BEANMAPPING_IGNORE_BY_DEFAULT_WITH_MAPPING_TARGET_THIS( "Using @BeanMapping( ignoreByDefault = true ) with @Mapping( target = \".\", ... ) is not allowed. You'll need to explicitly ignore the target properties that should be ignored instead." ), + BEANMAPPING_UNKNOWN_PROPERTY_IN_IGNORED("No property named \"%s\" exists in @Ignored for target type \"%s\""), CONDITION_MISSING_APPLIES_TO_STRATEGY("'appliesTo' has to have at least one value in @Condition" ), CONDITION_SOURCE_PARAMETERS_INVALID_PARAMETER("Parameter \"%s\" cannot be used with the ConditionStrategy#SOURCE_PARAMETERS. Only source and @Context parameters are allowed for conditions applicable to source parameters." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/ErroneousIssue3902Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/ErroneousIssue3902Mapper.java new file mode 100644 index 0000000000..07f94ce6fe --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/ErroneousIssue3902Mapper.java @@ -0,0 +1,69 @@ +/* + * 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._3902; + +import org.mapstruct.Ignored; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * Mapper for testing bug #3902. + * + * @author znight1020 + */ +@Mapper +public interface ErroneousIssue3902Mapper { + + ErroneousIssue3902Mapper INSTANCE = Mappers.getMapper( ErroneousIssue3902Mapper.class ); + + @Ignored(targets = {"name", "foo", "bar"}) + ZooDto mapWithOneKnownAndMultipleUnknowns(Zoo source); + + @Ignored(targets = {"name", "address", "foo"}) + ZooDto mapWithMultipleKnownAndOneUnknown(Zoo source); + + class Zoo { + private String name; + private String address; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + } + + class ZooDto { + private String name; + private String address; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/Issue3902Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/Issue3902Test.java new file mode 100644 index 0000000000..f1332bbdc6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/Issue3902Test.java @@ -0,0 +1,57 @@ +/* + * 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._3902; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; + +/** + * Verifies that using an unknown property in {@code @Ignored} yields a proper + * compile error instead of an internal processor error. + * + * @author znight1020 + */ +@WithClasses({ErroneousIssue3902Mapper.class}) +@IssueKey("3902") +public class Issue3902Test { + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + // Test case: mapWithOneKnownAndMultipleUnknowns + @Diagnostic( + type = ErroneousIssue3902Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 23, + message = "No property named \"foo\" exists in @Ignored for target type " + + "\"ErroneousIssue3902Mapper.ZooDto\"" + ), + @Diagnostic( + type = ErroneousIssue3902Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 23, + message = "No property named \"bar\" exists in @Ignored for target type " + + "\"ErroneousIssue3902Mapper.ZooDto\"" + ), + + // Test case: mapWithMultipleKnownAndOneUnknown + @Diagnostic( + type = ErroneousIssue3902Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 26, + message = "No property named \"foo\" exists in @Ignored for target type " + + "\"ErroneousIssue3902Mapper.ZooDto\"" + ) + } + ) + public void shouldFailOnUnknownPropertiesInIgnored() { + } +} From 29036542b85ff93a05fc5851793438ede8713f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=ED=98=84=EC=88=98?= Date: Mon, 4 Aug 2025 15:08:22 +0900 Subject: [PATCH 501/597] #3908: Add similar suggestion for unknown property in `@Ignored` --- .../ap/internal/model/BeanMappingMethod.java | 22 ++++++++++--------- .../mapstruct/ap/internal/util/Message.java | 2 +- .../bugs/_3902/ErroneousIssue3902Mapper.java | 3 +++ .../ap/test/bugs/_3902/Issue3902Test.java | 15 ++++++++++--- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index c81fc51800..7973ece427 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -1284,6 +1284,9 @@ else if ( inheritContext.isReversed() ) { Message msg; String[] args; + Set readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet(); + String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); + Element elementForMessage = mapping.getElement(); if ( elementForMessage == null ) { elementForMessage = method.getExecutable(); @@ -1293,13 +1296,11 @@ else if ( inheritContext.isReversed() ) { msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_IGNORED; args = new String[] { targetPropertyName, - resultTypeToMap.describe() + resultTypeToMap.describe(), + mostSimilarProperty }; } else { - Set readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet(); - String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors ); - if ( targetRef.getPathProperties().isEmpty() ) { msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE; args = new String[] { @@ -1321,12 +1322,13 @@ else if ( inheritContext.isReversed() ) { } } - ctx.getMessager().printMessage( - elementForMessage, - mapping.getMirror(), - mapping.getTargetAnnotationValue(), - msg, - args + ctx.getMessager() + .printMessage( + elementForMessage, + mapping.getMirror(), + mapping.getTargetAnnotationValue(), + msg, + args ); return true; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 8534758285..60aa55eb38 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -45,7 +45,7 @@ public enum Message { BEANMAPPING_CYCLE_BETWEEN_PROPERTIES( "Cycle(s) between properties given via dependsOn(): %s." ), BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON( "\"%s\" is no property of the method return type." ), BEANMAPPING_IGNORE_BY_DEFAULT_WITH_MAPPING_TARGET_THIS( "Using @BeanMapping( ignoreByDefault = true ) with @Mapping( target = \".\", ... ) is not allowed. You'll need to explicitly ignore the target properties that should be ignored instead." ), - BEANMAPPING_UNKNOWN_PROPERTY_IN_IGNORED("No property named \"%s\" exists in @Ignored for target type \"%s\""), + BEANMAPPING_UNKNOWN_PROPERTY_IN_IGNORED("No property named \"%s\" exists in @Ignored for target type \"%s\". Did you mean \"%s\"?"), CONDITION_MISSING_APPLIES_TO_STRATEGY("'appliesTo' has to have at least one value in @Condition" ), CONDITION_SOURCE_PARAMETERS_INVALID_PARAMETER("Parameter \"%s\" cannot be used with the ConditionStrategy#SOURCE_PARAMETERS. Only source and @Context parameters are allowed for conditions applicable to source parameters." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/ErroneousIssue3902Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/ErroneousIssue3902Mapper.java index 07f94ce6fe..b646f1a08e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/ErroneousIssue3902Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/ErroneousIssue3902Mapper.java @@ -25,6 +25,9 @@ public interface ErroneousIssue3902Mapper { @Ignored(targets = {"name", "address", "foo"}) ZooDto mapWithMultipleKnownAndOneUnknown(Zoo source); + @Ignored(targets = {"name", "addres"}) + ZooDto mapWithTypo(Zoo source); + class Zoo { private String name; private String address; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/Issue3902Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/Issue3902Test.java index f1332bbdc6..44247d8c27 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/Issue3902Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3902/Issue3902Test.java @@ -32,14 +32,14 @@ public class Issue3902Test { kind = javax.tools.Diagnostic.Kind.ERROR, line = 23, message = "No property named \"foo\" exists in @Ignored for target type " + - "\"ErroneousIssue3902Mapper.ZooDto\"" + "\"ErroneousIssue3902Mapper.ZooDto\". Did you mean \"name\"?" ), @Diagnostic( type = ErroneousIssue3902Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, line = 23, message = "No property named \"bar\" exists in @Ignored for target type " + - "\"ErroneousIssue3902Mapper.ZooDto\"" + "\"ErroneousIssue3902Mapper.ZooDto\". Did you mean \"name\"?" ), // Test case: mapWithMultipleKnownAndOneUnknown @@ -48,7 +48,16 @@ public class Issue3902Test { kind = javax.tools.Diagnostic.Kind.ERROR, line = 26, message = "No property named \"foo\" exists in @Ignored for target type " + - "\"ErroneousIssue3902Mapper.ZooDto\"" + "\"ErroneousIssue3902Mapper.ZooDto\". Did you mean \"name\"?" + ), + + // Test case: mapWithTypo + @Diagnostic( + type = ErroneousIssue3902Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 29, + message = "No property named \"addres\" exists in @Ignored for target type " + + "\"ErroneousIssue3902Mapper.ZooDto\". Did you mean \"address\"?" ) } ) From fe43563c8184dcf0e96a9ac9bfb40a8ace436e6f Mon Sep 17 00:00:00 2001 From: Ritesh Chopade <94113981+codeswithritesh@users.noreply.github.com> Date: Sun, 24 Aug 2025 19:47:30 +0530 Subject: [PATCH 502/597] #3837 Add warning/error for redundant ignoreUnmappedSourceProperties entries (#3906) Adds compiler warning / error when properties listed in `ignoreUnmappedSourceProperties` are actually mapped. Respects `unmappedSourcePolicy` and includes tests for redundant and valid ignore cases. --- .../ap/internal/model/BeanMappingMethod.java | 44 +++++- .../mapstruct/ap/internal/util/Message.java | 2 + .../IgnoredMappedPropertyTest.java | 136 ++++++++++++++++++ .../ap/test/ignoreunmapped/UserDto.java | 31 ++++ .../ap/test/ignoreunmapped/UserEntity.java | 40 ++++++ .../ignoreunmapped/mapper/UserMapper.java | 19 +++ ...rMapperWithIgnorePolicyInMapperConfig.java | 24 ++++ .../UserMapperWithIgnoreSourcePolicy.java | 21 +++ ...serMapperWithWarnPolicyInMapperConfig.java | 24 ++++ .../UserMapperWithWarnSourcePolicy.java | 21 +++ ...serMapperWithWarnSourcePolicyInMapper.java | 20 +++ .../mapper/UserMapperWithoutBeanMapping.java | 17 +++ ...erMapperWithErrorPolicyInMapperConfig.java | 24 ++++ .../UserMapperWithErrorSourcePolicy.java | 21 +++ ...erMapperWithErrorSourcePolicyInMapper.java | 20 +++ .../erroneous/UserMapperWithMultiMapping.java | 22 +++ 16 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/IgnoredMappedPropertyTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/UserDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/UserEntity.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/UserMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/UserMapperWithIgnorePolicyInMapperConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/UserMapperWithIgnoreSourcePolicy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/UserMapperWithWarnPolicyInMapperConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/UserMapperWithWarnSourcePolicy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/UserMapperWithWarnSourcePolicyInMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/UserMapperWithoutBeanMapping.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/erroneous/UserMapperWithErrorPolicyInMapperConfig.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/erroneous/UserMapperWithErrorSourcePolicy.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/erroneous/UserMapperWithErrorSourcePolicyInMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/ignoreunmapped/mapper/erroneous/UserMapperWithMultiMapping.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 7973ece427..05e0424a21 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -114,6 +114,7 @@ public static class Builder extends AbstractMappingMethodBuilder unprocessedTargetProperties; private Map unprocessedSourceProperties; private Set missingIgnoredSourceProperties; + private Set redundantIgnoredSourceProperties; private Set targetProperties; private final List propertyMappings = new ArrayList<>(); private final Set unprocessedSourceParameters = new HashSet<>(); @@ -278,11 +279,23 @@ else if ( !method.isUpdateMethod() ) { // get bean mapping (when specified as annotation ) this.missingIgnoredSourceProperties = new HashSet<>(); - if ( beanMapping != null ) { + this.redundantIgnoredSourceProperties = new HashSet<>(); + if ( beanMapping != null && !beanMapping.getIgnoreUnmappedSourceProperties().isEmpty() ) { + // Get source properties explicitly mapped using @Mapping annotations + Set mappedSourceProperties = method.getOptions().getMappings().stream() + .map( MappingOptions::getSourceName ) + .filter( Objects::nonNull ) + .collect( Collectors.toSet() ); + for ( String ignoreUnmapped : beanMapping.getIgnoreUnmappedSourceProperties() ) { + // Track missing ignored properties (i.e. not in source class) if ( unprocessedSourceProperties.remove( ignoreUnmapped ) == null ) { missingIgnoredSourceProperties.add( ignoreUnmapped ); } + // Track redundant ignored properties (actually mapped despite being ignored) + if ( mappedSourceProperties.contains( ignoreUnmapped ) ) { + redundantIgnoredSourceProperties.add( ignoreUnmapped ); + } } } @@ -331,6 +344,7 @@ else if ( !method.isUpdateMethod() ) { } reportErrorForMissingIgnoredSourceProperties(); reportErrorForUnusedSourceParameters(); + reportErrorForRedundantIgnoredSourceProperties(); // mapNullToDefault boolean mapNullToDefault = method.getOptions() @@ -1914,6 +1928,34 @@ private void reportErrorForMissingIgnoredSourceProperties() { } } + private void reportErrorForRedundantIgnoredSourceProperties() { + if ( !redundantIgnoredSourceProperties.isEmpty() ) { + ReportingPolicyGem unmappedSourcePolicy = getUnmappedSourcePolicy(); + if ( unmappedSourcePolicy == ReportingPolicyGem.IGNORE ) { //don't show warning + return; + } + + Message message = Message.BEANMAPPING_REDUNDANT_IGNORED_SOURCES_WARNING; + if ( unmappedSourcePolicy.getDiagnosticKind() == Diagnostic.Kind.ERROR ) { + message = Message.BEANMAPPING_REDUNDANT_IGNORED_SOURCES_ERROR; + } + + Object[] args = new Object[] { + MessageFormat.format( + "{0,choice,1#property|1 Date: Thu, 28 Aug 2025 22:31:52 +0200 Subject: [PATCH 503/597] docs: reference gh discussions --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6064004d73..14443592e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ You love MapStruct but miss a certain feature? You found a bug and want to repor * Source code: [http://github.com/mapstruct/mapstruct](http://github.com/mapstruct/mapstruct) * Issue tracker: [https://github.com/mapstruct/mapstruct/issues](https://github.com/mapstruct/mapstruct/issues) -* Discussions: Join the [mapstruct-users](https://groups.google.com/forum/?fromgroups#!forum/mapstruct-users) Google group +* Discussions: [https://github.com/mapstruct/mapstruct/discussions](https://github.com/mapstruct/mapstruct/discussions) * CI build: [https://github.com/mapstruct/mapstruct/actions/](https://github.com/mapstruct/mapstruct/actions) MapStruct follows the _Fork & Pull_ development approach. To get started just fork the [MapStruct repository](http://github.com/mapstruct/mapstruct) to your GitHub account and create a new topic branch for each change. Once you are done with your change, submit a [pull request](https://help.github.com/articles/using-pull-requests) against the MapStruct repo. @@ -15,7 +15,7 @@ When doing changes, keep the following best practices in mind: * Use the code formatter for your IDE * [IntelliJ Formatter](https://github.com/mapstruct/mapstruct/blob/master/etc/mapstruct.xml) * Update the [reference documentation](mapstruct.org/documentation) on [mapstruct.org](mapstruct.org) where required -* Discuss new features you'd like to implement at the [Google group](https://groups.google.com/forum/?fromgroups#!forum/mapstruct-users) before getting started +* Discuss new features you'd like to implement in the [discussions](https://github.com/mapstruct/mapstruct/discussions) before getting started * Create one pull request per feature * Provide a meaningful history, e.g. squash intermediary commits before submitting a pull request From 0f7f5434295093d1f452d95d3d72c10c7f37103e Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 29 Aug 2025 15:57:31 +0200 Subject: [PATCH 504/597] Update Distribution management to point to new Maven Central Portal --- parent/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index 35fc24e96b..f90b066774 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -87,12 +87,12 @@ sonatype-nexus-staging Nexus Release Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/ sonatype-nexus-snapshots Sonatype Nexus Snapshots - https://oss.sonatype.org/content/repositories/snapshots/ + https://central.sonatype.com/repository/maven-snapshots/ From 4e1720c2a899252de448c38eb10942f9fbbf5276 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 29 Aug 2025 16:11:26 +0200 Subject: [PATCH 505/597] #3905: Using custom class Override should compile --- .../model/AbstractMappingMethodBuilder.java | 9 ++++++- .../model/NormalTypeMappingMethod.java | 6 ----- .../ap/internal/model/BeanMappingMethod.ftl | 1 - .../internal/model/IterableMappingMethod.ftl | 1 - .../ap/internal/model/MapMappingMethod.ftl | 1 - .../ap/internal/model/StreamMappingMethod.ftl | 1 - .../ap/test/bugs/_3905/Issue3905Mapper.java | 17 ++++++++++++ .../ap/test/bugs/_3905/Issue3905Test.java | 26 +++++++++++++++++++ .../ap/test/bugs/_3905/Override.java | 22 ++++++++++++++++ .../ap/test/bugs/_3905/OverrideDto.java | 22 ++++++++++++++++ 10 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Issue3905Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Issue3905Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Override.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/OverrideDto.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index 7329df5ab7..efb5887c95 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -130,7 +130,14 @@ public List getMethodAnnotations() { ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() ); - return new ArrayList<>( additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) ); + List annotations = new ArrayList<>( + additionalAnnotationsBuilder.getProcessedAnnotations( method.getExecutable() ) + ); + + if ( method.overridesMethod() ) { + annotations.add( new Annotation( ctx.getTypeFactory().getType( Override.class ) ) ); + } + return annotations; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java index 79cd0e0566..89ee1baab2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NormalTypeMappingMethod.java @@ -21,7 +21,6 @@ */ public abstract class NormalTypeMappingMethod extends MappingMethod { private final MethodReference factoryMethod; - private final boolean overridden; private final boolean mapNullToDefault; private final List annotations; @@ -33,7 +32,6 @@ public abstract class NormalTypeMappingMethod extends MappingMethod { List afterMappingReferences) { super( method, existingVariableNames, beforeMappingReferences, afterMappingReferences ); this.factoryMethod = factoryMethod; - this.overridden = method.overridesMethod(); this.mapNullToDefault = mapNullToDefault; this.annotations = annotations; } @@ -59,10 +57,6 @@ public boolean isMapNullToDefault() { return mapNullToDefault; } - public boolean isOverridden() { - return overridden; - } - public MethodReference getFactoryMethod() { return this.factoryMethod; } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index da590bb506..059e2d77d4 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -9,7 +9,6 @@ <#list annotations as annotation> <#nt><@includeModel object=annotation/> -<#if overridden>@Override <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#assign targetType = resultType /> <#if !existingInstanceMapping> 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 4d08c44b38..7c3d3071ac 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 @@ -9,7 +9,6 @@ <#list annotations as annotation> <#nt><@includeModel object=annotation/> -<#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> <@includeModel object=callback targetBeanName=resultName targetType=resultType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl index 1227dbd27a..957dc712ee 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MapMappingMethod.ftl @@ -9,7 +9,6 @@ <#list annotations as annotation> <#nt><@includeModel object=annotation/> -<#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> <@includeModel object=callback targetBeanName=resultName targetType=resultType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl index 7d0080a1d2..2269892b8a 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl @@ -9,7 +9,6 @@ <#list annotations as annotation> <#nt><@includeModel object=annotation/> -<#if overridden>@Override <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#--TODO does it even make sense to do a callback if the result is a Stream, as they are immutable--> <#list beforeMappingReferencesWithoutMappingTarget as callback> diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Issue3905Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Issue3905Mapper.java new file mode 100644 index 0000000000..27e3ac9960 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Issue3905Mapper.java @@ -0,0 +1,17 @@ +/* + * 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._3905; + +import org.mapstruct.Mapper; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue3905Mapper { + + OverrideDto map(Override override); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Issue3905Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Issue3905Test.java new file mode 100644 index 0000000000..f15271362d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Issue3905Test.java @@ -0,0 +1,26 @@ +/* + * 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._3905; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +/** + * @author Filip Hrisafov + */ +@IssueKey("3905") +@WithClasses({ + Issue3905Mapper.class, + Override.class, + OverrideDto.class +}) +class Issue3905Test { + + @ProcessorTest + void shouldCompile() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Override.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Override.java new file mode 100644 index 0000000000..dedc6b28dc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/Override.java @@ -0,0 +1,22 @@ +/* + * 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._3905; + +/** + * @author Filip Hrisafov + */ +public class Override { + + private final String name; + + public Override(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/OverrideDto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/OverrideDto.java new file mode 100644 index 0000000000..531fab505b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3905/OverrideDto.java @@ -0,0 +1,22 @@ +/* + * 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._3905; + +/** + * @author Filip Hrisafov + */ +public class OverrideDto { + + private final String name; + + public OverrideDto(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} From a0552131a5bf89f8192acb154b81bbd03d334ba4 Mon Sep 17 00:00:00 2001 From: hdulme Date: Wed, 31 Dec 2025 17:23:53 +0100 Subject: [PATCH 506/597] skip coverage upload to codecov on contributors repositories --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b6a2ef371e..c4e6092723 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: if: matrix.java == 21 run: ./mvnw jacoco:report - name: 'Upload coverage to Codecov' - if: matrix.java == 21 + if: matrix.java == 21 && github.repository == 'mapstruct/mapstruct' uses: codecov/codecov-action@v2 - name: 'Publish Snapshots' if: matrix.java == 21 && github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mapstruct/mapstruct' From 1c8b9107f99e986e5e28cff66ad604ca89975a8c Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Fri, 23 Jan 2026 22:01:18 +0100 Subject: [PATCH 507/597] Update codecov-action to v5 and use token (#3967) --- .github/workflows/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c4e6092723..c4def2a89d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,7 +30,10 @@ jobs: run: ./mvnw jacoco:report - name: 'Upload coverage to Codecov' if: matrix.java == 21 && github.repository == 'mapstruct/mapstruct' - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v5 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} - name: 'Publish Snapshots' if: matrix.java == 21 && github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mapstruct/mapstruct' run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy From 7bcab2824d0d17fe0a68d520e0c73f83a0e82588 Mon Sep 17 00:00:00 2001 From: ro-otgo <116685946+ro-otgo@users.noreply.github.com> Date: Sat, 24 Jan 2026 15:44:21 +0100 Subject: [PATCH 508/597] Fix example 5 code comment. (#3968) Example 5: Mapping with default value should be using fullName instead of just name in order to map the property. --- core/src/main/java/org/mapstruct/Mapping.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index f15cfb75cc..c1923eb41a 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -118,7 +118,7 @@ * // we can use {@link #defaultValue()} or {@link #defaultExpression()} for it * @Mapper * public interface HumanMapper { - * @Mapping(source="name", target="name", defaultValue="Somebody") + * @Mapping(source="name", target="fullName", defaultValue="Somebody") * HumanDto toHumanDto(Human human) * } * From 57790791abd0193cf0ff42d33524c69ab0eb6215 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 24 Jan 2026 15:46:36 +0100 Subject: [PATCH 509/597] BeanMappingMethod and NestedPropertyMappingMethod simplification (#3970) * Move `returnTypeBuilder` and `userDefinedReturnType` in `BeanMappingMethod#build` instead of doing it earlier * Move source parameter presence check resolving into `PresenceCheckMethodResolver` * Simplify `NestedPropertyMappingMethod.ftl` by moving logic into java instead of freemarker --- .../internal/model/AbstractBaseBuilder.java | 4 +- .../model/AbstractMappingMethodBuilder.java | 7 +- .../ap/internal/model/BeanMappingMethod.java | 32 +++----- .../model/NestedPropertyMappingMethod.java | 79 ++++++++++++------- .../model/PresenceCheckMethodResolver.java | 4 + .../ap/internal/model/PropertyMapping.java | 2 +- .../AnyPresenceChecksPresenceCheck.java | 64 +++++++++++++++ .../processor/MapperCreationProcessor.java | 14 ---- .../model/NestedPropertyMappingMethod.ftl | 16 +--- .../AnyPresenceChecksPresenceCheck.ftl | 16 ++++ 10 files changed, 152 insertions(+), 86 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.ftl diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java index 56a1831d52..584e0260bc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java @@ -9,7 +9,6 @@ import javax.lang.model.element.AnnotationMirror; import org.mapstruct.ap.internal.model.common.Assignment; -import org.mapstruct.ap.internal.model.common.BuilderType; import org.mapstruct.ap.internal.model.common.ParameterBinding; import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; @@ -73,7 +72,7 @@ private boolean isDisableSubMappingMethodsGeneration() { * * @return See above */ - Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, ForgedMethod forgedMethod) { + Assignment createForgedAssignment(SourceRHS sourceRHS, ForgedMethod forgedMethod) { Supplier forgedMappingMethodCreator; if ( MappingMethodUtils.isEnumMapping( forgedMethod ) ) { @@ -87,7 +86,6 @@ Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, else { forgedMappingMethodCreator = () -> new BeanMappingMethod.Builder() .forgedMethod( forgedMethod ) - .returnTypeBuilder( builderType ) .mappingContext( ctx ) .build(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index efb5887c95..3b6e3c1686 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -5,7 +5,6 @@ */ package org.mapstruct.ap.internal.model; -import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.model.beanmapping.MappingReferences; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.SourceRHS; @@ -94,12 +93,8 @@ private Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targe ForgedMethod forgedMethod = forgeMethodCreator.createMethod( name, sourceType, targetType, method, description, true ); - BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); - return createForgedAssignment( - sourceRHS, - ctx.getTypeFactory().builderTypeFor( targetType, builder ), - forgedMethod ); + return createForgedAssignment( sourceRHS, forgedMethod ); } private String getName(Type sourceType, Type targetType) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 05e0424a21..39222efc88 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -32,6 +32,7 @@ import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic; +import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.model.PropertyMapping.ConstantMappingBuilder; @@ -52,7 +53,6 @@ import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder; -import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; import org.mapstruct.ap.internal.model.source.BeanMappingOptions; import org.mapstruct.ap.internal.model.source.MappingOptions; import org.mapstruct.ap.internal.model.source.Method; @@ -106,8 +106,6 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { public static class Builder extends AbstractMappingMethodBuilder { - private Type userDefinedReturnType; - /* returnType to construct can have a builder */ private BuilderType returnTypeBuilder; private Map unprocessedConstructorProperties; @@ -135,16 +133,6 @@ protected boolean shouldUsePropertyNamesInHistory() { return true; } - public Builder userDefinedReturnType(Type userDefinedReturnType) { - this.userDefinedReturnType = userDefinedReturnType; - return this; - } - - public Builder returnTypeBuilder( BuilderType returnTypeBuilder ) { - this.returnTypeBuilder = returnTypeBuilder; - return this; - } - public Builder sourceMethod(SourceMethod sourceMethod) { method( sourceMethod ); return this; @@ -181,7 +169,17 @@ public BeanMappingMethod build() { // determine which return type to construct boolean cannotConstructReturnType = false; if ( !method.getReturnType().isVoid() ) { - Type returnTypeImpl = null; + BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); + Type returnTypeImpl; + Type userDefinedReturnType = null; + if ( selectionParameters != null && selectionParameters.getResultType() != null ) { + // This is a user-defined return type, which means we need to do some extra checks for it + userDefinedReturnType = ctx.getTypeFactory().getType( selectionParameters.getResultType() ); + returnTypeBuilder = ctx.getTypeFactory().builderTypeFor( userDefinedReturnType, builder ); + } + else { + returnTypeBuilder = ctx.getTypeFactory().builderTypeFor( method.getReturnType(), builder ); + } if ( isBuilderRequired() ) { // the userDefinedReturn type can also require a builder. That buildertype is already set returnTypeImpl = returnTypeBuilder.getBuilder(); @@ -446,12 +444,6 @@ else if ( !method.isUpdateMethod() ) { if ( parameterPresenceCheck != null ) { presenceChecksByParameter.put( sourceParameter.getName(), parameterPresenceCheck ); } - else if ( !sourceParameter.getType().isPrimitive() ) { - presenceChecksByParameter.put( - sourceParameter.getName(), - new NullPresenceCheck( sourceParameter.getName() ) - ); - } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java index da73e1783f..25c9f8fc03 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.internal.model; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Set; @@ -14,6 +15,8 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.presence.AnyPresenceChecksPresenceCheck; +import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; import org.mapstruct.ap.internal.model.presence.SuffixPresenceCheck; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; @@ -68,9 +71,32 @@ public NestedPropertyMappingMethod build() { } String previousPropertyName = sourceParameter.getName(); - for ( PropertyEntry propertyEntry : propertyEntries ) { + for ( int i = 0; i < propertyEntries.size(); i++ ) { + PropertyEntry propertyEntry = propertyEntries.get( i ); + PresenceCheck presenceCheck = getPresenceCheck( propertyEntry, previousPropertyName ); + + if ( i > 0 ) { + // If this is not the first property entry, + // then we might need to combine the presence check with a null check of the previous property + if ( presenceCheck != null ) { + presenceCheck = new AnyPresenceChecksPresenceCheck( Arrays.asList( + new NullPresenceCheck( previousPropertyName, true ), + presenceCheck + ) ); + } + else { + presenceCheck = new NullPresenceCheck( previousPropertyName, true ); + } + } + String safeName = Strings.getSafeVariableName( propertyEntry.getName(), existingVariableNames ); - safePropertyEntries.add( new SafePropertyEntry( propertyEntry, safeName, previousPropertyName ) ); + String source = previousPropertyName + "." + propertyEntry.getReadAccessor().getReadValueSource(); + safePropertyEntries.add( new SafePropertyEntry( + propertyEntry.getType(), + safeName, + source, + presenceCheck + ) ); existingVariableNames.add( safeName ); thrownTypes.addAll( ctx.getTypeFactory().getThrownTypes( propertyEntry.getReadAccessor() ) ); @@ -79,6 +105,18 @@ public NestedPropertyMappingMethod build() { method.addThrownTypes( thrownTypes ); return new NestedPropertyMappingMethod( method, safePropertyEntries ); } + + private PresenceCheck getPresenceCheck(PropertyEntry propertyEntry, String previousPropertyName) { + PresenceCheckAccessor propertyPresenceChecker = propertyEntry.getPresenceChecker(); + if ( propertyPresenceChecker != null ) { + return new SuffixPresenceCheck( + previousPropertyName, + propertyPresenceChecker.getPresenceCheckSuffix(), + true + ); + } + return null; + } } private NestedPropertyMappingMethod( ForgedMethod method, List sourcePropertyEntries ) { @@ -157,44 +195,29 @@ public boolean equals( Object obj ) { public static class SafePropertyEntry { private final String safeName; - private final String readAccessorName; + private final String source; private final PresenceCheck presenceChecker; - private final String previousPropertyName; private final Type type; - public SafePropertyEntry(PropertyEntry entry, String safeName, String previousPropertyName) { + public SafePropertyEntry(Type type, String safeName, String source, PresenceCheck presenceCheck) { this.safeName = safeName; - this.readAccessorName = entry.getReadAccessor().getReadValueSource(); - PresenceCheckAccessor presenceChecker = entry.getPresenceChecker(); - if ( presenceChecker != null ) { - this.presenceChecker = new SuffixPresenceCheck( - previousPropertyName, - presenceChecker.getPresenceCheckSuffix() - ); - } - else { - this.presenceChecker = null; - } - this.previousPropertyName = previousPropertyName; - this.type = entry.getType(); + this.source = source; + this.presenceChecker = presenceCheck; + this.type = type; } public String getName() { return safeName; } - public String getAccessorName() { - return readAccessorName; + public String getSource() { + return source; } public PresenceCheck getPresenceChecker() { return presenceChecker; } - public String getPreviousPropertyName() { - return previousPropertyName; - } - public Type getType() { return type; } @@ -210,7 +233,7 @@ public boolean equals(Object o) { SafePropertyEntry that = (SafePropertyEntry) o; - if ( !Objects.equals( readAccessorName, that.readAccessorName ) ) { + if ( !Objects.equals( source, that.source ) ) { return false; } @@ -218,10 +241,6 @@ public boolean equals(Object o) { return false; } - if ( !Objects.equals( previousPropertyName, that.previousPropertyName ) ) { - return false; - } - if ( !Objects.equals( type, that.type ) ) { return false; } @@ -231,7 +250,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - int result = readAccessorName != null ? readAccessorName.hashCode() : 0; + int result = source != null ? source.hashCode() : 0; result = 31 * result + ( presenceChecker != null ? presenceChecker.hashCode() : 0 ); result = 31 * result + ( type != null ? type.hashCode() : 0 ); return result; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java index 96826f8f66..049e4caf37 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java @@ -12,6 +12,7 @@ import org.mapstruct.ap.internal.gem.ConditionStrategyGem; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SelectionParameters; @@ -86,6 +87,9 @@ public static PresenceCheck getPresenceCheckForSourceParameter( ); if ( matchingMethods.isEmpty() ) { + if ( !sourceParameter.getType().isPrimitive() ) { + return new NullPresenceCheck( sourceParameter.getName() ); + } return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index db14ccbd8e..08e8adbd1b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -829,7 +829,7 @@ private Assignment forgeMapping(Type sourceType, Type targetType, SourceRHS sour forgeMethodWithMappingReferences, forgedNamedBased ); - return createForgedAssignment( sourceRHS, targetBuilderType, forgedMethod ); + return createForgedAssignment( sourceRHS, forgedMethod ); } private ForgedMethodHistory getForgedMethodHistory(SourceRHS sourceRHS) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.java new file mode 100644 index 0000000000..0b5770e42f --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.java @@ -0,0 +1,64 @@ +/* + * 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.internal.model.presence; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.NegatePresenceCheck; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Filip Hrisafov + */ +public class AnyPresenceChecksPresenceCheck extends ModelElement implements PresenceCheck { + + private final Collection presenceChecks; + + public AnyPresenceChecksPresenceCheck(Collection presenceChecks) { + this.presenceChecks = presenceChecks; + } + + public Collection getPresenceChecks() { + return presenceChecks; + } + + @Override + public PresenceCheck negate() { + return new NegatePresenceCheck( this ); + } + + @Override + public Set getImportTypes() { + Set importTypes = new HashSet<>(); + for ( PresenceCheck presenceCheck : presenceChecks ) { + importTypes.addAll( presenceCheck.getImportTypes() ); + } + + return importTypes; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + AnyPresenceChecksPresenceCheck that = (AnyPresenceChecksPresenceCheck) o; + return Objects.equals( presenceChecks, that.presenceChecks ); + } + + @Override + public int hashCode() { + return Objects.hash( presenceChecks ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 4fbed6cbec..ba24e868aa 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -24,7 +24,6 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; -import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.DecoratedWithGem; import org.mapstruct.ap.internal.gem.InheritConfigurationGem; import org.mapstruct.ap.internal.gem.InheritInverseConfigurationGem; @@ -420,15 +419,10 @@ else if ( method.isStreamMapping() ) { } else { this.messager.note( 1, Message.BEANMAPPING_CREATE_NOTE, method ); - BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); - Type userDefinedReturnType = getUserDesiredReturnType( method ); - Type builderBaseType = userDefinedReturnType != null ? userDefinedReturnType : method.getReturnType(); BeanMappingMethod.Builder beanMappingBuilder = new BeanMappingMethod.Builder(); BeanMappingMethod beanMappingMethod = beanMappingBuilder .mappingContext( mappingContext ) .sourceMethod( method ) - .userDefinedReturnType( userDefinedReturnType ) - .returnTypeBuilder( typeFactory.builderTypeFor( builderBaseType, builder ) ) .build(); // We can consider that the bean mapping method can always be constructed. If there is a problem @@ -466,14 +460,6 @@ private Javadoc getJavadoc(TypeElement element) { return javadoc; } - private Type getUserDesiredReturnType(SourceMethod method) { - SelectionParameters selectionParameters = method.getOptions().getBeanMapping().getSelectionParameters(); - if ( selectionParameters != null && selectionParameters.getResultType() != null ) { - return typeFactory.getType( selectionParameters.getResultType() ); - } - return null; - } - private M createWithElementMappingMethod(SourceMethod method, MappingMethodOptions mappingMethodOptions, ContainerMappingMethodBuilder builder) { diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl index 8eabe23a16..04d385a9dc 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl @@ -9,22 +9,14 @@ <#lt>private <@includeModel object=returnType.typeBound/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { <#list propertyEntries as entry> <#if entry.presenceChecker?? > - if ( <#if entry_index != 0>${entry.previousPropertyName} == null || !<@includeModel object=entry.presenceChecker /> ) { + if ( <@includeModel object=entry.presenceChecker /> ) { return ${returnType.null}; } - <#if !entry_has_next> - return ${entry.previousPropertyName}.${entry.accessorName}; - <#if entry_has_next> - <@includeModel object=entry.type.typeBound/> ${entry.name} = ${entry.previousPropertyName}.${entry.accessorName}; - <#if !entry.presenceChecker?? > - <#if !entry.type.primitive> - if ( ${entry.name} == null ) { - return ${returnType.null}; - } - - + <@includeModel object=entry.type.typeBound/> ${entry.name} = ${entry.source}; + <#else> + return ${entry.source}; } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.ftl new file mode 100644 index 0000000000..3df10348ad --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.ftl @@ -0,0 +1,16 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.AnyPresenceChecksPresenceCheck" --> +<@compress single_line=true> +<#list presenceChecks as presenceCheck> + <#if presenceCheck_index != 0> + || + + <@includeModel object=presenceCheck /> + + \ No newline at end of file From 9d75a48df5e40c1ed87d2f68d96fbb35807e3b91 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 26 Jan 2026 11:35:18 +0100 Subject: [PATCH 510/597] Block plexus.snapshots repository in GitHub actions #3972 --- .github/workflows/java-ea.yml | 3 +++ .github/workflows/macos.yml | 3 +++ .github/workflows/main.yml | 6 ++++++ .github/workflows/release.yml | 4 ++++ .github/workflows/windows.yml | 3 +++ 5 files changed, 19 insertions(+) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index fc3a4a0fb1..70b81c40f1 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -17,5 +17,8 @@ jobs: with: website: jdk.java.net release: EA + - uses: s4u/maven-settings-action@v4.0.0 + with: + mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Test' run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 833bb6ba39..98b4e156e0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -16,5 +16,8 @@ jobs: with: distribution: 'zulu' java-version: 21 + - uses: s4u/maven-settings-action@v4.0.0 + with: + mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Test' run: ./mvnw ${MAVEN_ARGS} install diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c4def2a89d..ca66a22291 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,6 +23,9 @@ jobs: with: distribution: 'zulu' java-version: ${{ matrix.java }} + - uses: s4u/maven-settings-action@v4.0.0 + with: + mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Test' run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=${{ matrix.java != 21 }} install -DskipDistribution=${{ matrix.java != 21 }} - name: 'Generate coverage report' @@ -59,5 +62,8 @@ jobs: with: distribution: 'zulu' java-version: ${{ matrix.java }} + - uses: s4u/maven-settings-action@v4.0.0 + with: + mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Run integration tests' run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 61f347af3c..b4dffe0586 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,10 @@ jobs: distribution: 'zulu' cache: maven + - uses: s4u/maven-settings-action@v4.0.0 + with: + mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' + - name: Set release version id: version run: | diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index bda38f8783..fc37af6a9d 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -16,5 +16,8 @@ jobs: with: distribution: 'zulu' java-version: 21 + - uses: s4u/maven-settings-action@v4.0.0 + with: + mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Test' run: ./mvnw %MAVEN_ARGS% install From 99e865f9861bba3d8cc24d90b89821e8bb191164 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 29 Jan 2026 10:27:59 +0100 Subject: [PATCH 511/597] Improve testing support for Kotlin as part of the regular processor testing (#3977) --- .../itest/tests/MavenIntegrationTest.java | 6 + .../test/resources/fullFeatureTest/pom.xml | 1 + .../resources/kotlinFullFeatureTest/pom.xml | 143 ++++++++++++++++++ parent/pom.xml | 13 ++ processor/pom.xml | 68 +++++++++ .../ap/test/kotlin/data/CustomerDto.kt | 11 ++ .../ap/test/kotlin/data/CustomerEntity.java | 31 ++++ .../ap/test/kotlin/data/CustomerMapper.java | 27 ++++ .../ap/test/kotlin/data/KotlinDataTest.java | 54 +++++++ .../ap/testutil/WithKotlinSources.java | 32 ++++ .../model/CompilationOutcomeDescriptor.java | 31 ++++ .../model/DiagnosticDescriptor.java | 34 +++++ .../testutil/runner/CompilationRequest.java | 12 +- .../testutil/runner/CompilingExtension.java | 102 ++++++++++++- .../runner/EclipseCompilingExtension.java | 11 +- .../runner/JdkCompilingExtension.java | 15 +- 16 files changed, 579 insertions(+), 12 deletions(-) create mode 100644 integrationtest/src/test/resources/kotlinFullFeatureTest/pom.xml create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerDto.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerEntity.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithKotlinSources.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index ab43cc4cba..70e1f948b9 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -159,6 +159,12 @@ void expressionTextBlocksTest() { void kotlinDataTest() { } + @ProcessorTest(baseDir = "kotlinFullFeatureTest", processorTypes = { + ProcessorTest.ProcessorType.JAVAC_WITH_PATHS + }) + void kotlinFullFeatureTest() { + } + @ProcessorTest(baseDir = "simpleTest") void simpleTest() { } diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index b5ddae9a79..670d5cff92 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -47,6 +47,7 @@ **/*Test.java **/testutil/**/*.java **/spi/**/*.java + **/kotlin/**/*.java ${additionalExclude0} ${additionalExclude1} ${additionalExclude2} diff --git a/integrationtest/src/test/resources/kotlinFullFeatureTest/pom.xml b/integrationtest/src/test/resources/kotlinFullFeatureTest/pom.xml new file mode 100644 index 0000000000..ba18457f42 --- /dev/null +++ b/integrationtest/src/test/resources/kotlinFullFeatureTest/pom.xml @@ -0,0 +1,143 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + kotlinFullFeatureTest + jar + + + 2.3.0 + + + + + generate-via-compiler-plugin-with-annotation-processor-paths + + false + + + + + maven-resources-plugin + + + filter-kotlin-patterns + generate-sources + + copy-resources + + + \${project.build.directory}/kotlin-sources + + + ../../../../../processor/src/test/java + + **/kotlin/**/*.kt + + + + true + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + kotlin-compile + compile + + compile + + + + \${project.build.directory}/kotlin-sources + + + + ${project.groupId} + mapstruct-processor + ${mapstruct.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + + default-compile + none + + + default-testCompile + none + + + + + java-compile + compile + + compile + + + + ../../../../../processor/src/test/java + + + **/kotlin/**/*.java + + + **/erroneous/**/*.java + **/*Erroneous*.java + **/*Test.java + **/testutil/**/*.java + + + + + java-test-compile + test-compile + + testCompile + + + + + + + + + + + + org.jetbrains.kotlin + kotlin-stdlib + + + + diff --git a/parent/pom.xml b/parent/pom.xml index f90b066774..5c3a73da88 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -52,6 +52,7 @@ 1.8 3.25.5 2.3.2 + 2.3.0 @@ -151,6 +152,13 @@ ${com.puppycrawl.tools.checkstyle.version} + + org.jetbrains.kotlin + kotlin-bom + ${kotlin.version} + pom + import + org.junit junit-bom @@ -490,6 +498,11 @@ maven-shade-plugin 3.2.0 + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + com.mycila.maven-license-plugin maven-license-plugin diff --git a/processor/pom.xml b/processor/pom.xml index 87c3517018..9f24281e33 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -42,6 +42,11 @@ mapstruct provided + + org.jetbrains.kotlin + kotlin-compiler-embeddable + test + org.eclipse.tycho tycho-compiler-jdt @@ -282,6 +287,42 @@ + + org.jetbrains.kotlin + + kotlin-maven-plugin + + false + + + + kotlin-compile + compile + + compile + + + + src/main/kotlin + + src/main/java + + + + + kotlin-test-compile + test-compile + + test-compile + + + + src/test/java + + + + + org.apache.maven.plugins maven-compiler-plugin @@ -296,6 +337,33 @@ + + + + default-compile + none + + + default-testCompile + none + + + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + org.apache.maven.plugins diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerDto.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerDto.kt new file mode 100644 index 0000000000..3fbd48b0b1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerDto.kt @@ -0,0 +1,11 @@ +/* + * 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.kotlin.data + +/** + * @author Filip Hrisafov + */ +data class CustomerDto(var name: String?, var email: String?) diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerEntity.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerEntity.java new file mode 100644 index 0000000000..7ddd41bda0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerEntity.java @@ -0,0 +1,31 @@ +/* + * 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.kotlin.data; + +/** + * @author Filip Hrisafov + */ +public class CustomerEntity { + + private String name; + private String mail; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerMapper.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerMapper.java new file mode 100644 index 0000000000..1830a9c583 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/CustomerMapper.java @@ -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.kotlin.data; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface CustomerMapper { + + CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class ); + + @Mapping(target = "mail", source = "email") + CustomerEntity fromData(CustomerDto record); + + @InheritInverseConfiguration + CustomerDto toData(CustomerEntity entity); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java new file mode 100644 index 0000000000..310039363d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java @@ -0,0 +1,54 @@ +/* + * 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.kotlin.data; + +import org.junit.jupiter.api.Nested; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithKotlinSources; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +class KotlinDataTest { + + @Nested + @WithClasses({ + CustomerEntity.class, + CustomerMapper.class + }) + @WithKotlinSources("CustomerDto.kt") + class Standard { + + @ProcessorTest + void shouldMapData() { + CustomerEntity customer = CustomerMapper.INSTANCE.fromData( new CustomerDto( + "Kermit", + "kermit@test.com" + ) ); + + assertThat( customer ).isNotNull(); + assertThat( customer.getName() ).isEqualTo( "Kermit" ); + assertThat( customer.getMail() ).isEqualTo( "kermit@test.com" ); + } + + @ProcessorTest + void shouldMapIntoData() { + CustomerEntity entity = new CustomerEntity(); + entity.setName( "Kermit" ); + entity.setMail( "kermit@test.com" ); + + CustomerDto customer = CustomerMapper.INSTANCE.toData( entity ); + + assertThat( customer ).isNotNull(); + assertThat( customer.getName() ).isEqualTo( "Kermit" ); + assertThat( customer.getEmail() ).isEqualTo( "kermit@test.com" ); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithKotlinSources.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithKotlinSources.java new file mode 100644 index 0000000000..656dc9bc5f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithKotlinSources.java @@ -0,0 +1,32 @@ +/* + * 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.testutil; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.intellij.lang.annotations.Language; + +/** + * Specifies the kotlin sources to compile during an annotation processor test. + * If given both on the class-level and the method-level for a given test, all the given sources will be compiled. + * + * @author Filip Hrisafov + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +public @interface WithKotlinSources { + + /** + * The classes to be compiled for the annotated test class or method. + * + * @return the classes to be compiled for the annotated test class or method + */ + @Language(value = "file-reference", prefix = "", suffix = ".kt") + String[] value(); +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/CompilationOutcomeDescriptor.java b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/CompilationOutcomeDescriptor.java index b9f7bb8425..b5a4dc535d 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/CompilationOutcomeDescriptor.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/CompilationOutcomeDescriptor.java @@ -18,6 +18,8 @@ import org.codehaus.plexus.compiler.CompilerMessage; import org.codehaus.plexus.compiler.CompilerResult; +import org.jetbrains.kotlin.cli.common.ExitCode; +import org.jetbrains.kotlin.cli.common.messages.MessageCollectorImpl; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedNote; @@ -104,6 +106,25 @@ public static CompilationOutcomeDescriptor forResult(String sourceDir, CompilerR return new CompilationOutcomeDescriptor( compilationResult, diagnosticDescriptors, Collections.emptyList() ); } + public static CompilationOutcomeDescriptor forResult(String sourceDir, ExitCode exitCode, + List messages) { + List notes = new ArrayList<>(); + List diagnosticDescriptors = new ArrayList<>( messages.size() ); + for ( MessageCollectorImpl.Message message : messages ) { + //ignore notes created by the compiler + DiagnosticDescriptor descriptor = DiagnosticDescriptor.forCompilerMessage( sourceDir, message ); + if ( descriptor.getKind() != Kind.NOTE ) { + diagnosticDescriptors.add( descriptor ); + } + else { + notes.add( descriptor.getMessage() ); + } + } + CompilationResult compilationResult = + exitCode == ExitCode.OK ? CompilationResult.SUCCEEDED : CompilationResult.FAILED; + return new CompilationOutcomeDescriptor( compilationResult, diagnosticDescriptors, notes ); + } + public CompilationResult getCompilationResult() { return compilationResult; } @@ -116,6 +137,16 @@ public List getNotes() { return notes; } + public CompilationOutcomeDescriptor merge(CompilationOutcomeDescriptor other) { + CompilationResult compilationResult = + this.compilationResult == CompilationResult.SUCCEEDED ? other.compilationResult : this.compilationResult; + List diagnostics = new ArrayList<>( this.diagnostics ); + diagnostics.addAll( other.diagnostics ); + List notes = new ArrayList<>( this.notes ); + notes.addAll( other.notes ); + return new CompilationOutcomeDescriptor( compilationResult, diagnostics, notes ); + } + @Override public int hashCode() { final int prime = 31; diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java index ecfec14a16..68e2cb00fe 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/model/DiagnosticDescriptor.java @@ -15,6 +15,9 @@ import javax.tools.JavaFileObject; import org.codehaus.plexus.compiler.CompilerMessage; +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity; +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation; +import org.jetbrains.kotlin.cli.common.messages.MessageCollectorImpl; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; /** @@ -100,6 +103,37 @@ private static Kind toJavaxKind(CompilerMessage.Kind kind) { } } + public static DiagnosticDescriptor forCompilerMessage(String sourceDir, MessageCollectorImpl.Message message) { + CompilerMessageSourceLocation location = message.getLocation(); + String sourceFileName; + Long line; + if ( location != null ) { + sourceFileName = location.getPath(); + line = (long) location.getLine(); + } + else { + sourceFileName = null; + line = null; + } + return new DiagnosticDescriptor( + sourceFileName, + toJavaxKind( message.getSeverity() ), + line, + message.getMessage(), + null + ); + } + + private static Kind toJavaxKind(CompilerMessageSeverity severity) { + return switch ( severity ) { + case ERROR, EXCEPTION -> Kind.ERROR; + case WARNING, FIXED_WARNING -> Kind.WARNING; + case STRONG_WARNING -> Kind.MANDATORY_WARNING; + case INFO, LOGGING -> Kind.NOTE; + case OUTPUT -> Kind.OTHER; + }; + } + private static String getSourceName(String sourceDir, javax.tools.Diagnostic diagnostic) { if ( diagnostic.getSource() == null ) { return null; diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java index 9f1b78caa0..8622fbe92c 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java @@ -19,14 +19,17 @@ public class CompilationRequest { private final Map, Class> services; private final List processorOptions; private final Collection testDependencies; + private final Collection kotlinSources; CompilationRequest(Compiler compiler, Set> sourceClasses, Map, Class> services, - List processorOptions, Collection testDependencies) { + List processorOptions, Collection testDependencies, + Collection kotlinSources) { this.compiler = compiler; this.sourceClasses = sourceClasses; this.services = services; this.processorOptions = processorOptions; this.testDependencies = testDependencies; + this.kotlinSources = kotlinSources; } @Override @@ -58,7 +61,8 @@ public boolean equals(Object obj) { && processorOptions.equals( other.processorOptions ) && services.equals( other.services ) && testDependencies.equals( other.testDependencies ) - && sourceClasses.equals( other.sourceClasses ); + && sourceClasses.equals( other.sourceClasses ) + && kotlinSources.equals( other.kotlinSources ); } public Set> getSourceClasses() { @@ -77,6 +81,10 @@ public Collection getTestDependencies() { return testDependencies; } + public Collection getKotlinSources() { + return kotlinSources; + } + public Compiler getCompiler() { return compiler; } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java index 95874e4b3d..7ff78753a6 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.stream.Collectors; import com.puppycrawl.tools.checkstyle.Checker; import com.puppycrawl.tools.checkstyle.ConfigurationLoader; @@ -30,9 +31,15 @@ import com.puppycrawl.tools.checkstyle.PropertiesExpander; import com.puppycrawl.tools.checkstyle.api.AutomaticBean; import org.apache.commons.io.output.NullOutputStream; +import org.jetbrains.kotlin.cli.common.ExitCode; +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments; +import org.jetbrains.kotlin.cli.common.messages.MessageCollectorImpl; +import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler; +import org.jetbrains.kotlin.config.Services; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithKotlinSources; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.WithTestDependency; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; @@ -455,6 +462,20 @@ protected static Set getSourceFiles(Collection> classes) { return sourceFiles; } + private Collection getKotlinSources(ExtensionContext context) { + Collection kotlinSources = new HashSet<>(); + Method testMethod = context.getRequiredTestMethod(); + Class testClass = context.getRequiredTestClass(); + + findAnnotation( testMethod, WithKotlinSources.class ) + .ifPresent( withKotlinSources -> Collections.addAll( kotlinSources, withKotlinSources.value() ) ); + + findAnnotation( testClass, WithKotlinSources.class ) + .ifPresent( withKotlinSources -> Collections.addAll( kotlinSources, withKotlinSources.value() ) ); + + return kotlinSources; + } + private CompilationOutcomeDescriptor compile(ExtensionContext context) { Method testMethod = context.getRequiredTestMethod(); Class testClass = context.getRequiredTestClass(); @@ -464,7 +485,8 @@ private CompilationOutcomeDescriptor compile(ExtensionContext context) { getTestClasses( testMethod, testClass ), getServices( testMethod, testClass ), getProcessorOptions( testMethod, testClass ), - getAdditionalTestDependencies( testMethod, testClass ) + getAdditionalTestDependencies( testMethod, testClass ), + getKotlinSources( context ) ); ExtensionContext.Store rootStore = context.getRoot().getStore( NAMESPACE ); @@ -484,7 +506,8 @@ private CompilationOutcomeDescriptor compile(ExtensionContext context) { boolean needsAdditionalCompilerClasspath = prepareServices( compilationRequest ); CompilationOutcomeDescriptor resultHolder; - resultHolder = compileWithSpecificCompiler( + resultHolder = compile( + context, compilationRequest, sourceOutputDir, classOutputDir, @@ -503,6 +526,81 @@ protected Object loadAndInstantiate(ClassLoader processorClassloader, Class c } } + protected CompilationOutcomeDescriptor compile( + ExtensionContext context, + CompilationRequest compilationRequest, + String sourceOutputDir, + String classOutputDir, + String additionalCompilerClasspath) { + CompilationOutcomeDescriptor kotlinCompilationOutcome = compileWithKotlin( + context, + compilationRequest, + sourceOutputDir, + classOutputDir + ); + + if ( kotlinCompilationOutcome != null && + kotlinCompilationOutcome.getCompilationResult() == CompilationResult.FAILED ) { + return kotlinCompilationOutcome; + } + + CompilationOutcomeDescriptor javaCompilationOutcome = compileWithSpecificCompiler( + compilationRequest, + sourceOutputDir, + classOutputDir, + additionalCompilerClasspath + ); + return kotlinCompilationOutcome == null ? javaCompilationOutcome : + kotlinCompilationOutcome.merge( javaCompilationOutcome ); + } + + private CompilationOutcomeDescriptor compileWithKotlin( + ExtensionContext context, + CompilationRequest compilationRequest, + String sourceOutputDir, + String classOutputDir + ) { + CompilationOutcomeDescriptor kotlinCompilationOutcome = null; + if ( !compilationRequest.getKotlinSources().isEmpty() ) { + K2JVMCompiler k2JvmCompiler = new K2JVMCompiler(); + MessageCollectorImpl messageCollector = new MessageCollectorImpl(); + K2JVMCompilerArguments k2JvmArguments = new K2JVMCompilerArguments(); + k2JvmArguments.setClasspath( + String.join( + File.pathSeparator, filterBootClassPath( List.of( + "kotlin-stdlib", + "kotlin-reflect" + ) ) + ) + ); + k2JvmArguments.setNoStdlib( true ); + k2JvmArguments.setNoReflect( true ); + String packageName = context.getRequiredTestClass() + .getPackageName(); + String sourcePrefix = + SOURCE_DIR + File.separator + packageName.replace( ".", File.separator ) + File.separator; + k2JvmArguments.setFreeArgs( Arrays.asList( + compilationRequest.getKotlinSources() + .stream() + .map( kotlinSource -> sourcePrefix + kotlinSource ) + .collect( Collectors.joining( File.pathSeparator ) ) + ) ); + k2JvmArguments.setVerbose( true ); + k2JvmArguments.setSuppressWarnings( false ); + k2JvmArguments.setDestination( classOutputDir ); + + ExitCode kotlinExitCode = k2JvmCompiler.exec( messageCollector, Services.EMPTY, k2JvmArguments ); + + kotlinCompilationOutcome = CompilationOutcomeDescriptor.forResult( + sourceOutputDir, + kotlinExitCode, + messageCollector.getMessages() + ); + + } + return kotlinCompilationOutcome; + } + protected abstract CompilationOutcomeDescriptor compileWithSpecificCompiler( CompilationRequest compilationRequest, String sourceOutputDir, diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java index c356347664..4755df0bd1 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java @@ -68,24 +68,27 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe return clHelper.compileInOtherClassloader( compilationRequest, - getTestCompilationClasspath( compilationRequest ), + getTestCompilationClasspath( compilationRequest, classOutputDir ), getSourceFiles( compilationRequest.getSourceClasses() ), SOURCE_DIR, sourceOutputDir, classOutputDir ); } - private static List getTestCompilationClasspath(CompilationRequest request) { + private static List getTestCompilationClasspath(CompilationRequest request, String classOutputDir) { Collection testDependencies = request.getTestDependencies(); - if ( testDependencies.isEmpty() ) { + if ( testDependencies.isEmpty() && request.getKotlinSources().isEmpty() ) { return TEST_COMPILATION_CLASSPATH; } List testCompilationPaths = new ArrayList<>( - TEST_COMPILATION_CLASSPATH.size() + testDependencies.size() ); + TEST_COMPILATION_CLASSPATH.size() + testDependencies.size() + 1 ); testCompilationPaths.addAll( TEST_COMPILATION_CLASSPATH ); testCompilationPaths.addAll( filterBootClassPath( testDependencies ) ); + if ( !request.getKotlinSources().isEmpty() ) { + testCompilationPaths.add( classOutputDir ); + } return testCompilationPaths; } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java index 5185bfb7ed..b6d8afd3bf 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java @@ -59,7 +59,10 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe fileManager.getJavaFileObjectsFromFiles( getSourceFiles( compilationRequest.getSourceClasses() ) ); try { - fileManager.setLocation( StandardLocation.CLASS_PATH, getCompilerClasspathFiles( compilationRequest ) ); + fileManager.setLocation( + StandardLocation.CLASS_PATH, + getCompilerClasspathFiles( compilationRequest, classOutputDir ) + ); fileManager.setLocation( StandardLocation.CLASS_OUTPUT, Arrays.asList( new File( classOutputDir ) ) ); fileManager.setLocation( StandardLocation.SOURCE_OUTPUT, Arrays.asList( new File( sourceOutputDir ) ) ); } @@ -99,20 +102,24 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe diagnostics.getDiagnostics() ); } - private static List getCompilerClasspathFiles(CompilationRequest request) { + private static List getCompilerClasspathFiles(CompilationRequest request, String classOutputDir) { Collection testDependencies = request.getTestDependencies(); - if ( testDependencies.isEmpty() ) { + if ( testDependencies.isEmpty() && request.getKotlinSources().isEmpty() ) { return COMPILER_CLASSPATH_FILES; } List compilerClasspathFiles = new ArrayList<>( - COMPILER_CLASSPATH_FILES.size() + testDependencies.size() ); + COMPILER_CLASSPATH_FILES.size() + testDependencies.size() + 1 ); compilerClasspathFiles.addAll( COMPILER_CLASSPATH_FILES ); for ( String testDependencyPath : filterBootClassPath( testDependencies ) ) { compilerClasspathFiles.add( new File( testDependencyPath ) ); } + if ( !request.getKotlinSources().isEmpty() ) { + compilerClasspathFiles.add( new File( classOutputDir ) ); + } + return compilerClasspathFiles; } From d3006640938014a1d8fa193d735cee934771309b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 29 Jan 2026 12:30:13 +0100 Subject: [PATCH 512/597] Improve support for Kotlin Data Classes (#3978) * Properly handle single field data classes * Properly handle multi constructor data classes * Properly handle all defaults constructor data classes Fixes #2281, #2577, #3031 --- NEXT_RELEASE_CHANGELOG.md | 6 + distribution/pom.xml | 5 + .../main/asciidoc/chapter-2-set-up.asciidoc | 71 +++- parent/pom.xml | 5 + processor/pom.xml | 5 + .../ap/internal/model/BeanMappingMethod.java | 22 ++ .../ap/internal/model/common/Type.java | 156 ++++++++ .../internal/util/kotlin/KotlinMetadata.java | 21 + .../ap/spi/DefaultAccessorNamingStrategy.java | 68 +++- .../test/kotlin/data/AllDefaultsProperty.kt | 11 + .../data/AllDefaultsPropertyMapper.java | 39 ++ .../ap/test/kotlin/data/KotlinDataTest.java | 370 ++++++++++++++++++ .../kotlin/data/MultiConstructorProperty.kt | 13 + .../data/MultiConstructorPropertyMapper.java | 45 +++ .../data/MultiDefaultConstructorProperty.kt | 18 + ...MultiDefaultConstructorPropertyMapper.java | 45 +++ .../data/MultiSimilarConstructorProperty.kt | 50 +++ ...MultiSimilarConstructorPropertyMapper.java | 57 +++ .../ap/test/kotlin/data/SingleProperty.kt | 11 + .../kotlin/data/SinglePropertyMapper.java | 32 ++ .../org/mapstruct/ap/testutil/WithKotlin.java | 29 ++ .../testutil/WithProcessorDependencies.java | 22 ++ .../ap/testutil/WithProcessorDependency.java | 28 ++ .../testutil/runner/CompilationRequest.java | 8 + .../testutil/runner/CompilingExtension.java | 22 ++ .../runner/EclipseCompilingExtension.java | 18 +- .../runner/JdkCompilingExtension.java | 31 +- 27 files changed, 1186 insertions(+), 22 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/kotlin/KotlinMetadata.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/AllDefaultsProperty.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/AllDefaultsPropertyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiConstructorProperty.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiConstructorPropertyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorProperty.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorPropertyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiSimilarConstructorProperty.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiSimilarConstructorPropertyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/SingleProperty.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/SinglePropertyMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithKotlin.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithProcessorDependencies.java create mode 100644 processor/src/test/java/org/mapstruct/ap/testutil/WithProcessorDependency.java diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 2d0555cb24..f279193f6a 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,6 +1,12 @@ ### Features * Support for Java 21 Sequenced Collections (#3240) +* Improved support for Kotlin. Requires use of `org.jetbrains.kotlin:kotlin-metadata-jvm`. + - Data Classes (#2281, #2577, #3031) - MapStruct now properly handles: + - Single field data classes + - Proper primary constructor detection + - Data classes with multiple constructors + - Data classes with all default parameters ### Enhancements diff --git a/distribution/pom.xml b/distribution/pom.xml index db654236e4..b480e24a06 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -39,6 +39,11 @@ org.mapstruct.tools.gem gem-api + + + org.jetbrains.kotlin + kotlin-metadata-jvm + jakarta.xml.bind diff --git a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc index 58759ff8e0..86ed345630 100644 --- a/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc +++ b/documentation/src/main/asciidoc/chapter-2-set-up.asciidoc @@ -35,10 +35,10 @@ For Maven based projects add the following to your POM file in order to use MapS org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.14.1 - 1.8 - 1.8 + ${java.version} + ${java.version} org.mapstruct @@ -141,10 +141,10 @@ When invoking javac directly, these options are passed to the compiler in the fo org.apache.maven.plugins maven-compiler-plugin - 3.5.1 + 3.14.1 - 1.8 - 1.8 + ${java.version} + ${java.version} org.mapstruct @@ -323,3 +323,62 @@ Some features include: * Code completion in `target` and `source` * Quick Fixes + +[[kotlin-setup]] +=== Kotlin Support + +MapStruct provides support for Kotlin interoperability with Java. + +When using MapStruct with Kotlin, it's recommended to add the `org.jetbrains.kotlin:kotlin-metadata-jvm` library to enable proper introspection of Kotlin metadata: + +.Maven configuration for Kotlin support +==== +[source, xml, linenums] +[subs="verbatim,attributes"] +---- +... + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.1 + + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + org.jetbrains.kotlin + kotlin-metadata-jvm + ${kotlin.version} + + + + + + +---- +==== + +.Gradle configuration for Kotlin support +==== +[source, groovy, linenums] +[subs="verbatim,attributes"] +---- +dependencies { + implementation "org.mapstruct:mapstruct:${mapstructVersion}" + + annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" + annotationProcessor "org.jetbrains.kotlin:kotlin-metadata-jvm:${kotlinVersion}" +} +---- +==== + +[NOTE] +==== +The `org.jetbrains.kotlin:kotlin-metadata-jvm` dependency is optional but highly recommended when working with Kotlin and Java. +Without it, MapStruct will still work but may not handle complex Kotlin scenarios optimally. +==== \ No newline at end of file diff --git a/parent/pom.xml b/parent/pom.xml index 5c3a73da88..8c1266ed19 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -159,6 +159,11 @@ pom import + + org.jetbrains.kotlin + kotlin-metadata-jvm + ${kotlin.version} + org.junit junit-bom diff --git a/processor/pom.xml b/processor/pom.xml index 9f24281e33..c51c2bca26 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -42,6 +42,11 @@ mapstruct provided + + org.jetbrains.kotlin + kotlin-metadata-jvm + provided + org.jetbrains.kotlin kotlin-compiler-embeddable diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 39222efc88..619272a175 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -68,6 +68,7 @@ import org.mapstruct.ap.internal.util.accessor.ElementAccessor; import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; import org.mapstruct.ap.internal.util.accessor.ReadAccessor; +import org.mapstruct.ap.internal.util.kotlin.KotlinMetadata; import static org.mapstruct.ap.internal.model.beanmapping.MappingReferences.forSourceMethod; import static org.mapstruct.ap.internal.util.Collections.first; @@ -904,6 +905,27 @@ private ConstructorAccessor getConstructorAccessor(Type type) { return new ConstructorAccessor( parameterBindings, constructorAccessors ); } + KotlinMetadata kotlinMetadata = type.getKotlinMetadata(); + if ( kotlinMetadata != null && kotlinMetadata.isDataClass() ) { + List constructors = ElementFilter.constructorsIn( type.getTypeElement() + .getEnclosedElements() ); + + for ( ExecutableElement constructor : constructors ) { + if ( constructor.getModifiers().contains( Modifier.PRIVATE ) ) { + continue; + } + + // prefer constructor annotated with @Default + if ( hasDefaultAnnotationFromAnyPackage( constructor ) ) { + return getConstructorAccessor( type, constructor ); + } + } + + ExecutableElement primaryConstructor = kotlinMetadata.determinePrimaryConstructor( constructors ); + + return primaryConstructor != null ? getConstructorAccessor( type, primaryConstructor ) : null; + } + List constructors = ElementFilter.constructorsIn( type.getTypeElement() .getEnclosedElements() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 24f23ecea8..313ff38125 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -43,6 +43,13 @@ import javax.lang.model.util.ElementFilter; import javax.lang.model.util.SimpleTypeVisitor8; +import kotlin.Metadata; +import kotlin.metadata.Attributes; +import kotlin.metadata.KmClass; +import kotlin.metadata.KmConstructor; +import kotlin.metadata.jvm.JvmExtensionsKt; +import kotlin.metadata.jvm.JvmMethodSignature; +import kotlin.metadata.jvm.KotlinClassMetadata; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.util.AccessorNamingUtils; import org.mapstruct.ap.internal.util.ElementUtils; @@ -58,6 +65,7 @@ import org.mapstruct.ap.internal.util.accessor.MapValueAccessor; import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; import org.mapstruct.ap.internal.util.accessor.ReadAccessor; +import org.mapstruct.ap.internal.util.kotlin.KotlinMetadata; import static java.util.Collections.emptyList; import static org.mapstruct.ap.internal.util.Collections.first; @@ -75,6 +83,7 @@ */ public class Type extends ModelElement implements Comparable { private static final Method SEALED_PERMITTED_SUBCLASSES_METHOD; + private static final boolean KOTLIN_METADATA_JVM_PRESENT; static { Method permittedSubclassesMethod; @@ -85,6 +94,16 @@ public class Type extends ModelElement implements Comparable { permittedSubclassesMethod = null; } SEALED_PERMITTED_SUBCLASSES_METHOD = permittedSubclassesMethod; + + boolean kotlinMetadataJvmPresent; + try { + Class.forName( "kotlin.metadata.jvm.KotlinClassMetadata", false, ModelElement.class.getClassLoader() ); + kotlinMetadataJvmPresent = true; + } + catch ( ClassNotFoundException e ) { + kotlinMetadataJvmPresent = false; + } + KOTLIN_METADATA_JVM_PRESENT = kotlinMetadataJvmPresent; } private final TypeUtils typeUtils; @@ -139,6 +158,8 @@ public class Type extends ModelElement implements Comparable { private Type boxedEquivalent = null; private Boolean hasAccessibleConstructor; + private KotlinMetadata kotlinMetadata; + private boolean kotlinMetadataInitialized; private final Filters filters; @@ -1370,6 +1391,23 @@ public boolean hasAccessibleConstructor() { return hasAccessibleConstructor; } + public KotlinMetadata getKotlinMetadata() { + if ( !kotlinMetadataInitialized ) { + kotlinMetadataInitialized = true; + if ( typeElement != null && KOTLIN_METADATA_JVM_PRESENT ) { + Metadata metadataAnnotation = typeElement.getAnnotation( Metadata.class ); + if ( metadataAnnotation != null ) { + KotlinClassMetadata classMetadata = KotlinClassMetadata.readLenient( metadataAnnotation ); + if ( classMetadata instanceof KotlinClassMetadata.Class ) { + kotlinMetadata = new KotlinMetadataImpl( (KotlinClassMetadata.Class) classMetadata ); + } + } + } + } + + return kotlinMetadata; + } + /** * Returns the direct supertypes of a type. The interface types, if any, * will appear last in the list. @@ -1831,4 +1869,122 @@ public List getPermittedSubclasses() { } } + private class KotlinMetadataImpl implements KotlinMetadata { + + private final KotlinClassMetadata.Class kotlinClassMetadata; + + private KotlinMetadataImpl(KotlinClassMetadata.Class kotlinClassMetadata) { + this.kotlinClassMetadata = kotlinClassMetadata; + } + + @Override + public boolean isDataClass() { + return Attributes.isData( kotlinClassMetadata.getKmClass() ); + } + + @Override + public ExecutableElement determinePrimaryConstructor(List constructors) { + if ( constructors.size() == 1 ) { + // If we have one constructor, that this constructor is the primary one + return constructors.get( 0 ); + } + KmClass kmClass = kotlinClassMetadata.getKmClass(); + KmConstructor primaryKmConstructor = null; + for ( KmConstructor constructor : kmClass.getConstructors() ) { + if ( !Attributes.isSecondary( constructor ) ) { + primaryKmConstructor = constructor; + } + + } + + if ( primaryKmConstructor == null ) { + return null; + } + + List sameParametersSizeConstructors = new ArrayList<>(); + for ( ExecutableElement constructor : constructors ) { + if ( constructor.getParameters().size() == primaryKmConstructor.getValueParameters().size() ) { + sameParametersSizeConstructors.add( constructor ); + } + } + + if ( sameParametersSizeConstructors.size() == 1 ) { + return sameParametersSizeConstructors.get( 0 ); + } + + JvmMethodSignature signature = JvmExtensionsKt.getSignature( primaryKmConstructor ); + if ( signature == null ) { + return null; + } + + String signatureDescriptor = signature.getDescriptor(); + for ( ExecutableElement constructor : constructors ) { + String constructorDescriptor = buildJvmConstructorDescriptor( constructor ); + if ( signatureDescriptor.equals( constructorDescriptor ) ) { + return constructor; + } + } + + return null; + } + + private String buildJvmConstructorDescriptor(ExecutableElement constructor) { + StringBuilder signature = new StringBuilder( "(" ); + + for ( VariableElement param : constructor.getParameters() ) { + signature.append( getJvmTypeDescriptor( param.asType() ) ); + } + + signature.append( ")V" ); + return signature.toString(); + } + + private String getJvmTypeDescriptor(TypeMirror type) { + return type.accept( + new SimpleTypeVisitor8() { + @Override + public String visitPrimitive(PrimitiveType t, Void p) { + switch ( t.getKind() ) { + case BOOLEAN: + return "Z"; + case BYTE: + return "B"; + case SHORT: + return "S"; + case INT: + return "I"; + case LONG: + return "J"; + case CHAR: + return "C"; + case FLOAT: + return "F"; + case DOUBLE: + return "D"; + default: + return ""; + } + } + + @Override + public String visitDeclared(DeclaredType t, Void p) { + TypeElement element = (TypeElement) t.asElement(); + String binaryName = elementUtils.getBinaryName( element ).toString(); + return "L" + binaryName.replace( '.', '/' ) + ";"; + } + + @Override + public String visitArray(ArrayType t, Void p) { + return "[" + getJvmTypeDescriptor( t.getComponentType() ); + } + + @Override + protected String defaultAction(TypeMirror e, Void p) { + return ""; + } + }, null + ); + } + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/kotlin/KotlinMetadata.java b/processor/src/main/java/org/mapstruct/ap/internal/util/kotlin/KotlinMetadata.java new file mode 100644 index 0000000000..d58ab75c79 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/kotlin/KotlinMetadata.java @@ -0,0 +1,21 @@ +/* + * 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.internal.util.kotlin; + +import java.util.List; +import javax.lang.model.element.ExecutableElement; + +/** + * Information about a type in case it's a Kotlin type. + * + * @author Filip Hrisafov + */ +public interface KotlinMetadata { + + boolean isDataClass(); + + ExecutableElement determinePrimaryConstructor(List constructors); +} diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java index 82ecb4e17b..511504c937 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java @@ -7,6 +7,7 @@ import java.util.regex.Pattern; +import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; @@ -17,6 +18,9 @@ import javax.lang.model.util.SimpleTypeVisitor6; import javax.lang.model.util.Types; +import kotlin.Metadata; +import kotlin.metadata.Attributes; +import kotlin.metadata.jvm.KotlinClassMetadata; import org.mapstruct.ap.spi.util.IntrospectorUtils; /** @@ -28,6 +32,24 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy { private static final Pattern JAVA_JAVAX_PACKAGE = Pattern.compile( "^javax?\\..*" ); + private static final boolean KOTLIN_METADATA_JVM_PRESENT; + + static { + boolean kotlinMetadataJvmPresent; + try { + Class.forName( + "kotlin.metadata.jvm.KotlinClassMetadata", + false, + AccessorNamingStrategy.class.getClassLoader() + ); + kotlinMetadataJvmPresent = true; + } + catch ( ClassNotFoundException e ) { + kotlinMetadataJvmPresent = false; + } + KOTLIN_METADATA_JVM_PRESENT = kotlinMetadataJvmPresent; + } + protected Elements elementUtils; protected Types typeUtils; @@ -102,10 +124,48 @@ public boolean isSetterMethod(ExecutableElement method) { } protected boolean isFluentSetter(ExecutableElement method) { - return method.getParameters().size() == 1 && - !JAVA_JAVAX_PACKAGE.matcher( method.getEnclosingElement().asType().toString() ).matches() && - !isAdderWithUpperCase4thCharacter( method ) && - typeUtils.isAssignable( method.getReturnType(), method.getEnclosingElement().asType() ); + if ( method.getParameters().size() != 1 ) { + return false; + } + Element methodOwnerElement = method.getEnclosingElement(); + if ( !canMethodOwnerBeUsedInFluentSetter( methodOwnerElement ) ) { + return false; + } + return !isAdderWithUpperCase4thCharacter( method ) && + typeUtils.isAssignable( method.getReturnType(), methodOwnerElement.asType() ); + } + + private boolean canMethodOwnerBeUsedInFluentSetter(Element methodOwnerElement) { + if ( !( methodOwnerElement instanceof TypeElement ) ) { + return false; + } + + TypeElement typeElement = (TypeElement) methodOwnerElement; + if ( JAVA_JAVAX_PACKAGE.matcher( typeElement.getQualifiedName().toString() ).matches() ) { + return false; + } + if ( isKotlinDataClass( typeElement ) ) { + return false; + } + + return true; + } + + private boolean isKotlinDataClass(TypeElement typeElement) { + if ( !KOTLIN_METADATA_JVM_PRESENT ) { + return false; + } + + Metadata kotlinMetadata = typeElement.getAnnotation( Metadata.class ); + if ( kotlinMetadata == null ) { + return false; + } + KotlinClassMetadata classMetadata = KotlinClassMetadata.readLenient( kotlinMetadata ); + if ( classMetadata instanceof KotlinClassMetadata.Class ) { + return Attributes.isData( ( (KotlinClassMetadata.Class) classMetadata ).getKmClass() ); + } + + return false; } /** diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/AllDefaultsProperty.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/AllDefaultsProperty.kt new file mode 100644 index 0000000000..e452102f6e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/AllDefaultsProperty.kt @@ -0,0 +1,11 @@ +/* + * 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.kotlin.data + +/** + * @author Filip Hrisafov + */ +data class AllDefaultsProperty(val firstName: String? = null, val lastName: String? = null) diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/AllDefaultsPropertyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/AllDefaultsPropertyMapper.java new file mode 100644 index 0000000000..1f8aab4624 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/AllDefaultsPropertyMapper.java @@ -0,0 +1,39 @@ +/* + * 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.kotlin.data; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface AllDefaultsPropertyMapper { + + AllDefaultsPropertyMapper INSTANCE = Mappers.getMapper( AllDefaultsPropertyMapper.class ); + + AllDefaultsProperty map(Source source); + + class Source { + + private final String firstName; + private final String lastName; + + public Source(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java index 310039363d..1daa19c231 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java @@ -8,7 +8,11 @@ import org.junit.jupiter.api.Nested; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithKotlin; import org.mapstruct.ap.testutil.WithKotlinSources; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; import static org.assertj.core.api.Assertions.assertThat; @@ -51,4 +55,370 @@ void shouldMapIntoData() { } } + + @Nested + @WithClasses({ + SinglePropertyMapper.class, + }) + @WithKotlinSources("SingleProperty.kt") + class SingleData { + + @ProcessorTest + @WithKotlin + void shouldCompileWithoutWarnings() { + + SingleProperty property = SinglePropertyMapper.INSTANCE.map( new SinglePropertyMapper.Source( "test" ) ); + assertThat( property ).isNotNull(); + assertThat( property.getValue() ).isEqualTo( "test" ); + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.WARNING, + type = SinglePropertyMapper.class, + line = 19, + message = "Unmapped target property: \"copy\"." + ) + } + ) + void shouldCompileWithWarnings() { + + SingleProperty property = SinglePropertyMapper.INSTANCE.map( new SinglePropertyMapper.Source( "test" ) ); + assertThat( property ).isNotNull(); + assertThat( property.getValue() ).isEqualTo( "test" ); + } + } + + @Nested + @WithClasses({ + AllDefaultsPropertyMapper.class, + }) + @WithKotlinSources("AllDefaultsProperty.kt") + class AllDefaults { + + @ProcessorTest + @WithKotlin + void shouldCompileWithoutWarnings() { + + AllDefaultsProperty property = AllDefaultsPropertyMapper.INSTANCE.map( new AllDefaultsPropertyMapper.Source( + "Kermit", + "the Frog" + ) ); + assertThat( property ).isNotNull(); + assertThat( property.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( property.getLastName() ).isEqualTo( "the Frog" ); + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.WARNING, + type = AllDefaultsPropertyMapper.class, + line = 19, + message = "No target property found for target \"AllDefaultsProperty\"." + ) + } + ) + void shouldCompileWithWarnings() { + + AllDefaultsProperty property = AllDefaultsPropertyMapper.INSTANCE.map( new AllDefaultsPropertyMapper.Source( + "Kermit", + "the Frog" + ) ); + assertThat( property ).isNotNull(); + assertThat( property.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( property.getLastName() ).isEqualTo( "the Frog" ); + } + } + + @Nested + @WithClasses({ + MultiConstructorPropertyMapper.class, + }) + @WithKotlinSources("MultiConstructorProperty.kt") + class MultiConstructor { + + @ProcessorTest + @WithKotlin + void shouldCompileWithoutWarnings() { + + MultiConstructorProperty property = MultiConstructorPropertyMapper.INSTANCE + .map( new MultiConstructorPropertyMapper.Source( + "Kermit", + "the Frog", + "Kermit the Frog" + ) ); + assertThat( property ).isNotNull(); + assertThat( property.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( property.getLastName() ).isEqualTo( "the Frog" ); + assertThat( property.getDisplayName() ).isEqualTo( "Kermit the Frog" ); + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiConstructorPropertyMapper.class, + line = 19, + messageRegExp = "Ambiguous constructors found for creating .*MultiConstructorProperty: " + + "MultiConstructorProperty\\(java.lang.String, java.lang.String.*\\), " + + "MultiConstructorProperty\\(java.lang.String, java.lang.String.*\\)\\. " + + "Either declare parameterless constructor or annotate the default constructor with an " + + "annotation named @Default\\." + ) + } + ) + void shouldFailToCompile() { + + } + } + + @Nested + @WithClasses({ + MultiSimilarConstructorPropertyMapper.class, + }) + @WithKotlinSources("MultiSimilarConstructorProperty.kt") + class MultiSimilarConstructor { + + @ProcessorTest + @WithKotlin + void shouldCompileWithoutWarnings() { + PrimaryString primaryString = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + "Kermit the Frog" + ); + assertThat( primaryString ).isNotNull(); + assertThat( primaryString.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryString.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryString.getDisplayName() ).isEqualTo( "Kermit the Frog" ); + + PrimaryInt primaryInt = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + 42 + ); + assertThat( primaryInt ).isNotNull(); + assertThat( primaryInt.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryInt.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryInt.getAge() ).isEqualTo( 42 ); + + PrimaryLong primaryLong = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + 42L + ); + assertThat( primaryLong ).isNotNull(); + assertThat( primaryLong.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryLong.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryLong.getAge() ).isEqualTo( 42 ); + + PrimaryBoolean primaryBoolean = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + true + ); + assertThat( primaryBoolean ).isNotNull(); + assertThat( primaryBoolean.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryBoolean.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryBoolean.getActive() ).isTrue(); + + PrimaryByte primaryByte = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + (byte) 4 + ); + assertThat( primaryByte ).isNotNull(); + assertThat( primaryByte.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryByte.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryByte.getB() ).isEqualTo( (byte) 4 ); + + PrimaryShort primaryShort = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + (short) 4 + ); + assertThat( primaryShort ).isNotNull(); + assertThat( primaryShort.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryShort.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryShort.getAge() ).isEqualTo( (short) 4 ); + + PrimaryChar primaryChar = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + 't' + ); + assertThat( primaryChar ).isNotNull(); + assertThat( primaryChar.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryChar.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryChar.getC() ).isEqualTo( 't' ); + + PrimaryFloat primaryFloat = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + 42.2f + ); + assertThat( primaryFloat ).isNotNull(); + assertThat( primaryFloat.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryFloat.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryFloat.getPrice() ).isEqualTo( 42.2f ); + + PrimaryDouble primaryDouble = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + 42.2 + ); + assertThat( primaryDouble ).isNotNull(); + assertThat( primaryDouble.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryDouble.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryDouble.getPrice() ).isEqualTo( 42.2d ); + + PrimaryArray primaryArray = MultiSimilarConstructorPropertyMapper.INSTANCE.map( + new MultiSimilarConstructorPropertyMapper.Source( + "Kermit", + "the Frog" + ), + new String[] { "Kermit", "the Frog" } + ); + assertThat( primaryArray ).isNotNull(); + assertThat( primaryArray.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( primaryArray.getLastName() ).isEqualTo( "the Frog" ); + assertThat( primaryArray.getElements() ).containsExactly( "Kermit", "the Frog" ); + } + + @ProcessorTest + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 19, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryString" + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 21, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryInt" + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 23, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryLong" + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 25, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryBoolean" + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 27, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryByte" + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 29, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryShort" + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 31, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryChar" + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 33, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryFloat" + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 35, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryDouble" + ), + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.ERROR, + type = MultiSimilarConstructorPropertyMapper.class, + line = 37, + messageRegExp = "Ambiguous constructors found for creating .*PrimaryArray" + ) + + + } + ) + void shouldFailToCompile() { + + } + } + + @Nested + @WithClasses({ + MultiDefaultConstructorPropertyMapper.class, + }) + @WithKotlinSources("MultiDefaultConstructorProperty.kt") + class MultiDefaultConstructor { + + @ProcessorTest + @WithKotlin + void shouldCompileWithoutWarnings() { + + MultiDefaultConstructorProperty property = MultiDefaultConstructorPropertyMapper.INSTANCE + .map( new MultiDefaultConstructorPropertyMapper.Source( + "Kermit", + "the Frog", + "Kermit the Frog" + ) ); + assertThat( property ).isNotNull(); + assertThat( property.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( property.getLastName() ).isEqualTo( "the Frog" ); + assertThat( property.getDisplayName() ).isNull(); + } + + @ProcessorTest + void shouldCompileWithoutKotlin() { + MultiDefaultConstructorProperty property = MultiDefaultConstructorPropertyMapper.INSTANCE + .map( new MultiDefaultConstructorPropertyMapper.Source( + "Kermit", + "the Frog", + "Kermit the Frog" + ) ); + assertThat( property ).isNotNull(); + assertThat( property.getFirstName() ).isEqualTo( "Kermit" ); + assertThat( property.getLastName() ).isEqualTo( "the Frog" ); + assertThat( property.getDisplayName() ).isNull(); + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiConstructorProperty.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiConstructorProperty.kt new file mode 100644 index 0000000000..46cb8034e9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiConstructorProperty.kt @@ -0,0 +1,13 @@ +/* + * 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.kotlin.data + +/** + * @author Filip Hrisafov + */ +data class MultiConstructorProperty(var firstName: String?, var lastName: String?, var displayName: String?) { + constructor(firstName: String?, lastName: String?) : this(firstName, lastName, null) +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiConstructorPropertyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiConstructorPropertyMapper.java new file mode 100644 index 0000000000..3186226a57 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiConstructorPropertyMapper.java @@ -0,0 +1,45 @@ +/* + * 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.kotlin.data; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface MultiConstructorPropertyMapper { + + MultiConstructorPropertyMapper INSTANCE = Mappers.getMapper( MultiConstructorPropertyMapper.class ); + + MultiConstructorProperty map(Source source); + + class Source { + + private final String firstName; + private final String lastName; + private final String displayName; + + public Source(String firstName, String lastName, String displayName) { + this.firstName = firstName; + this.lastName = lastName; + this.displayName = displayName; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getDisplayName() { + return displayName; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorProperty.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorProperty.kt new file mode 100644 index 0000000000..712cf1151b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorProperty.kt @@ -0,0 +1,18 @@ +/* + * 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.kotlin.data + +/** + * @author Filip Hrisafov + */ +data class MultiDefaultConstructorProperty(val firstName: String?, val lastName: String?, val displayName: String?) { + @Default + constructor(firstName: String?, lastName: String?) : this(firstName, lastName, null) +} + +@Target(AnnotationTarget.CONSTRUCTOR) +@Retention(AnnotationRetention.BINARY) +annotation class Default diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorPropertyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorPropertyMapper.java new file mode 100644 index 0000000000..da04c01fb8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorPropertyMapper.java @@ -0,0 +1,45 @@ +/* + * 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.kotlin.data; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface MultiDefaultConstructorPropertyMapper { + + MultiDefaultConstructorPropertyMapper INSTANCE = Mappers.getMapper( MultiDefaultConstructorPropertyMapper.class ); + + MultiDefaultConstructorProperty map(Source source); + + class Source { + + private final String firstName; + private final String lastName; + private final String displayName; + + public Source(String firstName, String lastName, String displayName) { + this.firstName = firstName; + this.lastName = lastName; + this.displayName = displayName; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getDisplayName() { + return displayName; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiSimilarConstructorProperty.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiSimilarConstructorProperty.kt new file mode 100644 index 0000000000..e122169075 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiSimilarConstructorProperty.kt @@ -0,0 +1,50 @@ +/* + * 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.kotlin.data + +/** + * @author Filip Hrisafov + */ +data class PrimaryString(var firstName: String?, var lastName: String?, var displayName: String?) { + constructor(firstName: String?, lastName: String?, age: Int) : this(firstName, lastName, null as String?) +} + +data class PrimaryInt(var firstName: String?, var lastName: String?, var age: Int) { + constructor(firstName: String?, lastName: String?, displayName: String?) : this(firstName, lastName, -1) +} + +data class PrimaryLong(var firstName: String?, var lastName: String?, var age: Long) { + constructor(firstName: String?, lastName: String?, displayName: String?) : this(firstName, lastName, -1) +} + +data class PrimaryBoolean(var firstName: String?, var lastName: String?, var active: Boolean) { + constructor(firstName: String?, lastName: String?, displayName: String?) : this(firstName, lastName, false) +} + +data class PrimaryByte(var firstName: String?, var lastName: String?, var b: Byte) { + constructor(firstName: String?, lastName: String?, displayName: String?) : this(firstName, lastName, 0) +} + +data class PrimaryShort(var firstName: String?, var lastName: String?, var age: Short) { + constructor(firstName: String?, lastName: String?, displayName: String?) : this(firstName, lastName, 0) +} + +data class PrimaryChar(var firstName: String?, var lastName: String?, var c: Char) { + constructor(firstName: String?, lastName: String?, displayName: String?) : this(firstName, lastName, 'a') +} + +data class PrimaryFloat(var firstName: String?, var lastName: String?, var price: Float) { + constructor(firstName: String?, lastName: String?, displayName: String?) : this(firstName, lastName, 0.0f) +} + +data class PrimaryDouble(var firstName: String?, var lastName: String?, var price: Double) { + constructor(firstName: String?, lastName: String?, displayName: String?) : this(firstName, lastName, 0.0) +} + +@Suppress("ArrayInDataClass") +data class PrimaryArray(var firstName: String?, var lastName: String?, var elements: Array) { + constructor(firstName: String?, lastName: String?, displayName: String?) : this(firstName, lastName, emptyArray()) +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiSimilarConstructorPropertyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiSimilarConstructorPropertyMapper.java new file mode 100644 index 0000000000..86021cd6d4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiSimilarConstructorPropertyMapper.java @@ -0,0 +1,57 @@ +/* + * 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.kotlin.data; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface MultiSimilarConstructorPropertyMapper { + + MultiSimilarConstructorPropertyMapper INSTANCE = Mappers.getMapper( MultiSimilarConstructorPropertyMapper.class ); + + PrimaryString map(Source source, String displayName); + + PrimaryInt map(Source source, int age); + + PrimaryLong map(Source source, long age); + + PrimaryBoolean map(Source source, boolean active); + + PrimaryByte map(Source source, byte b); + + PrimaryShort map(Source source, short age); + + PrimaryChar map(Source source, char c); + + PrimaryFloat map(Source source, float price); + + PrimaryDouble map(Source source, double price); + + PrimaryArray map(Source source, String[] elements); + + class Source { + + private final String firstName; + private final String lastName; + + public Source(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/SingleProperty.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/SingleProperty.kt new file mode 100644 index 0000000000..1bb63c410d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/SingleProperty.kt @@ -0,0 +1,11 @@ +/* + * 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.kotlin.data + +/** + * @author Filip Hrisafov + */ +data class SingleProperty(val value: String?) diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/SinglePropertyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/SinglePropertyMapper.java new file mode 100644 index 0000000000..d3d70f261c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/SinglePropertyMapper.java @@ -0,0 +1,32 @@ +/* + * 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.kotlin.data; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SinglePropertyMapper { + + SinglePropertyMapper INSTANCE = Mappers.getMapper( SinglePropertyMapper.class ); + + SingleProperty map(Source source); + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithKotlin.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithKotlin.java new file mode 100644 index 0000000000..f4bd2c5970 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithKotlin.java @@ -0,0 +1,29 @@ +/* + * 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.testutil; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that adds the needed Spring Dependencies + * + * @author Filip Hrisafov + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@WithProcessorDependency({ + "kotlin-stdlib", + "kotlin-metadata-jvm", +}) +@WithTestDependency("kotlin-stdlib") +public @interface WithKotlin { + +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithProcessorDependencies.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithProcessorDependencies.java new file mode 100644 index 0000000000..cccb30b6b4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithProcessorDependencies.java @@ -0,0 +1,22 @@ +/* + * 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.testutil; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Filip Hrisafov + * @see WithProcessorDependency + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +public @interface WithProcessorDependencies { + + WithProcessorDependency[] value(); +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/WithProcessorDependency.java b/processor/src/test/java/org/mapstruct/ap/testutil/WithProcessorDependency.java new file mode 100644 index 0000000000..8dbf8c8acd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/testutil/WithProcessorDependency.java @@ -0,0 +1,28 @@ +/* + * 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.testutil; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the group id of the additional dependencies that should be added to the processor path + * + * @author Filip Hrisafov + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Repeatable(WithProcessorDependencies.class) +public @interface WithProcessorDependency { + /** + * @return The group ids of the additional dependencies for the processor path + */ + String[] value(); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java index 8622fbe92c..c9718c6c21 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilationRequest.java @@ -19,16 +19,19 @@ public class CompilationRequest { private final Map, Class> services; private final List processorOptions; private final Collection testDependencies; + private final Collection processorDependencies; private final Collection kotlinSources; CompilationRequest(Compiler compiler, Set> sourceClasses, Map, Class> services, List processorOptions, Collection testDependencies, + Collection processorDependencies, Collection kotlinSources) { this.compiler = compiler; this.sourceClasses = sourceClasses; this.services = services; this.processorOptions = processorOptions; this.testDependencies = testDependencies; + this.processorDependencies = processorDependencies; this.kotlinSources = kotlinSources; } @@ -61,6 +64,7 @@ public boolean equals(Object obj) { && processorOptions.equals( other.processorOptions ) && services.equals( other.services ) && testDependencies.equals( other.testDependencies ) + && processorDependencies.equals( other.processorDependencies ) && sourceClasses.equals( other.sourceClasses ) && kotlinSources.equals( other.kotlinSources ); } @@ -81,6 +85,10 @@ public Collection getTestDependencies() { return testDependencies; } + public Collection getProcessorDependencies() { + return processorDependencies; + } + public Collection getKotlinSources() { return kotlinSources; } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java index 7ff78753a6..d8ed3b953d 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java @@ -40,6 +40,7 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithKotlinSources; +import org.mapstruct.ap.testutil.WithProcessorDependency; import org.mapstruct.ap.testutil.WithServiceImplementation; import org.mapstruct.ap.testutil.WithTestDependency; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; @@ -152,6 +153,15 @@ protected static List filterBootClassPath(Collection whitelist) return classpath; } + protected static Collection getProcessorClasspathDependencies(CompilationRequest request, + String additionalCompilerClasspath) { + List processClassPaths = filterBootClassPath( request.getProcessorDependencies() ); + if ( additionalCompilerClasspath != null ) { + processClassPaths.add( additionalCompilerClasspath ); + } + return processClassPaths; + } + private static boolean isWhitelisted(String path, Collection whitelist) { return whitelist.stream().anyMatch( path::contains ); } @@ -392,6 +402,17 @@ private Collection getAdditionalTestDependencies(Method testMethod, Clas return testDependencies; } + private Collection getAdditionalProcessorDependencies(Method testMethod, Class testClass) { + Collection processorDependencies = new HashSet<>(); + findRepeatableAnnotations( testMethod, WithProcessorDependency.class ) + .forEach( annotation -> Collections.addAll( processorDependencies, annotation.value() ) ); + + findRepeatableAnnotations( testClass, WithProcessorDependency.class ) + .forEach( annotation -> Collections.addAll( processorDependencies, annotation.value() ) ); + + return processorDependencies; + } + private void addServices(Map, Class> services, List withImplementations) { for ( WithServiceImplementation withImplementation : withImplementations ) { addService( services, withImplementation ); @@ -486,6 +507,7 @@ private CompilationOutcomeDescriptor compile(ExtensionContext context) { getServices( testMethod, testClass ), getProcessorOptions( testMethod, testClass ), getAdditionalTestDependencies( testMethod, testClass ), + getAdditionalProcessorDependencies( testMethod, testClass ), getKotlinSources( context ) ); diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java index 4755df0bd1..6b13036be3 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/EclipseCompilingExtension.java @@ -47,8 +47,12 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe String sourceOutputDir, String classOutputDir, String additionalCompilerClasspath) { + Collection processorClassPaths = getProcessorClasspathDependencies( + compilationRequest, + additionalCompilerClasspath + ); ClassLoader compilerClassloader; - if ( additionalCompilerClasspath == null ) { + if ( processorClassPaths.isEmpty() ) { compilerClassloader = DEFAULT_ECLIPSE_COMPILER_CLASSLOADER; } else { @@ -59,7 +63,7 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe compilerClassloader = loader.withPaths( ECLIPSE_COMPILER_CLASSPATH ) .withPaths( PROCESSOR_CLASSPATH ) .withOriginOf( ClassLoaderExecutor.class ) - .withPath( additionalCompilerClasspath ) + .withPaths( processorClassPaths ) .withOriginsOf( compilationRequest.getServices().values() ); } @@ -77,16 +81,19 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe private static List getTestCompilationClasspath(CompilationRequest request, String classOutputDir) { Collection testDependencies = request.getTestDependencies(); - if ( testDependencies.isEmpty() && request.getKotlinSources().isEmpty() ) { + Collection processorDependencies = request.getProcessorDependencies(); + Collection kotlinSources = request.getKotlinSources(); + if ( testDependencies.isEmpty() && processorDependencies.isEmpty() && kotlinSources.isEmpty() ) { return TEST_COMPILATION_CLASSPATH; } List testCompilationPaths = new ArrayList<>( - TEST_COMPILATION_CLASSPATH.size() + testDependencies.size() + 1 ); + TEST_COMPILATION_CLASSPATH.size() + testDependencies.size() + processorDependencies.size() + 1 ); testCompilationPaths.addAll( TEST_COMPILATION_CLASSPATH ); testCompilationPaths.addAll( filterBootClassPath( testDependencies ) ); - if ( !request.getKotlinSources().isEmpty() ) { + testCompilationPaths.addAll( filterBootClassPath( processorDependencies ) ); + if ( !kotlinSources.isEmpty() ) { testCompilationPaths.add( classOutputDir ); } return testCompilationPaths; @@ -96,6 +103,7 @@ private static FilteringParentClassLoader newFilteringClassLoaderForEclipse() { return new FilteringParentClassLoader( // reload eclipse compiler classes "org.eclipse.", + "kotlin.", // reload mapstruct processor classes "org.mapstruct.ap.internal.", "org.mapstruct.ap.spi.", diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java index b6d8afd3bf..115618382c 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java @@ -39,7 +39,7 @@ class JdkCompilingExtension extends CompilingExtension { private static final List COMPILER_CLASSPATH_FILES = asFiles( TEST_COMPILATION_CLASSPATH ); private static final ClassLoader DEFAULT_PROCESSOR_CLASSLOADER = - new ModifiableURLClassLoader( new FilteringParentClassLoader( "org.mapstruct." ) ) + new ModifiableURLClassLoader( newFilteringClassLoaderForJdk() ) .withPaths( PROCESSOR_CLASSPATH ); JdkCompilingExtension() { @@ -70,15 +70,21 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe throw new RuntimeException( e ); } + Collection processorClassPaths = getProcessorClasspathDependencies( + compilationRequest, + additionalCompilerClasspath + ); ClassLoader processorClassloader; - if ( additionalCompilerClasspath == null ) { + if ( processorClassPaths.isEmpty() ) { processorClassloader = DEFAULT_PROCESSOR_CLASSLOADER; } else { processorClassloader = new ModifiableURLClassLoader( - new FilteringParentClassLoader( "org.mapstruct." ) ) + newFilteringClassLoaderForJdk() + .hidingClasses( compilationRequest.getServices().values() ) + ) .withPaths( PROCESSOR_CLASSPATH ) - .withPath( additionalCompilerClasspath ) + .withPaths( processorClassPaths ) .withOriginsOf( compilationRequest.getServices().values() ); } @@ -104,19 +110,21 @@ protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRe private static List getCompilerClasspathFiles(CompilationRequest request, String classOutputDir) { Collection testDependencies = request.getTestDependencies(); - if ( testDependencies.isEmpty() && request.getKotlinSources().isEmpty() ) { + Collection processorDependencies = request.getProcessorDependencies(); + Collection kotlinSources = request.getKotlinSources(); + if ( testDependencies.isEmpty() && processorDependencies.isEmpty() && kotlinSources.isEmpty() ) { return COMPILER_CLASSPATH_FILES; } List compilerClasspathFiles = new ArrayList<>( - COMPILER_CLASSPATH_FILES.size() + testDependencies.size() + 1 ); + COMPILER_CLASSPATH_FILES.size() + testDependencies.size() + processorDependencies.size() + 1 ); compilerClasspathFiles.addAll( COMPILER_CLASSPATH_FILES ); for ( String testDependencyPath : filterBootClassPath( testDependencies ) ) { compilerClasspathFiles.add( new File( testDependencyPath ) ); } - if ( !request.getKotlinSources().isEmpty() ) { + if ( !kotlinSources.isEmpty() ) { compilerClasspathFiles.add( new File( classOutputDir ) ); } @@ -161,4 +169,13 @@ protected List filterExpectedDiagnostics(List Date: Thu, 29 Jan 2026 15:38:08 +0100 Subject: [PATCH 513/597] Add test case with Kotlin unsigned type (#3979) --- .../ap/internal/model/BeanMappingMethod.java | 5 +- .../mapstruct/ap/test/kotlin/data/Default.kt | 10 ++++ .../ap/test/kotlin/data/KotlinDataTest.java | 50 ++++++++++++++++++- .../data/MultiDefaultConstructorProperty.kt | 3 -- .../ap/test/kotlin/data/UnsignedProperty.kt | 17 +++++++ .../kotlin/data/UnsignedPropertyMapper.java | 35 +++++++++++++ .../testutil/runner/CompilingExtension.java | 11 ++-- 7 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/Default.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/UnsignedProperty.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/UnsignedPropertyMapper.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 619272a175..086773dbe9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -910,8 +910,11 @@ private ConstructorAccessor getConstructorAccessor(Type type) { List constructors = ElementFilter.constructorsIn( type.getTypeElement() .getEnclosedElements() ); - for ( ExecutableElement constructor : constructors ) { + Iterator constructorIterator = constructors.iterator(); + while ( constructorIterator.hasNext() ) { + ExecutableElement constructor = constructorIterator.next(); if ( constructor.getModifiers().contains( Modifier.PRIVATE ) ) { + constructorIterator.remove(); continue; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/Default.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/Default.kt new file mode 100644 index 0000000000..58ebbd128d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/Default.kt @@ -0,0 +1,10 @@ +/* + * 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.kotlin.data + +@Target(AnnotationTarget.CONSTRUCTOR) +@Retention(AnnotationRetention.BINARY) +annotation class Default diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java index 1daa19c231..f55cb42535 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java @@ -10,6 +10,7 @@ import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.WithKotlin; import org.mapstruct.ap.testutil.WithKotlinSources; +import org.mapstruct.ap.testutil.WithTestDependency; import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; @@ -388,7 +389,10 @@ void shouldFailToCompile() { @WithClasses({ MultiDefaultConstructorPropertyMapper.class, }) - @WithKotlinSources("MultiDefaultConstructorProperty.kt") + @WithKotlinSources({ + "MultiDefaultConstructorProperty.kt", + "Default.kt" + }) class MultiDefaultConstructor { @ProcessorTest @@ -421,4 +425,48 @@ void shouldCompileWithoutKotlin() { assertThat( property.getDisplayName() ).isNull(); } } + + @Nested + @WithClasses({ + UnsignedPropertyMapper.class, + }) + @WithKotlinSources("UnsignedProperty.kt") + class Unsigned { + + @ProcessorTest + @WithKotlin + void shouldCompileWithoutWarnings() { + + UnsignedProperty property = UnsignedPropertyMapper.INSTANCE.map( new UnsignedPropertyMapper.Source( 10 ) ); + assertThat( property ).isNotNull(); + assertThat( property.getAge() ).isEqualTo( 10 ); + + UnsignedPropertyMapper.Source source = UnsignedPropertyMapper.INSTANCE.map( new UnsignedProperty( 20 ) ); + assertThat( source ).isNotNull(); + assertThat( source.getAge() ).isEqualTo( 20 ); + } + + @ProcessorTest + @WithTestDependency( "kotlin-stdlib" ) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic( + kind = javax.tools.Diagnostic.Kind.WARNING, + type = UnsignedPropertyMapper.class, + line = 19, + messageRegExp = "Unmapped target property: \"copy-.*\"\\." + ) + } + ) + void shouldCompileWithoutKotlinJvmMetadata() { + UnsignedProperty property = UnsignedPropertyMapper.INSTANCE.map( new UnsignedPropertyMapper.Source( 10 ) ); + assertThat( property ).isNotNull(); + assertThat( property.getAge() ).isEqualTo( 10 ); + + UnsignedPropertyMapper.Source source = UnsignedPropertyMapper.INSTANCE.map( new UnsignedProperty( 20 ) ); + assertThat( source ).isNotNull(); + assertThat( source.getAge() ).isEqualTo( 20 ); + } + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorProperty.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorProperty.kt index 712cf1151b..9f9af16053 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorProperty.kt +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/MultiDefaultConstructorProperty.kt @@ -13,6 +13,3 @@ data class MultiDefaultConstructorProperty(val firstName: String?, val lastName: constructor(firstName: String?, lastName: String?) : this(firstName, lastName, null) } -@Target(AnnotationTarget.CONSTRUCTOR) -@Retention(AnnotationRetention.BINARY) -annotation class Default diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/UnsignedProperty.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/UnsignedProperty.kt new file mode 100644 index 0000000000..c52426b3af --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/UnsignedProperty.kt @@ -0,0 +1,17 @@ +/* + * 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.kotlin.data + +/** + * @author Filip Hrisafov + */ +data class UnsignedProperty(val age: UInt?) { + // Java-friendly secondary constructor + constructor(age: Int?) : this(age?.toUInt()) + + @JvmName("getAge") + fun getAgeAsLong(): Long? = age?.toLong() +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/UnsignedPropertyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/UnsignedPropertyMapper.java new file mode 100644 index 0000000000..4f055c7b41 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/UnsignedPropertyMapper.java @@ -0,0 +1,35 @@ +/* + * 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.kotlin.data; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface UnsignedPropertyMapper { + + UnsignedPropertyMapper INSTANCE = Mappers.getMapper( UnsignedPropertyMapper.class ); + + UnsignedProperty map(Source source); + + Source map(UnsignedProperty property); + + class Source { + + private final int age; + + public Source(int age) { + this.age = age; + } + + public int getAge() { + return age; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java index d8ed3b953d..08056ca697 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java @@ -601,12 +601,11 @@ File.pathSeparator, filterBootClassPath( List.of( .getPackageName(); String sourcePrefix = SOURCE_DIR + File.separator + packageName.replace( ".", File.separator ) + File.separator; - k2JvmArguments.setFreeArgs( Arrays.asList( - compilationRequest.getKotlinSources() - .stream() - .map( kotlinSource -> sourcePrefix + kotlinSource ) - .collect( Collectors.joining( File.pathSeparator ) ) - ) ); + k2JvmArguments.setFreeArgs( compilationRequest.getKotlinSources() + .stream() + .map( kotlinSource -> sourcePrefix + kotlinSource ) + .collect( Collectors.toList() ) + ); k2JvmArguments.setVerbose( true ); k2JvmArguments.setSuppressWarnings( false ); k2JvmArguments.setDestination( classOutputDir ); From 3f20144b2f0529e048eee06cdcd93015686544c8 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 29 Jan 2026 15:54:38 +0100 Subject: [PATCH 514/597] Add test case for Kotlin property named default (#3980) --- .../ap/test/kotlin/data/DefaultProperty.kt | 16 ++++++ .../kotlin/data/DefaultPropertyMapper.java | 22 +++++++++ .../ap/test/kotlin/data/KotlinDataTest.java | 49 +++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/DefaultProperty.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/data/DefaultPropertyMapper.java diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/DefaultProperty.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/DefaultProperty.kt new file mode 100644 index 0000000000..f5ff27d3d3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/DefaultProperty.kt @@ -0,0 +1,16 @@ +/* + * 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.kotlin.data + +/** + * @author Filip Hrisafov + */ +data class DefaultPropertySource(val default: Boolean, val identifier: String?) + +class DefaultPropertyTarget( + var default: Boolean, + var identifier: String? +) diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/DefaultPropertyMapper.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/DefaultPropertyMapper.java new file mode 100644 index 0000000000..ce08e26e7e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/DefaultPropertyMapper.java @@ -0,0 +1,22 @@ +/* + * 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.kotlin.data; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface DefaultPropertyMapper { + + DefaultPropertyMapper INSTANCE = Mappers.getMapper( DefaultPropertyMapper.class ); + + DefaultPropertyTarget map(DefaultPropertySource source); + + DefaultPropertySource map(DefaultPropertyTarget target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java index f55cb42535..94d0d90d75 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/data/KotlinDataTest.java @@ -469,4 +469,53 @@ void shouldCompileWithoutKotlinJvmMetadata() { assertThat( source.getAge() ).isEqualTo( 20 ); } } + + @Nested + @WithClasses({ + DefaultPropertyMapper.class, + }) + @WithKotlinSources("DefaultProperty.kt") + class Default { + + @ProcessorTest + @WithKotlin + void shouldCompileWithoutWarnings() { + + DefaultPropertyTarget target = DefaultPropertyMapper.INSTANCE.map( new DefaultPropertySource( + true, + "test" + ) ); + assertThat( target ).isNotNull(); + assertThat( target.getDefault() ).isTrue(); + assertThat( target.getIdentifier() ).isEqualTo( "test" ); + + DefaultPropertySource source = DefaultPropertyMapper.INSTANCE.map( new DefaultPropertyTarget( + false, + "private" + ) ); + assertThat( source ).isNotNull(); + assertThat( source.getDefault() ).isFalse(); + assertThat( source.getIdentifier() ).isEqualTo( "private" ); + + } + + @ProcessorTest + void shouldCompileWithoutKotlin() { + DefaultPropertyTarget target = DefaultPropertyMapper.INSTANCE.map( new DefaultPropertySource( + true, + "test" + ) ); + assertThat( target ).isNotNull(); + assertThat( target.getDefault() ).isTrue(); + assertThat( target.getIdentifier() ).isEqualTo( "test" ); + + DefaultPropertySource source = DefaultPropertyMapper.INSTANCE.map( new DefaultPropertyTarget( + false, + "private" + ) ); + assertThat( source ).isNotNull(); + assertThat( source.getDefault() ).isFalse(); + assertThat( source.getIdentifier() ).isEqualTo( "private" ); + } + } } From ec1e5f9317d399b3af420191bc5d6d025726073d Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 29 Jan 2026 16:07:57 +0100 Subject: [PATCH 515/597] Add tests for sealed classes in processor module --- ...eatureCompilationExclusionCliEnhancer.java | 6 ++ .../subclassmapping/jdk17/sealed/Motor.java | 42 ++++++++++ .../jdk17/sealed/MotorDto.java | 42 ++++++++++ .../jdk17/sealed/SealedSubclassMapper.java | 31 +++++++ .../jdk17/sealed/SealedSubclassTest.java | 80 +++++++++++++++++++ .../subclassmapping/jdk17/sealed/Vehicle.java | 51 ++++++++++++ .../jdk17/sealed/VehicleCollection.java | 17 ++++ .../jdk17/sealed/VehicleCollectionDto.java | 17 ++++ .../jdk17/sealed/VehicleDto.java | 51 ++++++++++++ 9 files changed, 337 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/Motor.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/MotorDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/SealedSubclassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/SealedSubclassTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/Vehicle.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleCollection.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleCollectionDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleDto.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java index 0cc1a87818..7d397c9499 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -35,6 +35,8 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/annotatewith/deprecated/jdk11/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/**/*.java" ); if ( processorType == ProcessorTest.ProcessorType.ECLIPSE_JDT ) { additionalExcludes.add( "org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java" ); @@ -43,10 +45,14 @@ public Collection getAdditionalCommandLineArguments(ProcessorTest.Proces case JAVA_9: // TODO find out why this fails: additionalExcludes.add( "org/mapstruct/ap/test/collection/wildcard/BeanMapper.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" ); break; case JAVA_11: additionalExcludes.add( "org/mapstruct/ap/test/**/spring/**/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/*.java" ); + additionalExcludes.add( "org/mapstruct/ap/test/**/jdk17/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/**/jdk21/*.java" ); break; case JAVA_17: diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/Motor.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/Motor.java new file mode 100644 index 0000000000..3498bb09e0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/Motor.java @@ -0,0 +1,42 @@ +/* + * 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.subclassmapping.jdk17.sealed; + +public abstract sealed class Motor extends Vehicle { + private int cc; + + public int getCc() { + return cc; + } + + public void setCc(int cc) { + this.cc = cc; + } + + public static final class Davidson extends Motor { + private int numberOfExhausts; + + public int getNumberOfExhausts() { + return numberOfExhausts; + } + + public void setNumberOfExhausts(int numberOfExhausts) { + this.numberOfExhausts = numberOfExhausts; + } + } + + public static final class Harley extends Motor { + private int engineDb; + + public int getEngineDb() { + return engineDb; + } + + public void setEngineDb(int engineDb) { + this.engineDb = engineDb; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/MotorDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/MotorDto.java new file mode 100644 index 0000000000..a7ec0be4a4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/MotorDto.java @@ -0,0 +1,42 @@ +/* + * 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.subclassmapping.jdk17.sealed; + +public abstract sealed class MotorDto extends VehicleDto { + private int cc; + + public int getCc() { + return cc; + } + + public void setCc(int cc) { + this.cc = cc; + } + + public static final class DavidsonDto extends MotorDto { + private int numberOfExhausts; + + public int getNumberOfExhausts() { + return numberOfExhausts; + } + + public void setNumberOfExhausts(int numberOfExhausts) { + this.numberOfExhausts = numberOfExhausts; + } + } + + public static final class HarleyDto extends MotorDto { + private int engineDb; + + public int getEngineDb() { + return engineDb; + } + + public void setEngineDb(int engineDb) { + this.engineDb = engineDb; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/SealedSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/SealedSubclassMapper.java new file mode 100644 index 0000000000..806eec78a4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/SealedSubclassMapper.java @@ -0,0 +1,31 @@ +/* + * 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.subclassmapping.jdk17.sealed; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SealedSubclassMapper { + SealedSubclassMapper INSTANCE = Mappers.getMapper( SealedSubclassMapper.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + @SubclassMapping( source = Vehicle.Car.class, target = VehicleDto.CarDto.class ) + @SubclassMapping( source = Vehicle.Bike.class, target = VehicleDto.BikeDto.class ) + @SubclassMapping( source = Motor.Harley.class, target = MotorDto.HarleyDto.class ) + @SubclassMapping( source = Motor.Davidson.class, target = MotorDto.DavidsonDto.class ) + @Mapping( source = "vehicleManufacturingCompany", target = "maker") + VehicleDto map(Vehicle vehicle); + + VehicleCollection mapInverse(VehicleCollectionDto vehicles); + + @InheritInverseConfiguration + Vehicle mapInverse(VehicleDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/SealedSubclassTest.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/SealedSubclassTest.java new file mode 100644 index 0000000000..f9f59c45ec --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/SealedSubclassTest.java @@ -0,0 +1,80 @@ +/* + * 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.subclassmapping.jdk17.sealed; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.Compiler; + +import static org.assertj.core.api.Assertions.assertThat; + +@WithClasses({ + Motor.class, + MotorDto.class, + SealedSubclassMapper.class, + Vehicle.class, + VehicleCollection.class, + VehicleCollectionDto.class, + VehicleDto.class +}) +public class SealedSubclassTest { + + @ProcessorTest(Compiler.JDK) + public void mappingIsDoneUsingSubclassMapping() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Vehicle.Car() ); + vehicles.getVehicles().add( new Vehicle.Bike() ); + vehicles.getVehicles().add( new Motor.Harley() ); + vehicles.getVehicles().add( new Motor.Davidson() ); + + VehicleCollectionDto result = SealedSubclassMapper.INSTANCE.map( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( + VehicleDto.CarDto.class, + VehicleDto.BikeDto.class, + MotorDto.HarleyDto.class, + MotorDto.DavidsonDto.class + ); + } + + @ProcessorTest(Compiler.JDK) + public void inverseMappingIsDoneUsingSubclassMapping() { + VehicleCollectionDto vehicles = new VehicleCollectionDto(); + vehicles.getVehicles().add( new VehicleDto.CarDto() ); + vehicles.getVehicles().add( new VehicleDto.BikeDto() ); + vehicles.getVehicles().add( new MotorDto.HarleyDto() ); + vehicles.getVehicles().add( new MotorDto.DavidsonDto() ); + + VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( + Vehicle.Car.class, + Vehicle.Bike.class, + Motor.Harley.class, + Motor.Davidson.class + ); + } + + @ProcessorTest(Compiler.JDK) + public void subclassMappingInheritsInverseMapping() { + VehicleCollectionDto vehiclesDto = new VehicleCollectionDto(); + VehicleDto.CarDto carDto = new VehicleDto.CarDto(); + carDto.setMaker( "BenZ" ); + vehiclesDto.getVehicles().add( carDto ); + + VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehiclesDto ); + + assertThat( result.getVehicles() ) + .extracting( Vehicle::getVehicleManufacturingCompany ) + .containsExactly( "BenZ" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/Vehicle.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/Vehicle.java new file mode 100644 index 0000000000..c558ec877e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/Vehicle.java @@ -0,0 +1,51 @@ +/* + * 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.subclassmapping.jdk17.sealed; + +public abstract sealed class Vehicle permits Motor, Vehicle.Bike, Vehicle.Car { + private String name; + private String vehicleManufacturingCompany; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVehicleManufacturingCompany() { + return vehicleManufacturingCompany; + } + + public void setVehicleManufacturingCompany(String vehicleManufacturingCompany) { + this.vehicleManufacturingCompany = vehicleManufacturingCompany; + } + + public static final class Bike extends Vehicle { + private int numberOfGears; + + public int getNumberOfGears() { + return numberOfGears; + } + + public void setNumberOfGears(int numberOfGears) { + this.numberOfGears = numberOfGears; + } + } + + public static final class Car extends Vehicle { + private boolean manual; + + public boolean isManual() { + return manual; + } + + public void setManual(boolean manual) { + this.manual = manual; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleCollection.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleCollection.java new file mode 100644 index 0000000000..2285789baa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleCollection.java @@ -0,0 +1,17 @@ +/* + * 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.subclassmapping.jdk17.sealed; + +import java.util.ArrayList; +import java.util.Collection; + +public class VehicleCollection { + private Collection vehicles = new ArrayList<>(); + + public Collection getVehicles() { + return vehicles; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleCollectionDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleCollectionDto.java new file mode 100644 index 0000000000..6c9dc7d382 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleCollectionDto.java @@ -0,0 +1,17 @@ +/* + * 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.subclassmapping.jdk17.sealed; + +import java.util.ArrayList; +import java.util.Collection; + +public class VehicleCollectionDto { + private Collection vehicles = new ArrayList<>(); + + public Collection getVehicles() { + return vehicles; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleDto.java b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleDto.java new file mode 100644 index 0000000000..40cbb0df60 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/subclassmapping/jdk17/sealed/VehicleDto.java @@ -0,0 +1,51 @@ +/* + * 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.subclassmapping.jdk17.sealed; + +public abstract sealed class VehicleDto permits VehicleDto.CarDto, VehicleDto.BikeDto, MotorDto { + private String name; + private String maker; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMaker() { + return maker; + } + + public void setMaker(String maker) { + this.maker = maker; + } + + public static final class BikeDto extends VehicleDto { + private int numberOfGears; + + public int getNumberOfGears() { + return numberOfGears; + } + + public void setNumberOfGears(int numberOfGears) { + this.numberOfGears = numberOfGears; + } + } + + public static final class CarDto extends VehicleDto { + private boolean manual; + + public boolean isManual() { + return manual; + } + + public void setManual(boolean manual) { + this.manual = manual; + } + } +} From 49963e34dec5cebc7a3dceda03a568d96939c2f1 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 29 Jan 2026 17:42:00 +0100 Subject: [PATCH 516/597] Upgrade to Checkstyle 13.0.0 Fix errors which are now reported. Run only on Java 21 or later for integration tests module --- integrationtest/pom.xml | 36 ++++++++++++------- parent/pom.xml | 4 +-- .../ap/internal/model/common/Assignment.java | 1 - .../ap/internal/model/source/Method.java | 1 - .../processor/ModelElementProcessor.java | 2 +- .../bugs/_3485/ErroneousIssue3485Mapper.java | 1 + .../ap/test/bugs/_3485/Issue3485Test.java | 2 +- .../java8time/SourceTargetMapper.java | 1 + .../java/MultiLineExpressionMapper.java | 1 - .../expressions/java/SourceTargetMapper.java | 1 - .../compilation/annotation/ExpectedNote.java | 2 +- .../testutil/runner/CompilingExtension.java | 8 ++--- 12 files changed, 34 insertions(+), 26 deletions(-) diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 7c91837e79..71a61fc2fe 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -107,19 +107,6 @@ - - org.apache.maven.plugins - maven-checkstyle-plugin - - - check-style - verify - - checkstyle - - - - @@ -138,5 +125,28 @@ + + jdk-21-or-newer + + [21 + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + check-style + verify + + checkstyle + + + + + + + diff --git a/parent/pom.xml b/parent/pom.xml index 8c1266ed19..f7d26dfd22 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -36,7 +36,7 @@ 3.1.0 6.2.7 1.6.0 - 8.36.1 + 13.0.0 5.10.1 2.2.0 1.12.0 @@ -376,7 +376,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.1 + 3.6.0 build-config/checkstyle.xml true diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java index 293df9516b..7ed0140d81 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java @@ -147,7 +147,6 @@ public boolean isConverted() { */ void setSourceLoopVarName(String sourceLoopVarName); - /** * Returns whether the type of assignment * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index bea4d56101..ba32b12b46 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -97,7 +97,6 @@ public interface Method { */ Parameter getTargetTypeParameter(); - /** * Returns the {@link Accessibility} of this method. * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java index d43b52953d..4a30b776b7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/ModelElementProcessor.java @@ -40,7 +40,7 @@ public interface ModelElementProcessor { * * @author Gunnar Morling */ - public interface ProcessorContext { + interface ProcessorContext { Filer getFiler(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/ErroneousIssue3485Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/ErroneousIssue3485Mapper.java index 5714e04684..dc1558907c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/ErroneousIssue3485Mapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/ErroneousIssue3485Mapper.java @@ -17,6 +17,7 @@ public interface ErroneousIssue3485Mapper { ErroneousIssue3485Mapper INSTANCE = Mappers.getMapper( ErroneousIssue3485Mapper.class ); + class Target { private final String value; diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java index 7427b63a4e..964de36ec8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3485/Issue3485Test.java @@ -24,7 +24,7 @@ public class Issue3485Test { diagnostics = { @Diagnostic(type = ErroneousIssue3485Mapper.class, kind = javax.tools.Diagnostic.Kind.ERROR, - line = 33, + line = 34, message = "Using @Mapping( target = \".\") requires a source property. Expression or " + "constant cannot be used as a source.") }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapper.java index 37fc7d6cbe..f2351f4175 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/SourceTargetMapper.java @@ -26,6 +26,7 @@ public interface SourceTargetMapper { String LOCAL_TIME_FORMAT = "HH:mm"; SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + @Mappings( { @Mapping( target = "zonedDateTime", dateFormat = DATE_TIME_FORMAT ), @Mapping( target = "localDateTime", dateFormat = LOCAL_DATE_TIME_FORMAT ), @Mapping( target = "localDate", dateFormat = LOCAL_DATE_FORMAT ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/MultiLineExpressionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/MultiLineExpressionMapper.java index 50d0b4b2cd..76286bfa3a 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/MultiLineExpressionMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/MultiLineExpressionMapper.java @@ -17,7 +17,6 @@ @Mapper(imports = TimeAndFormat.class) public interface MultiLineExpressionMapper { - MultiLineExpressionMapper INSTANCE = Mappers.getMapper( MultiLineExpressionMapper.class ); @Mappings({ diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/SourceTargetMapper.java index 0e462ab3b3..56809494a3 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/expressions/java/SourceTargetMapper.java @@ -18,7 +18,6 @@ @Mapper( imports = TimeAndFormat.class ) public interface SourceTargetMapper { - SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mappings( { diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/annotation/ExpectedNote.java b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/annotation/ExpectedNote.java index 1cb95f52c2..2070c028c7 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/compilation/annotation/ExpectedNote.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/compilation/annotation/ExpectedNote.java @@ -29,7 +29,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) - public @interface ExpectedNotes { + @interface ExpectedNotes { /** * Regexp for the note to match. diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java index 08056ca697..bcfc707d68 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java @@ -25,11 +25,11 @@ import java.util.Set; import java.util.stream.Collectors; +import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean; import com.puppycrawl.tools.checkstyle.Checker; import com.puppycrawl.tools.checkstyle.ConfigurationLoader; import com.puppycrawl.tools.checkstyle.DefaultLogger; import com.puppycrawl.tools.checkstyle.PropertiesExpander; -import com.puppycrawl.tools.checkstyle.api.AutomaticBean; import org.apache.commons.io.output.NullOutputStream; import org.jetbrains.kotlin.cli.common.ExitCode; import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments; @@ -225,10 +225,10 @@ private void assertCheckstyleRules() throws Exception { ByteArrayOutputStream errorStream = new ByteArrayOutputStream(); checker.addListener( new DefaultLogger( - NullOutputStream.NULL_OUTPUT_STREAM, - AutomaticBean.OutputStreamOptions.CLOSE, + NullOutputStream.INSTANCE, + AbstractAutomaticBean.OutputStreamOptions.CLOSE, errorStream, - AutomaticBean.OutputStreamOptions.CLOSE + AbstractAutomaticBean.OutputStreamOptions.CLOSE ) ); From 60151132145a606990224376d35be6eaff9e250f Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 29 Jan 2026 22:33:00 +0100 Subject: [PATCH 517/597] #3404 Add support for Kotlin Sealed classes --- NEXT_RELEASE_CHANGELOG.md | 1 + .../ap/internal/model/common/Type.java | 26 ++++++ .../internal/util/kotlin/KotlinMetadata.java | 5 ++ .../mapstruct/ap/test/kotlin/sealed/Dtos.kt | 36 +++++++++ .../ap/test/kotlin/sealed/Entities.kt | 36 +++++++++ .../kotlin/sealed/SealedSubclassMapper.java | 31 +++++++ .../kotlin/sealed/SealedSubclassTest.java | 80 +++++++++++++++++++ 7 files changed, 215 insertions(+) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/Dtos.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/Entities.kt create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/SealedSubclassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/SealedSubclassTest.java diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index f279193f6a..1f7026c127 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -7,6 +7,7 @@ - Proper primary constructor detection - Data classes with multiple constructors - Data classes with all default parameters + - Sealed Classes (#3404) - Subclass exhaustiveness is now checked for Kotlin sealed classes ### Enhancements diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 313ff38125..8fb10090c2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -47,6 +47,7 @@ import kotlin.metadata.Attributes; import kotlin.metadata.KmClass; import kotlin.metadata.KmConstructor; +import kotlin.metadata.Modality; import kotlin.metadata.jvm.JvmExtensionsKt; import kotlin.metadata.jvm.JvmMethodSignature; import kotlin.metadata.jvm.KotlinClassMetadata; @@ -1850,6 +1851,10 @@ public boolean isEnumSet() { * return true if this type is a java 17+ sealed class */ public boolean isSealed() { + KotlinMetadata kotlinMetadata = getKotlinMetadata(); + if ( kotlinMetadata != null ) { + return kotlinMetadata.isSealedClass(); + } return typeElement.getModifiers().stream().map( Modifier::name ).anyMatch( "SEALED"::equals ); } @@ -1858,6 +1863,10 @@ public boolean isSealed() { */ @SuppressWarnings( "unchecked" ) public List getPermittedSubclasses() { + KotlinMetadata kotlinMetadata = getKotlinMetadata(); + if ( kotlinMetadata != null ) { + return kotlinMetadata.getPermittedSubclasses(); + } if (SEALED_PERMITTED_SUBCLASSES_METHOD == null) { return emptyList(); } @@ -1882,6 +1891,11 @@ public boolean isDataClass() { return Attributes.isData( kotlinClassMetadata.getKmClass() ); } + @Override + public boolean isSealedClass() { + return Attributes.getModality( kotlinClassMetadata.getKmClass() ) == Modality.SEALED; + } + @Override public ExecutableElement determinePrimaryConstructor(List constructors) { if ( constructors.size() == 1 ) { @@ -1928,6 +1942,18 @@ public ExecutableElement determinePrimaryConstructor(List con return null; } + @Override + public List getPermittedSubclasses() { + List sealedSubclassNames = kotlinClassMetadata.getKmClass().getSealedSubclasses(); + List permittedSubclasses = new ArrayList<>( sealedSubclassNames.size() ); + for ( String sealedSubclassName : sealedSubclassNames ) { + Type subclassType = typeFactory.getType( sealedSubclassName.replace( '/', '.' ) ); + permittedSubclasses.add( subclassType.getTypeMirror() ); + } + + return permittedSubclasses; + } + private String buildJvmConstructorDescriptor(ExecutableElement constructor) { StringBuilder signature = new StringBuilder( "(" ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/kotlin/KotlinMetadata.java b/processor/src/main/java/org/mapstruct/ap/internal/util/kotlin/KotlinMetadata.java index d58ab75c79..66a6fafc02 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/kotlin/KotlinMetadata.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/kotlin/KotlinMetadata.java @@ -7,6 +7,7 @@ import java.util.List; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.TypeMirror; /** * Information about a type in case it's a Kotlin type. @@ -17,5 +18,9 @@ public interface KotlinMetadata { boolean isDataClass(); + boolean isSealedClass(); + ExecutableElement determinePrimaryConstructor(List constructors); + + List getPermittedSubclasses(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/Dtos.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/Dtos.kt new file mode 100644 index 0000000000..5a9084a685 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/Dtos.kt @@ -0,0 +1,36 @@ +/* + * 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.kotlin.sealed + +sealed class VehicleDto { + var name: String? = null + var maker: String? = null + + class BikeDto : VehicleDto() { + var numberOfGears: Int = 0 + } + + class CarDto : VehicleDto() { + var manual: Boolean = false + } + +} + +sealed class MotorDto : VehicleDto() { + var cc: Int = 0 + + class DavidsonDto : MotorDto() { + var numberOfExhausts: Int = 0 + } + + class HarleyDto : MotorDto() { + var engineDb: Int = 0 + } +} + +class VehicleCollectionDto { + var vehicles: MutableList = mutableListOf() +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/Entities.kt b/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/Entities.kt new file mode 100644 index 0000000000..b380bac231 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/Entities.kt @@ -0,0 +1,36 @@ +/* + * 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.kotlin.sealed + +sealed class Vehicle { + var name: String? = null + var vehicleManufacturingCompany: String? = null + + class Bike : Vehicle() { + var numberOfGears: Int = 0 + } + + class Car : Vehicle() { + var manual: Boolean = false + } + +} + +sealed class Motor : Vehicle() { + var cc: Int = 0 + + class Davidson : Motor() { + var numberOfExhausts: Int = 0 + } + + class Harley : Motor() { + var engineDb: Int = 0 + } +} + +class VehicleCollection { + var vehicles: MutableList = mutableListOf() +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/SealedSubclassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/SealedSubclassMapper.java new file mode 100644 index 0000000000..bde7dc24cf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/SealedSubclassMapper.java @@ -0,0 +1,31 @@ +/* + * 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.kotlin.sealed; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SealedSubclassMapper { + SealedSubclassMapper INSTANCE = Mappers.getMapper( SealedSubclassMapper.class ); + + VehicleCollectionDto map(VehicleCollection vehicles); + + @SubclassMapping( source = Vehicle.Car.class, target = VehicleDto.CarDto.class ) + @SubclassMapping( source = Vehicle.Bike.class, target = VehicleDto.BikeDto.class ) + @SubclassMapping( source = Motor.Harley.class, target = MotorDto.HarleyDto.class ) + @SubclassMapping( source = Motor.Davidson.class, target = MotorDto.DavidsonDto.class ) + @Mapping( source = "vehicleManufacturingCompany", target = "maker") + VehicleDto map(Vehicle vehicle); + + VehicleCollection mapInverse(VehicleCollectionDto vehicles); + + @InheritInverseConfiguration + Vehicle mapInverse(VehicleDto dto); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/SealedSubclassTest.java b/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/SealedSubclassTest.java new file mode 100644 index 0000000000..f7162ec3a2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/kotlin/sealed/SealedSubclassTest.java @@ -0,0 +1,80 @@ +/* + * 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.kotlin.sealed; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithKotlin; +import org.mapstruct.ap.testutil.WithKotlinSources; + +import static org.assertj.core.api.Assertions.assertThat; + +@WithClasses({ + SealedSubclassMapper.class, +}) +@WithKotlinSources({ + "Dtos.kt", + "Entities.kt" +}) +@WithKotlin +public class SealedSubclassTest { + + @ProcessorTest + public void mappingIsDoneUsingSubclassMapping() { + VehicleCollection vehicles = new VehicleCollection(); + vehicles.getVehicles().add( new Vehicle.Car() ); + vehicles.getVehicles().add( new Vehicle.Bike() ); + vehicles.getVehicles().add( new Motor.Harley() ); + vehicles.getVehicles().add( new Motor.Davidson() ); + + VehicleCollectionDto result = SealedSubclassMapper.INSTANCE.map( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( + VehicleDto.CarDto.class, + VehicleDto.BikeDto.class, + MotorDto.HarleyDto.class, + MotorDto.DavidsonDto.class + ); + } + + @ProcessorTest + public void inverseMappingIsDoneUsingSubclassMapping() { + VehicleCollectionDto vehicles = new VehicleCollectionDto(); + vehicles.getVehicles().add( new VehicleDto.CarDto() ); + vehicles.getVehicles().add( new VehicleDto.BikeDto() ); + vehicles.getVehicles().add( new MotorDto.HarleyDto() ); + vehicles.getVehicles().add( new MotorDto.DavidsonDto() ); + + VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehicles ); + + assertThat( result.getVehicles() ).doesNotContainNull(); + assertThat( result.getVehicles() ) // remove generic so that test works. + .extracting( vehicle -> (Class) vehicle.getClass() ) + .containsExactly( + Vehicle.Car.class, + Vehicle.Bike.class, + Motor.Harley.class, + Motor.Davidson.class + ); + } + + @ProcessorTest + public void subclassMappingInheritsInverseMapping() { + VehicleCollectionDto vehiclesDto = new VehicleCollectionDto(); + VehicleDto.CarDto carDto = new VehicleDto.CarDto(); + carDto.setMaker( "BenZ" ); + vehiclesDto.getVehicles().add( carDto ); + + VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehiclesDto ); + + assertThat( result.getVehicles() ) + .extracting( Vehicle::getVehicleManufacturingCompany ) + .containsExactly( "BenZ" ); + } +} From 9923f78d91a8dae76f210c5de2ac0013baa078b2 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Fri, 30 Jan 2026 12:25:56 +0100 Subject: [PATCH 518/597] #674 Add support for `Optional` (#3971) MapStruct now fully supports Optional as both source and target types: * `Optional` to `Optional` - Both source and target wrapped in `Optional` * `Optional` to Non-`Optional` - Unwrapping `Optional` values * Non-`Optional` to `Optional` - Wrapping values in `Optional` * Always assume that optionals are not `null`, i.e. we can always call `isPresent()` / `isEmpty()` on an `Optional` * Add support for conversions e.g. `Optional` -> `String`, `Optional` -> `String` etc. --------- Co-authored-by: Ken Wang --- NEXT_RELEASE_CHANGELOG.md | 5 + copyright.txt | 3 +- .../mapstruct/NullValueMappingStrategy.java | 6 +- .../NullValuePropertyMappingStrategy.java | 5 +- ...apter-10-advanced-mapping-options.asciidoc | 17 +- .../chapter-3-defining-a-mapper.asciidoc | 415 +++++++++++ .../chapter-5-data-type-conversions.asciidoc | 8 + .../ap/internal/conversion/Conversions.java | 35 + .../OptionalWrapperConversionProvider.java | 97 +++ .../conversion/TypeToOptionalConversion.java | 44 ++ .../ap/internal/model/BeanMappingMethod.java | 160 +++- .../model/FromOptionalTypeConversion.java | 113 +++ .../model/LifecycleMethodResolver.java | 33 +- .../internal/model/MappingBuilderContext.java | 11 + .../ap/internal/model/MappingMethod.java | 6 +- .../model/NestedPropertyMappingMethod.java | 63 +- .../model/PresenceCheckMethodResolver.java | 6 +- .../ap/internal/model/PropertyMapping.java | 16 +- .../model/ToOptionalTypeConversion.java | 119 +++ .../model/assignment/OptionalGetWrapper.java | 35 + .../model/beanmapping/SourceReference.java | 10 + .../model/common/ConversionContext.java | 7 + .../common/DefaultConversionContext.java | 5 + .../ap/internal/model/common/Parameter.java | 26 +- .../ap/internal/model/common/Type.java | 23 +- .../model/presence/OptionalPresenceCheck.java | 77 ++ .../ap/internal/model/source/Method.java | 9 - .../source/selector/SelectionContext.java | 21 +- .../processor/DefaultVersionInformation.java | 7 + .../processor/MapperCreationProcessor.java | 1 + .../creation/MappingResolverImpl.java | 13 +- .../internal/version/VersionInformation.java | 2 + .../ap/internal/model/BeanMappingMethod.ftl | 81 +- .../model/FromOptionalTypeConversion.ftl | 16 + .../model/ToOptionalTypeConversion.ftl | 21 + .../model/assignment/OptionalGetWrapper.ftl | 15 + .../model/assignment/UpdateWrapper.ftl | 4 +- .../ap/internal/model/macro/CommonMacros.ftl | 28 +- .../model/presence/OptionalPresenceCheck.ftl | 19 + .../OptionalEnumToIntegerConversionTest.java | 73 ++ .../_enum/OptionalEnumToIntegerMapper.java | 19 + .../_enum/OptionalEnumToIntegerSource.java | 31 + .../bignumbers/BigDecimalOptionalMapper.java | 19 + .../bignumbers/BigDecimalOptionalSource.java | 139 ++++ .../bignumbers/BigIntegerOptionalMapper.java | 19 + .../bignumbers/BigIntegerOptionalSource.java | 131 ++++ .../bignumbers/BigNumbersConversionTest.java | 150 ++++ .../Java8OptionalTimeConversionTest.java | 466 ++++++++++++ .../conversion/java8time/OptionalSource.java | 160 ++++ .../java8time/OptionalSourceTargetMapper.java | 70 ++ ...ErroneousKitchenDrawerOptionalMapper1.java | 23 + ...ErroneousKitchenDrawerOptionalMapper3.java | 23 + ...ErroneousKitchenDrawerOptionalMapper4.java | 23 + ...ErroneousKitchenDrawerOptionalMapper5.java | 23 + ...ErroneousKitchenDrawerOptionalMapper6.java | 20 + .../lossy/KitchenDrawerOptionalMapper2.java | 23 + .../lossy/KitchenDrawerOptionalMapper6.java | 23 + .../conversion/lossy/LossyConversionTest.java | 94 +++ .../OversizedKitchenDrawerOptionalDto.java | 84 +++ .../lossy/VerySpecialNumberMapper.java | 6 + .../ByteWrapperOptionalSource.java | 121 +++ .../nativetypes/DoubleOptionalSource.java | 121 +++ .../DoubleWrapperOptionalSource.java | 121 +++ .../FloatWrapperOptionalSource.java | 121 +++ .../nativetypes/IntOptionalSource.java | 121 +++ .../nativetypes/IntWrapperOptionalSource.java | 121 +++ .../nativetypes/LongOptionalSource.java | 121 +++ .../LongWrapperOptionalSource.java | 121 +++ .../NumberOptionalConversionTest.java | 357 +++++++++ .../OptionalNumberConversionMapper.java | 52 ++ .../ShortWrapperOptionalSource.java | 121 +++ .../OptionalBeforeAfterMapper.java | 76 ++ .../beforeafter/OptionalBeforeAfterTest.java | 25 + .../ap/test/optional/beforeafter/Source.java | 87 +++ .../ap/test/optional/beforeafter/Target.java | 86 +++ .../optional/builder/OptionalBuilderTest.java | 31 + .../builder/SimpleOptionalBuilderMapper.java | 67 ++ .../optional/custom/CustomOptionalMapper.java | 63 ++ .../optional/custom/CustomOptionalTest.java | 60 ++ .../OptionalDifferentTypesMapper.java | 18 + .../OptionalDifferentTypesTest.java | 204 +++++ .../test/optional/differenttypes/Source.java | 89 +++ .../test/optional/differenttypes/Target.java | 84 +++ .../optional/lifecycle/MappingContext.java | 108 +++ .../lifecycle/OptionalLifecycleTest.java | 165 +++++ .../lifecycle/OptionalToOptionalMapper.java | 27 + .../OptionalToOptionalWithBuilderMapper.java | 24 + .../lifecycle/OptionalToTypeMapper.java | 27 + .../OptionalToTypeMultiSourceMapper.java | 27 + .../ap/test/optional/lifecycle/Source.java | 21 + .../ap/test/optional/lifecycle/Target.java | 39 + .../ap/test/optional/nested/Artist.java | 81 ++ .../optional/nested/OptionalNestedMapper.java | 23 + ...ptionalNestedPresenceCheckFirstMapper.java | 52 ++ .../OptionalNestedPresenceCheckMapper.java | 44 ++ .../optional/nested/OptionalNestedTest.java | 148 ++++ .../ap/test/optional/nested/Source.java | 76 ++ .../ap/test/optional/nested/Target.java | 48 ++ .../test/optional/nested/TargetAggregate.java | 49 ++ .../OptionalNullCheckAlwaysMapper.java | 19 + .../OptionalNullCheckAlwaysTest.java | 84 +++ .../test/optional/nullcheckalways/Source.java | 40 + .../test/optional/nullcheckalways/Target.java | 40 + .../nullvalue/OptionalDefaultMapperTest.java | 2 - .../nullvalue/SimpleConstructorMapper.java | 29 - .../sametype/OptionalSameTypeMapper.java | 18 + .../sametype/OptionalSameTypeTest.java | 186 +++++ .../ap/test/optional/sametype/Source.java | 74 ++ .../ap/test/optional/sametype/Target.java | 70 ++ .../optional/simple/OptionalSimpleTest.java | 110 +++ .../simple/OptionalToOptionalMapper.java | 23 + ...ionalToOptionalNullValueDefaultMapper.java | 25 + .../optional/simple/OptionalToTypeMapper.java | 23 + .../OptionalToTypeNullValueDefaultMapper.java | 24 + .../OptionalToTypeWithMappingMapper.java | 67 ++ .../ap/test/optional/simple/Source.java | 21 + .../ap/test/optional/simple/Target.java | 22 + .../optional/simple/TypeToOptionalMapper.java | 23 + .../optional/update/OptionalUpdateMapper.java | 21 + ...alUpdateNullValuePropertyIgnoreMapper.java | 23 + ...pdateNullValuePropertyToDefaultMapper.java | 23 + .../optional/update/OptionalUpdateTest.java | 117 +++ .../ap/test/optional/update/Source.java | 37 + .../ap/test/optional/update/Target.java | 37 + .../testutil/assertions/JavaFileAssert.java | 2 +- .../OptionalSourceTargetMapperImpl.java | 701 ++++++++++++++++++ .../OptionalBeforeAfterMapperImpl.java | 113 +++ .../OptionalDifferentTypesMapperImpl.java | 89 +++ .../nested/OptionalNestedMapperImpl.java | 71 ++ ...nalNestedPresenceCheckFirstMapperImpl.java | 117 +++ ...OptionalNestedPresenceCheckMapperImpl.java | 100 +++ .../OptionalNullCheckAlwaysMapperImpl.java | 38 + .../sametype/OptionalSameTypeMapperImpl.java | 55 ++ 133 files changed, 8860 insertions(+), 132 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/OptionalWrapperConversionProvider.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/TypeToOptionalConversion.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/model/presence/OptionalPresenceCheck.java create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.ftl create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.ftl create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.ftl create mode 100644 processor/src/main/resources/org/mapstruct/ap/internal/model/presence/OptionalPresenceCheck.ftl create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigDecimalOptionalMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigDecimalOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigIntegerOptionalMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigIntegerOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8OptionalTimeConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/OptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/OptionalSourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper1.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper3.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper4.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper5.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper6.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/KitchenDrawerOptionalMapper2.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/KitchenDrawerOptionalMapper6.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/OversizedKitchenDrawerOptionalDto.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/ByteWrapperOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/DoubleOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/DoubleWrapperOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/FloatWrapperOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/IntOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/IntWrapperOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/LongOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/LongWrapperOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/NumberOptionalConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/OptionalNumberConversionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/ShortWrapperOptionalSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/OptionalBeforeAfterMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/OptionalBeforeAfterTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/builder/OptionalBuilderTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/builder/SimpleOptionalBuilderMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/custom/CustomOptionalMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/custom/CustomOptionalTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/OptionalDifferentTypesMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/OptionalDifferentTypesTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/MappingContext.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalLifecycleTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToOptionalMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToOptionalWithBuilderMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToTypeMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToTypeMultiSourceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nested/Artist.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nested/OptionalNestedMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nested/OptionalNestedPresenceCheckFirstMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nested/OptionalNestedPresenceCheckMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nested/OptionalNestedTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nested/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nested/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nested/TargetAggregate.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullcheckalways/OptionalNullCheckAlwaysMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullcheckalways/OptionalNullCheckAlwaysTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullcheckalways/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullcheckalways/Target.java delete mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/nullvalue/SimpleConstructorMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/sametype/OptionalSameTypeMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/sametype/OptionalSameTypeTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/sametype/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/sametype/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/simple/OptionalSimpleTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/simple/OptionalToOptionalMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/simple/OptionalToOptionalNullValueDefaultMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/simple/OptionalToTypeMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/simple/OptionalToTypeNullValueDefaultMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/simple/OptionalToTypeWithMappingMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/simple/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/simple/Target.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/simple/TypeToOptionalMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/update/OptionalUpdateMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/update/OptionalUpdateNullValuePropertyIgnoreMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/update/OptionalUpdateNullValuePropertyToDefaultMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/update/OptionalUpdateTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/update/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/optional/update/Target.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/OptionalSourceTargetMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/optional/beforeafter/OptionalBeforeAfterMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/optional/differenttypes/OptionalDifferentTypesMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/optional/nested/OptionalNestedMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/optional/nested/OptionalNestedPresenceCheckFirstMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/optional/nested/OptionalNestedPresenceCheckMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/optional/nullcheckalways/OptionalNullCheckAlwaysMapperImpl.java create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/optional/sametype/OptionalSameTypeMapperImpl.java diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 1f7026c127..59fed4fed3 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,6 +1,11 @@ ### Features * Support for Java 21 Sequenced Collections (#3240) +* Native support for `java.util.Optional` mapping (#674) - MapStruct now fully supports Optional as both source and target types: + - `Optional` to `Optional` - Both source and target wrapped in `Optional` + - `Optional` to Non-`Optional` - Unwrapping `Optional` values + - Non-`Optional` to `Optional` - Wrapping values in `Optional` + - `Optional` properties in beans with automatic presence checks. Note, there is no null check done for `Optional` properties. * Improved support for Kotlin. Requires use of `org.jetbrains.kotlin:kotlin-metadata-jvm`. - Data Classes (#2281, #2577, #3031) - MapStruct now properly handles: - Single field data classes diff --git a/copyright.txt b/copyright.txt index 71d47a796a..bd0034b076 100644 --- a/copyright.txt +++ b/copyright.txt @@ -38,6 +38,7 @@ Joshua Spoerri - https://github.com/spoerri Jude Niroshan - https://github.com/JudeNiroshan Justyna Kubica-Ledzion - https://github.com/JKLedzion Kemal Özcan - https://github.com/yekeoe +Ken Wang - https://github.com/ro0sterjam Kevin Grüneberg - https://github.com/kevcodez Lukas Lazar - https://github.com/LukeLaz Nikolas Charalambidis - https://github.com/Nikolas-Charalambidis @@ -70,4 +71,4 @@ Tomek Gubala - https://github.com/vgtworld Valentin Kulesh - https://github.com/unshare Vincent Alexander Beelte - https://github.com/grandmasterpixel Winter Andreas - https://github.dev/wandi34 -Xiu Hong Kooi - https://github.com/kooixh +Xiu Hong Kooi - https://github.com/kooixh \ No newline at end of file diff --git a/core/src/main/java/org/mapstruct/NullValueMappingStrategy.java b/core/src/main/java/org/mapstruct/NullValueMappingStrategy.java index 519a28bd81..4cece83031 100644 --- a/core/src/main/java/org/mapstruct/NullValueMappingStrategy.java +++ b/core/src/main/java/org/mapstruct/NullValueMappingStrategy.java @@ -13,8 +13,9 @@ public enum NullValueMappingStrategy { /** - * If {@code null} is passed to a mapping method, {@code null} will be returned. That's the default behavior if no - * alternative strategy is configured globally, for given mapper or method. + * If {@code null} is passed to a mapping method, {@code null} will be returned, unless the return type is + * {@link java.util.Optional Optional}, in which case an empty optional will be returned. + * That's the default behavior if no alternative strategy is configured globally, for given mapper or method. */ RETURN_NULL, @@ -28,6 +29,7 @@ public enum NullValueMappingStrategy { * case. *

    12. For iterable mapping methods an empty collection will be returned.
    13. *
    14. For map mapping methods an empty map will be returned.
    15. + *
    16. For optional mapping methods an empty optional will be returned.
    17. * */ RETURN_DEFAULT; diff --git a/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java index 556d4253b1..a58eed8a2a 100644 --- a/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java +++ b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java @@ -27,7 +27,9 @@ public enum NullValuePropertyMappingStrategy { /** - * If a source bean property equals {@code null} the target bean property will be set explicitly to {@code null}. + * If a source bean property equals {@code null} the target bean property will be set explicitly to {@code null} + * or {@link java.util.Optional#empty() Optional.empty()} in case the target type is an + * {@link java.util.Optional Optional}. */ SET_TO_NULL, @@ -36,6 +38,7 @@ public enum NullValuePropertyMappingStrategy { *

      * This means: *

        + *
      1. For {@code Optional} MapStruct generates an {@code Optional.empty()}
      2. *
      3. For {@code List} MapStruct generates an {@code ArrayList}
      4. *
      5. For {@code Map} a {@code HashMap}
      6. *
      7. For arrays an empty array
      8. diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index b5c116ffd3..43f8fc38a0 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -204,7 +204,8 @@ The mechanism is also present on iterable mapping and map mapping. `@IterableMap [[mapping-result-for-null-arguments]] === Controlling mapping result for 'null' arguments -MapStruct offers control over the object to create when the source argument of the mapping method equals `null`. By default `null` will be returned. +MapStruct offers control over the object to create when the source argument of the mapping method equals `null`. +By default `null` will be returned, or `Optional.empty()` if the return type is `Optional`. However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT` on `@BeanMapping`, `@IterableMapping`, `@MapMapping`, or globally on `@Mapper` or `@MapperConfig`, the mapping result can be altered to return empty *default* values. This means for: @@ -243,7 +244,7 @@ How the value of the `NullValueMappingStrategy` is applied is the same as in <> for detailed examples and behavior with Optional types. +==== + [[checking-source-property-for-null-arguments]] === Controlling checking result for 'null' properties in bean mapping diff --git a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc index 81980a35ac..96c76fc20d 100644 --- a/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc +++ b/documentation/src/main/asciidoc/chapter-3-defining-a-mapper.asciidoc @@ -660,6 +660,421 @@ public class PersonMapperImpl implements PersonMapper { ---- ==== +[[mapping-with-optional]] +=== Using Optional + +MapStruct provides comprehensive support for Java's `Optional` type, allowing you to use `Optional` as both source and target types in your mappings. +MapStruct handles all common `Optional` mapping scenarios, including conversions, nested properties, update mappings, and integration with builders and constructors. + +[[optional-basic-behavior]] +==== Basic Optional Behavior + +When using `Optional` as a source type, MapStruct performs presence checks using `Optional.isEmpty()` / `!Optional.isPresent()` instead of `null` checks. +When using `Optional` as a target type, MapStruct returns `Optional.empty()` instead of `null` when the source is absent. + +[[optional-mapping-scenarios]] +==== Optional Mapping Scenarios + +MapStruct supports three primary mapping scenarios with `Optional`: + +1. `Optional` to `Optional` - Both source and target are wrapped in `Optional` +2. `Optional` to Non-`Optional` - Source is `Optional`, target is a regular type +3. Non-`Optional` to `Optional` - Source is a regular type, target is `Optional` + +.Example mapper showing all Optional scenarios +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface ProductMapper { + + Optional map(Optional product); + + ProductDto unwrap(Optional product); + + Optional wrap(Product product); +} +---- +==== + +.Generated code for `Optional` to `Optional` +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +@Override +public Optional map(Optional product) { + if ( product.isEmpty() ) { + return Optional.empty(); + } + + Product productValue = product.get(); + + ProductDto productDto = new ProductDto(); + + productDto.setName( productValue.getName() ); + productDto.setPrice( productValue.getPrice() ); + + return Optional.of( productDto ); +} +---- +==== + +.Generated code for `Optional` to Non-`Optional` +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +@Override +public ProductDto unwrap(Optional product) { + if ( product.isEmpty() ) { + return null; + } + + Product productValue = product.get(); + + ProductDto productDto = new ProductDto(); + + productDto.setName( productValue.getName() ); + productDto.setPrice( productValue.getPrice() ); + + return productDto; +} +---- +==== + +.Generated code for Non-`Optional` to `Optional` +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +@Override +public Optional wrap(Product product) { + if ( product == null ) { + return Optional.empty(); + } + + ProductDto productDto = new ProductDto(); + + productDto.setName( product.getName() ); + productDto.setPrice( product.getPrice() ); + + return Optional.of( productDto ); +} +---- +==== + +[[optional-properties]] +==== Mapping Optional Properties + +MapStruct handles `Optional` properties within your beans seamlessly. +When mapping `Optional` properties, MapStruct generates appropriate presence checks before accessing the values. + +.Example with `Optional` properties +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public class Car { + private Optional make; + private Optional numberOfSeats; + private Person driver; + + // getters omitted for brevity +} + +public class CarDto { + private String manufacturer; + private int seatCount; + private PersonDto driver; + + // getters and setters omitted for brevity +} + +@Mapper +public interface CarMapper { + + @Mapping(target = "manufacturer", source = "make") + @Mapping(target = "seatCount", source = "numberOfSeats") + CarDto carToCarDto(Car car); +} +---- +==== + +.Code generated by MapStruct for `Optional` properties +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class CarMapperImpl implements CarMapper { + + @Override + public CarDto carToCarDto(Car car) { + if ( car == null ) { + return null; + } + + CarDto carDto = new CarDto(); + + if ( car.getMake().isPresent() ) { + carDto.setManufacturer( car.getMake().get() ); + } + if ( car.getNumberOfSeats().isPresent() ) { + carDto.setSeatCount( car.getNumberOfSeats().get() ); + } + carDto.setDriver( personToPersonDto( car.getDriver() ) ); + + return carDto; + } + + protected PersonDto personToPersonDto(Person person) { + //... + } +} +---- +==== + +[[optional-update-mappings]] +==== Update Mappings with Optional + +`Optional` works seamlessly with update mappings using `@MappingTarget`. +When the source `Optional` is present, the target object is updated with the unwrapped value. +When the source `Optional` is empty, the behavior depends on the null value property mapping strategy. + +.Update mapping with an `Optional` source +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface ProductUpdateMapper { + + void updateProduct(ProductUpdateDto dto, @MappingTarget Product product); +} + +public class ProductUpdateDto { + private Optional name; + private Optional price; + + // getters omitted +} + +public class Product { + private String name; + private BigDecimal price; + + // getters and setters omitted +} +---- +==== + +.Generated update method +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +@Override +public void updateProduct(ProductUpdateDto dto, Product product) { + if ( dto == null ) { + return; + } + + if ( dto.getName().isPresent() ) { + product.setName( dto.getName().get() ); + } + if ( dto.getPrice().isPresent() ) { + product.setPrice( dto.getPrice().get() ); + } +} +---- +==== + +[[optional-null-value-strategies]] +==== Null Value Mapping Strategies + +When working with `Optional` types, MapStruct's null value mapping strategies behave as follows: + +* For `Optional` return types: `NullValueMappingStrategy.RETURN_DEFAULT` will return `Optional.empty()` instead of `null` +* For `Optional` properties: When a source property `Optional` is empty, `NullValuePropertyMappingStrategy` determines whether to skip the property, set it to null/empty, or use a default value +* `IGNORE` strategy: When a source `Optional` is empty, the target property is not updated +* `SET_TO_NULL` strategy: When a source `Optional` is empty, the target property is set to `null` (for non-`Optional` targets) or `Optional.empty()` (for `Optional` targets) +* `SET_TO_DEFAULT` strategy: When a source `Optional` is empty, the target property is set to its default value + +.Using null value strategies with `Optional` +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) +public interface ProductMapper { + + void updateProduct(ProductUpdateDto dto, @MappingTarget Product product); +} +---- +==== + +With the `IGNORE` strategy, when Optional properties are empty, they won't overwrite existing target values. + +[[optional-builders-constructors]] +==== Optional with Builders and Constructors + +MapStruct's Optional support works seamlessly with both builder patterns and constructor-based mappings. + +.Optional with builders +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface PersonMapper { + Optional map(Optional person); +} + +public class PersonDto { + private final String name; + private final Integer age; + + private PersonDto(Builder builder) { + this.name = builder.name; + this.age = builder.age; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String name; + private Integer age; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder age(Integer age) { + this.age = age; + return this; + } + + public PersonDto build() { + return new PersonDto(this); + } + } + + // getters omitted +} +---- +==== + +.Generated code using builder +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +@Override +public Optional map(Optional person) { + if ( person.isEmpty() ) { + return Optional.empty(); + } + + Person personValue = person.get(); + + PersonDto.Builder builder = PersonDto.builder(); + + builder.name( personValue.getName() ); + builder.age( personValue.getAge() ); + + return Optional.of( builder.build() ); +} +---- +==== + +.Optional with constructors +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +public class PersonDto { + private final String name; + private final Integer age; + + public PersonDto(String name, Integer age) { + this.name = name; + this.age = age; + } + + // getters omitted +} +---- +==== + +When the target type uses constructors and has `Optional` properties, MapStruct will properly handle the `Optional` parameters. + +[[optional-complex-mappings]] +==== Complex Optional Mappings + +MapStruct can handle complex scenarios where `Optional` is combined with nested object mappings, collections, and custom mapping methods. + +.Complex nested Optional mappings +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface OrderMapper { + + OrderDto map(Order order); + + // MapStruct will automatically use this for nested mappings + CustomerDto map(Customer customer); +} + +public class Order { + private Optional customer; + private Optional
        shippingAddress; + + // getters omitted +} + +public class OrderDto { + private Optional customer; + private AddressDto shippingAddress; + + // getters and setters omitted +} +---- +==== + +When mapping nested objects that are wrapped in Optional, MapStruct will: + +1. Check if the source `Optional` is present +2. Extract the value from the `Optional` +3. Map the extracted value to the target type +4. Wrap the result in an `Optional` if the target is also Optional + +[[optional-best-practices]] +==== Best Practices and Limitations + +[NOTE] +==== +While MapStruct fully supports `Optional` for target properties (public fields or setter parameters), the Java community generally recommends using `Optional` only for return types (source properties / getters). +Consider your team's coding standards when deciding whether to use `Optional` for target properties. +==== + +[WARNING] +==== +Avoid using `null` when returning `Optional` values to prevent `NullPointerException`. +MapStruct assumes that if you're using `Optional`, the `Optional` itself is not null. +==== + [[mapping-map-to-bean]] === Mapping Map to Bean diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index ad49fe2960..9da7ff75c1 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -133,6 +133,14 @@ public interface CarMapper { * Between `java.util.Locale` and `String`. ** When converting from a `Locale`, the resulting `String` will be a well-formed IETF BCP 47 language tag representing the locale. When converting from a `String`, the locale that best represents the language tag will be returned. See https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#forLanguageTag-java.lang.String-[Locale.forLanguageTag()] and https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#toLanguageTag--[Locale.toLanguageTag()] for more information. +[NOTE] +==== +All the above conversions also work when the source or target type is wrapped in `java.util.Optional`, `java.util.OptionalDouble`, `java.util.OptionalLong` or `java.util.OptionalInt`. +For example, `Optional` can be converted to `String`, `int` can be converted to `Optional`, +or `Optional` can be converted to `Integer`. +The same conversion rules apply, with MapStruct automatically handling the wrapping and unwrapping of `Optional` values. +==== + [[mapping-object-references]] === Mapping object references diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 5090c903f3..076cd2f42e 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -333,6 +333,41 @@ private void register(String sourceTypeName, Class targetClass, ConversionPro } public ConversionProvider getConversion(Type sourceType, Type targetType) { + if ( sourceType.isOptionalType() ) { + if ( targetType.isOptionalType() ) { + // We cannot convert optional to optional + return null; + } + Type sourceBaseType = sourceType.getOptionalBaseType(); + if ( sourceBaseType.equals( targetType ) ) { + // Optional -> Type + return TypeToOptionalConversion.OPTIONAL_TO_TYPE_CONVERSION; + } + + ConversionProvider conversionProvider = getInternalConversion( sourceBaseType, targetType ); + if ( conversionProvider != null ) { + return inverse( new OptionalWrapperConversionProvider( conversionProvider ) ); + } + + } + else if ( targetType.isOptionalType() ) { + // Type -> Optional + Type targetBaseType = targetType.getOptionalBaseType(); + if ( targetBaseType.equals( sourceType )) { + return TypeToOptionalConversion.TYPE_TO_OPTIONAL_CONVERSION; + } + ConversionProvider conversionProvider = getInternalConversion( sourceType, targetBaseType ); + if ( conversionProvider != null ) { + return new OptionalWrapperConversionProvider( conversionProvider ); + } + return null; + + } + + return getInternalConversion( sourceType, targetType ); + } + + private ConversionProvider getInternalConversion(Type sourceType, Type targetType) { if ( sourceType.isEnumType() && ( targetType.equals( stringType ) || targetType.getBoxedEquivalent().equals( integerType ) ) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/OptionalWrapperConversionProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/OptionalWrapperConversionProvider.java new file mode 100644 index 0000000000..2b3e1bd960 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/OptionalWrapperConversionProvider.java @@ -0,0 +1,97 @@ +/* + * 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.internal.conversion; + +import java.util.List; + +import org.mapstruct.ap.internal.model.FromOptionalTypeConversion; +import org.mapstruct.ap.internal.model.HelperMethod; +import org.mapstruct.ap.internal.model.ToOptionalTypeConversion; +import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.FieldReference; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.model.common.TypeFactory; + +/** + * @author Filip Hrisafov + */ +public class OptionalWrapperConversionProvider implements ConversionProvider { + + private final ConversionProvider conversionProvider; + + public OptionalWrapperConversionProvider(ConversionProvider conversionProvider) { + this.conversionProvider = conversionProvider; + } + + @Override + public Assignment to(ConversionContext conversionContext) { + Assignment assignment = conversionProvider.to( new OptionalConversionContext( conversionContext ) ); + return new ToOptionalTypeConversion( conversionContext.getTargetType(), assignment ); + } + + @Override + public Assignment from(ConversionContext conversionContext) { + Assignment assignment = conversionProvider.to( new OptionalConversionContext( conversionContext ) ); + return new FromOptionalTypeConversion( conversionContext.getSourceType(), assignment ); + } + + @Override + public List getRequiredHelperMethods(ConversionContext conversionContext) { + return conversionProvider.getRequiredHelperMethods( conversionContext ); + } + + @Override + public List getRequiredHelperFields(ConversionContext conversionContext) { + return conversionProvider.getRequiredHelperFields( conversionContext ); + } + + private static class OptionalConversionContext implements ConversionContext { + + private final ConversionContext delegate; + + private OptionalConversionContext(ConversionContext delegate) { + this.delegate = delegate; + } + + @Override + public Type getTargetType() { + return resolveType( delegate.getTargetType() ); + } + + @Override + public Type getSourceType() { + return resolveType( delegate.getSourceType() ); + } + + private Type resolveType(Type type) { + if ( type.isOptionalType() ) { + return type.getOptionalBaseType(); + } + return type; + } + + @Override + public String getDateFormat() { + return delegate.getDateFormat(); + } + + @Override + public String getNumberFormat() { + return delegate.getNumberFormat(); + } + + @Override + public String getLocale() { + return delegate.getLocale(); + } + + @Override + public TypeFactory getTypeFactory() { + return delegate.getTypeFactory(); + } + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/TypeToOptionalConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/TypeToOptionalConversion.java new file mode 100644 index 0000000000..ad488d1a8f --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/TypeToOptionalConversion.java @@ -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.internal.conversion; + +import java.util.Collections; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Strings; + +import static org.mapstruct.ap.internal.conversion.ReverseConversion.inverse; + +/** + * @author Filip Hrisafov + */ +public class TypeToOptionalConversion extends SimpleConversion { + + static final TypeToOptionalConversion TYPE_TO_OPTIONAL_CONVERSION = new TypeToOptionalConversion(); + static final ConversionProvider OPTIONAL_TO_TYPE_CONVERSION = inverse( TYPE_TO_OPTIONAL_CONVERSION ); + + @Override + protected String getToExpression(ConversionContext conversionContext) { + return conversionContext.getTargetType().asRawType().createReferenceName() + ".of( )"; + } + + @Override + protected Set getToConversionImportTypes(ConversionContext conversionContext) { + return Collections.singleton( conversionContext.getTargetType().asRawType() ); + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + StringBuilder sb = new StringBuilder(".get"); + Type optionalBaseType = conversionContext.getSourceType().getOptionalBaseType(); + if ( optionalBaseType.isPrimitive() ) { + sb.append( "As" ).append( Strings.capitalize( optionalBaseType.getName() ) ); + } + return sb.append( "()" ).toString(); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 086773dbe9..c6fc7753f2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -20,6 +20,7 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; @@ -99,9 +100,12 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { private final BuilderType returnTypeBuilder; private final MethodReference finalizerMethod; private final String finalizedResultName; + private final String optionalResultName; private final List beforeMappingReferencesWithFinalizedReturnType; private final List afterMappingReferencesWithFinalizedReturnType; + private final List afterMappingReferencesWithOptionalReturnType; private final Type subclassExhaustiveException; + private final Map sourceParametersReassignments; private final MappingReferences mappingReferences; @@ -119,6 +123,7 @@ public static class Builder extends AbstractMappingMethodBuilder unprocessedSourceParameters = new HashSet<>(); private final Set existingVariableNames = new HashSet<>(); private final Map> unprocessedDefinedTargets = new LinkedHashMap<>(); + private final Map sourceParametersReassignments = new HashMap<>(); private MappingReferences mappingReferences; private List targetThisReferences; @@ -176,10 +181,18 @@ public BeanMappingMethod build() { if ( selectionParameters != null && selectionParameters.getResultType() != null ) { // This is a user-defined return type, which means we need to do some extra checks for it userDefinedReturnType = ctx.getTypeFactory().getType( selectionParameters.getResultType() ); + returnTypeImpl = userDefinedReturnType; returnTypeBuilder = ctx.getTypeFactory().builderTypeFor( userDefinedReturnType, builder ); } else { - returnTypeBuilder = ctx.getTypeFactory().builderTypeFor( method.getReturnType(), builder ); + Type methodReturnType = method.getReturnType(); + if ( methodReturnType.isOptionalType() ) { + returnTypeImpl = methodReturnType.getOptionalBaseType(); + } + else { + returnTypeImpl = methodReturnType; + } + returnTypeBuilder = ctx.getTypeFactory().builderTypeFor( returnTypeImpl, builder ); } if ( isBuilderRequired() ) { // the userDefinedReturn type can also require a builder. That buildertype is already set @@ -195,7 +208,6 @@ public BeanMappingMethod build() { } } else if ( userDefinedReturnType != null ) { - returnTypeImpl = userDefinedReturnType; initializeFactoryMethod( returnTypeImpl, selectionParameters ); if ( factoryMethod != null || canResultTypeFromBeanMappingBeConstructed( returnTypeImpl ) ) { returnTypeToConstruct = returnTypeImpl; @@ -205,7 +217,6 @@ else if ( userDefinedReturnType != null ) { } } else if ( !method.isUpdateMethod() ) { - returnTypeImpl = method.getReturnType(); initializeFactoryMethod( returnTypeImpl, selectionParameters ); if ( factoryMethod != null || allowsAbstractReturnTypeAndIsEitherAbstractOrCanBeConstructed( returnTypeImpl ) @@ -266,12 +277,25 @@ else if ( !method.isUpdateMethod() ) { for ( Parameter sourceParameter : method.getSourceParameters() ) { unprocessedSourceParameters.add( sourceParameter ); - if ( sourceParameter.getType().isPrimitive() || sourceParameter.getType().isArrayType() || - sourceParameter.getType().isMapType() ) { + Type sourceParameterType = sourceParameter.getType(); + if ( sourceParameterType.isOptionalType() ) { + String sourceParameterValueName = Strings.getSafeVariableName( + sourceParameter.getName() + "Value", + existingVariableNames + ); + existingVariableNames.add( sourceParameterValueName ); + sourceParameterType = sourceParameterType.getOptionalBaseType(); + sourceParametersReassignments.put( + sourceParameter.getName(), + new Parameter( sourceParameterValueName, sourceParameter.getName(), sourceParameterType ) + ); + } + if ( sourceParameterType.isPrimitive() || sourceParameterType.isArrayType() || + sourceParameterType.isMapType() ) { continue; } - Map readAccessors = sourceParameter.getType().getPropertyReadAccessors(); + Map readAccessors = sourceParameterType.getPropertyReadAccessors(); unprocessedSourceProperties.putAll( readAccessors ); } @@ -362,12 +386,24 @@ else if ( !method.isUpdateMethod() ) { ctx, existingVariableNames ); + + Supplier> additionalAfterMappingParameterBindingsProvider = () -> + sourceParametersReassignments.values() + .stream() + .map( parameter -> method.getSourceParameters().size() == 1 ? + ParameterBinding.fromParameter( parameter ) : + ParameterBinding.fromTypeAndName( + parameter.getType(), + parameter.getOriginalName() + ".get()" + ) ) + .collect( Collectors.toList() ); List afterMappingMethods = LifecycleMethodResolver.afterMappingMethods( method, resultTypeToMap, selectionParameters, ctx, - existingVariableNames + existingVariableNames, + Collections::emptyList ); if ( method instanceof ForgedMethod ) { @@ -409,12 +445,15 @@ else if ( !method.isUpdateMethod() ) { if ( shouldCallFinalizerMethod( returnTypeToConstruct ) ) { finalizeMethod = getFinalizerMethod(); - Type actualReturnType = method.getReturnType(); + Type finalizerReturnType = method.getReturnType(); + if ( finalizerReturnType.isOptionalType() ) { + finalizerReturnType = finalizerReturnType.getOptionalBaseType(); + } beforeMappingReferencesWithFinalizedReturnType.addAll( filterMappingTarget( LifecycleMethodResolver.beforeMappingMethods( method, - actualReturnType, + finalizerReturnType, selectionParameters, ctx, existingVariableNames @@ -424,14 +463,33 @@ else if ( !method.isUpdateMethod() ) { afterMappingReferencesWithFinalizedReturnType.addAll( LifecycleMethodResolver.afterMappingMethods( method, - actualReturnType, + finalizerReturnType, + selectionParameters, + ctx, + existingVariableNames, + additionalAfterMappingParameterBindingsProvider + ) ); + + keepMappingReferencesUsingTarget( beforeMappingReferencesWithFinalizedReturnType, finalizerReturnType ); + keepMappingReferencesUsingTarget( afterMappingReferencesWithFinalizedReturnType, finalizerReturnType ); + } + + List afterMappingReferencesWithOptionalReturnType = new ArrayList<>(); + if ( method.getReturnType().isOptionalType() ) { + afterMappingReferencesWithOptionalReturnType.addAll( LifecycleMethodResolver.afterMappingMethods( + method, + method.getReturnType(), selectionParameters, ctx, - existingVariableNames + existingVariableNames, + additionalAfterMappingParameterBindingsProvider ) ); - keepMappingReferencesUsingTarget( beforeMappingReferencesWithFinalizedReturnType, actualReturnType ); - keepMappingReferencesUsingTarget( afterMappingReferencesWithFinalizedReturnType, actualReturnType ); + keepMappingReferencesUsingTarget( + afterMappingReferencesWithOptionalReturnType, + method.getReturnType() + ); + } Map presenceChecksByParameter = new LinkedHashMap<>(); @@ -461,11 +519,13 @@ else if ( !method.isUpdateMethod() ) { afterMappingMethods, beforeMappingReferencesWithFinalizedReturnType, afterMappingReferencesWithFinalizedReturnType, + afterMappingReferencesWithOptionalReturnType, finalizeMethod, mappingReferences, subclasses, presenceChecksByParameter, - subclassExhaustiveExceptionType + subclassExhaustiveExceptionType, + sourceParametersReassignments ); } @@ -609,8 +669,17 @@ private void initializeMappingReferencesIfNeeded(Type resultTypeToMap) { * builder is not assignable to the return type (so without building). */ private boolean isBuilderRequired() { - return returnTypeBuilder != null - && ( !method.isUpdateMethod() || !method.isMappingTargetAssignableToReturnType() ); + if ( returnTypeBuilder == null ) { + return false; + } + if ( method.isUpdateMethod() ) { + // when @MappingTarget annotated parameter is the same type as the return type. + return !method.getResultType().isAssignableTo( method.getReturnType() ); + } + else { + // For non-update methods a builder is required when returnTypeBuilder is set + return true; + } } private boolean shouldCallFinalizerMethod(Type returnTypeToConstruct ) { @@ -1510,6 +1579,7 @@ else if ( mapping.getJavaExpression() != null ) { if ( sourceRef != null ) { // sourceRef == null is not considered an error here if ( sourceRef.isValid() ) { + Parameter sourceParameter = sourceRef.getParameter(); // targetProperty == null can occur: we arrived here because we want as many errors // as possible before we stop analysing @@ -1518,7 +1588,8 @@ else if ( mapping.getJavaExpression() != null ) { .sourceMethod( method ) .target( targetPropertyName, targetReadAccessor, targetWriteAccessor ) .sourcePropertyName( mapping.getSourceName() ) - .sourceReference( sourceRef ) + .sourceReference( sourceRef.withParameter( + sourceParametersReassignments.get( sourceParameter.getName() ) ) ) .selectionParameters( mapping.getSelectionParameters() ) .formattingParameters( mapping.getFormattingParameters() ) .existingVariableNames( existingVariableNames ) @@ -1530,7 +1601,6 @@ else if ( mapping.getJavaExpression() != null ) { .options( mapping ) .build(); handledTargets.add( targetPropertyName ); - Parameter sourceParameter = sourceRef.getParameter(); unprocessedSourceParameters.remove( sourceParameter ); // If the source parameter was directly mapped if ( sourceRef.getPropertyEntries().isEmpty() ) { @@ -1743,20 +1813,26 @@ private SourceReference getSourceRefByTargetName(Parameter sourceParameter, Stri SourceReference sourceRef = null; - if ( sourceParameter.getType().isPrimitive() || sourceParameter.getType().isArrayType() ) { + Type sourceParameterType = sourceParameter.getType(); + Parameter sourceParameterToUse = sourceParameter; + if ( sourceParameterType.isOptionalType() ) { + sourceParameterType = sourceParameterType.getOptionalBaseType(); + sourceParameterToUse = sourceParametersReassignments.get( sourceParameter.getName() ); + } + if ( sourceParameterType.isPrimitive() || sourceParameterType.isArrayType() ) { return sourceRef; } - ReadAccessor sourceReadAccessor = sourceParameter.getType() + ReadAccessor sourceReadAccessor = sourceParameterType .getReadAccessor( targetPropertyName, method.getSourceParameters().size() == 1 ); if ( sourceReadAccessor != null ) { // property mapping PresenceCheckAccessor sourcePresenceChecker = - sourceParameter.getType().getPresenceChecker( targetPropertyName ); + sourceParameterType.getPresenceChecker( targetPropertyName ); - DeclaredType declaredSourceType = (DeclaredType) sourceParameter.getType().getTypeMirror(); + DeclaredType declaredSourceType = (DeclaredType) sourceParameterType.getTypeMirror(); Type returnType = ctx.getTypeFactory().getReturnType( declaredSourceType, sourceReadAccessor ); - sourceRef = new SourceReference.BuilderFromProperty().sourceParameter( sourceParameter ) + sourceRef = new SourceReference.BuilderFromProperty().sourceParameter( sourceParameterToUse ) .type( returnType ) .readAccessor( sourceReadAccessor ) .presenceChecker( sourcePresenceChecker ) @@ -2033,11 +2109,14 @@ private BeanMappingMethod(Method method, List afterMappingReferences, List beforeMappingReferencesWithFinalizedReturnType, List afterMappingReferencesWithFinalizedReturnType, + List afterMappingReferencesWithOptionalReturnType, MethodReference finalizerMethod, MappingReferences mappingReferences, List subclassMappings, Map presenceChecksByParameter, - Type subclassExhaustiveException) { + Type subclassExhaustiveException, + Map sourceParametersReassignments + ) { super( method, annotations, @@ -2057,14 +2136,21 @@ private BeanMappingMethod(Method method, this.finalizedResultName = Strings.getSafeVariableName( getResultName() + "Result", existingVariableNames ); existingVariableNames.add( this.finalizedResultName ); + this.optionalResultName = + Strings.getSafeVariableName( getResultName() + "ResultOptional", existingVariableNames ); + existingVariableNames.add( this.optionalResultName ); } else { this.finalizedResultName = null; + this.optionalResultName = + Strings.getSafeVariableName( getResultName() + "Optional", existingVariableNames ); + existingVariableNames.add( this.optionalResultName ); } this.mappingReferences = mappingReferences; this.beforeMappingReferencesWithFinalizedReturnType = beforeMappingReferencesWithFinalizedReturnType; this.afterMappingReferencesWithFinalizedReturnType = afterMappingReferencesWithFinalizedReturnType; + this.afterMappingReferencesWithOptionalReturnType = afterMappingReferencesWithOptionalReturnType; // initialize constant mappings as all mappings, but take out the ones that can be contributed to a // parameter mapping. @@ -2099,6 +2185,7 @@ else if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) { } this.returnTypeToConstruct = returnTypeToConstruct; this.subclassMappings = subclassMappings; + this.sourceParametersReassignments = sourceParametersReassignments; } public Type getSubclassExhaustiveException() { @@ -2121,6 +2208,18 @@ public String getFinalizedResultName() { return finalizedResultName; } + public Type getFinalizedReturnType() { + Type returnType = getReturnType(); + if ( returnType.isOptionalType() ) { + return returnType.getOptionalBaseType(); + } + return returnType; + } + + public String getOptionalResultName() { + return optionalResultName; + } + public List getBeforeMappingReferencesWithFinalizedReturnType() { return beforeMappingReferencesWithFinalizedReturnType; } @@ -2129,6 +2228,10 @@ public List getAfterMappingReferencesWithFinal return afterMappingReferencesWithFinalizedReturnType; } + public List getAfterMappingReferencesWithOptionalReturnType() { + return afterMappingReferencesWithOptionalReturnType; + } + public List propertyMappingsByParameter(Parameter parameter) { // issues: #909 and #1244. FreeMarker has problem getting values from a map when the search key is size or value return mappingsByParameter.getOrDefault( parameter.getName(), Collections.emptyList() ); @@ -2188,6 +2291,10 @@ public Set getImportTypes() { types.addAll( reference.getImportTypes() ); } + for ( LifecycleCallbackMethodReference reference : afterMappingReferencesWithOptionalReturnType ) { + types.addAll( reference.getImportTypes() ); + } + return types; } @@ -2215,6 +2322,10 @@ public List getSourceParametersNotNeedingPresenceCheck() { .collect( Collectors.toList() ); } + public Parameter getSourceParameterReassignment(Parameter parameter) { + return sourceParametersReassignments.get( parameter.getName() ); + } + private boolean needsPresenceCheck(Parameter parameter) { if ( !presenceChecksByParameter.containsKey( parameter.getName() ) ) { return false; @@ -2277,5 +2388,4 @@ public boolean equals(Object obj) { return true; } - } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.java new file mode 100644 index 0000000000..f8c596a368 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.java @@ -0,0 +1,113 @@ +/* + * 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.internal.model; + +import java.util.List; +import java.util.Set; + +import org.mapstruct.ap.internal.model.assignment.OptionalGetWrapper; +import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Filip Hrisafov + */ +public class FromOptionalTypeConversion extends ModelElement implements Assignment { + + private final Assignment conversionAssignment; + private final Type optionalType; + + public FromOptionalTypeConversion(Type optionalType, Assignment conversionAssignment) { + this.conversionAssignment = conversionAssignment; + this.optionalType = optionalType; + } + + @Override + public Set getImportTypes() { + return conversionAssignment.getImportTypes(); + } + + @Override + public List getThrownTypes() { + return conversionAssignment.getThrownTypes(); + } + + public Assignment getAssignment() { + return conversionAssignment; + } + + @Override + public String getSourceReference() { + return conversionAssignment.getSourceReference(); + } + + @Override + public boolean isSourceReferenceParameter() { + return conversionAssignment.isSourceReferenceParameter(); + } + + @Override + public PresenceCheck getSourcePresenceCheckerReference() { + return conversionAssignment.getSourcePresenceCheckerReference(); + } + + @Override + public Type getSourceType() { + return conversionAssignment.getSourceType(); + } + + @Override + public String createUniqueVarName(String desiredName) { + return conversionAssignment.createUniqueVarName( desiredName ); + } + + @Override + public String getSourceLocalVarName() { + return conversionAssignment.getSourceLocalVarName(); + } + + @Override + public void setSourceLocalVarName(String sourceLocalVarName) { + conversionAssignment.setSourceLocalVarName( sourceLocalVarName ); + } + + @Override + public String getSourceLoopVarName() { + return conversionAssignment.getSourceLoopVarName(); + } + + @Override + public void setSourceLoopVarName(String sourceLoopVarName) { + conversionAssignment.setSourceLoopVarName( sourceLoopVarName ); + } + + @Override + public String getSourceParameterName() { + return conversionAssignment.getSourceParameterName(); + } + + @Override + public void setAssignment(Assignment assignment) { + this.conversionAssignment.setAssignment( new OptionalGetWrapper( assignment, optionalType ) ); + } + + @Override + public AssignmentType getType() { + return conversionAssignment.getType(); + } + + @Override + public boolean isCallingUpdateMethod() { + return false; + } + + @Override + public String toString() { + return conversionAssignment.toString(); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java index cfe4f9f8b7..ada9dac470 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/LifecycleMethodResolver.java @@ -6,11 +6,14 @@ package org.mapstruct.ap.internal.model; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.ParameterBinding; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; @@ -63,13 +66,16 @@ public static List afterMappingMethods(Method Type alternativeTarget, SelectionParameters selectionParameters, MappingBuilderContext ctx, - Set existingVariableNames) { + Set existingVariableNames, + Supplier> parameterBindingsProvider) { return collectLifecycleCallbackMethods( method, alternativeTarget, selectionParameters, filterAfterMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ), ctx, - existingVariableNames ); + existingVariableNames, + parameterBindingsProvider + ); } /** @@ -131,13 +137,34 @@ private static List getAllAvailableMethods(Method method, List collectLifecycleCallbackMethods( Method method, Type targetType, SelectionParameters selectionParameters, List callbackMethods, MappingBuilderContext ctx, Set existingVariableNames) { + return collectLifecycleCallbackMethods( + method, + targetType, + selectionParameters, + callbackMethods, + ctx, + existingVariableNames, + Collections::emptyList + ); + } + + private static List collectLifecycleCallbackMethods( + Method method, Type targetType, SelectionParameters selectionParameters, List callbackMethods, + MappingBuilderContext ctx, Set existingVariableNames, + Supplier> parameterBindingsProvider) { MethodSelectors selectors = new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager(), ctx.getOptions() ); List> matchingMethods = selectors.getMatchingMethods( callbackMethods, - SelectionContext.forLifecycleMethods( method, targetType, selectionParameters, ctx.getTypeFactory() ) + SelectionContext.forLifecycleMethods( + method, + targetType, + selectionParameters, + ctx.getTypeFactory(), + parameterBindingsProvider + ) ); return toLifecycleCallbackMethodRefs( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index 95c229c6e6..1ebf99ffeb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -30,6 +30,7 @@ import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Services; import org.mapstruct.ap.internal.util.TypeUtils; +import org.mapstruct.ap.internal.version.VersionInformation; import org.mapstruct.ap.spi.EnumMappingStrategy; import org.mapstruct.ap.spi.EnumTransformationStrategy; import org.mapstruct.ap.spi.MappingExclusionProvider; @@ -109,6 +110,7 @@ Assignment getTargetAssignment(Method mappingMethod, ForgedMethodHistory descrip private final ElementUtils elementUtils; private final TypeUtils typeUtils; private final FormattingMessager messager; + private final VersionInformation versionInformation; private final AccessorNamingUtils accessorNaming; private final EnumMappingStrategy enumMappingStrategy; private final Map enumTransformationStrategies; @@ -126,6 +128,7 @@ public MappingBuilderContext(TypeFactory typeFactory, ElementUtils elementUtils, TypeUtils typeUtils, FormattingMessager messager, + VersionInformation versionInformation, AccessorNamingUtils accessorNaming, EnumMappingStrategy enumMappingStrategy, Map enumTransformationStrategies, @@ -138,6 +141,7 @@ public MappingBuilderContext(TypeFactory typeFactory, this.elementUtils = elementUtils; this.typeUtils = typeUtils; this.messager = messager; + this.versionInformation = versionInformation; this.accessorNaming = accessorNaming; this.enumMappingStrategy = enumMappingStrategy; this.enumTransformationStrategies = enumTransformationStrategies; @@ -190,6 +194,10 @@ public FormattingMessager getMessager() { return messager; } + public VersionInformation getVersionInformation() { + return versionInformation; + } + public AccessorNamingUtils getAccessorNaming() { return accessorNaming; } @@ -264,6 +272,9 @@ public boolean canGenerateAutoSubMappingBetween(Type sourceType, Type targetType * @return {@code true} if the type is not excluded from the {@link MappingExclusionProvider} */ private boolean canGenerateAutoSubMappingFor(Type type) { + if ( "java.util.Optional".equals( type.getFullyQualifiedName() ) ) { + return !SUB_MAPPING_EXCLUSION_PROVIDER.isExcluded( type.getOptionalBaseType().getTypeElement() ); + } return type.getTypeElement() != null && !SUB_MAPPING_EXCLUSION_PROVIDER.isExcluded( type.getTypeElement() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java index 599ddf1d35..0d04f5b6d5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingMethod.java @@ -97,7 +97,11 @@ else if ( getResultType().isArrayType() ) { return name; } else { - String name = getSafeVariableName( getResultType().getName(), existingVarNames ); + Type resultType = getResultType(); + if ( resultType.isOptionalType() ) { + resultType = resultType.getOptionalBaseType(); + } + String name = getSafeVariableName( resultType.getName(), existingVarNames ); existingVarNames.add( name ); return name; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java index 25c9f8fc03..ed841ba5bc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java @@ -17,6 +17,7 @@ import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.presence.AnyPresenceChecksPresenceCheck; import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; +import org.mapstruct.ap.internal.model.presence.OptionalPresenceCheck; import org.mapstruct.ap.internal.model.presence.SuffixPresenceCheck; import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor; @@ -71,21 +72,58 @@ public NestedPropertyMappingMethod build() { } String previousPropertyName = sourceParameter.getName(); + Type previousPropertyType = sourceParameter.getType(); for ( int i = 0; i < propertyEntries.size(); i++ ) { PropertyEntry propertyEntry = propertyEntries.get( i ); - PresenceCheck presenceCheck = getPresenceCheck( propertyEntry, previousPropertyName ); - - if ( i > 0 ) { - // If this is not the first property entry, - // then we might need to combine the presence check with a null check of the previous property - if ( presenceCheck != null ) { - presenceCheck = new AnyPresenceChecksPresenceCheck( Arrays.asList( - new NullPresenceCheck( previousPropertyName, true ), - presenceCheck - ) ); + PresenceCheck presenceCheck; + + if ( previousPropertyType.isOptionalType() ) { + String optionalValueSafeName = Strings.getSafeVariableName( + previousPropertyName + "Value", + existingVariableNames + ); + existingVariableNames.add( optionalValueSafeName ); + + presenceCheck = getPresenceCheck( propertyEntry, optionalValueSafeName ); + + String optionalValueSource = previousPropertyName + ".get()"; + boolean doesNotNeedFollowUpProperty = false; + if ( i == propertyEntries.size() - 1 ) { + // If this is the last property, and we do not have a presence check, + // then we do not need to assign the optional value + // e.g., we need to generate .get().getXxx(); + doesNotNeedFollowUpProperty = presenceCheck == null; + if ( doesNotNeedFollowUpProperty ) { + optionalValueSource += "." + propertyEntry.getReadAccessor().getReadValueSource(); + } } - else { - presenceCheck = new NullPresenceCheck( previousPropertyName, true ); + Type optionalBaseType = previousPropertyType.getOptionalBaseType(); + safePropertyEntries.add( new SafePropertyEntry( + optionalBaseType, + optionalValueSafeName, + optionalValueSource, + new OptionalPresenceCheck( previousPropertyName, ctx.getVersionInformation(), true ) + ) ); + if ( doesNotNeedFollowUpProperty ) { + break; + } + previousPropertyName = optionalValueSafeName; + + } + else { + presenceCheck = getPresenceCheck( propertyEntry, previousPropertyName ); + if ( i > 0 ) { + // If this is not the first property entry, + // then we might need to combine the presence check with a null check of the previous property + if ( presenceCheck != null ) { + presenceCheck = new AnyPresenceChecksPresenceCheck( Arrays.asList( + new NullPresenceCheck( previousPropertyName, true ), + presenceCheck + ) ); + } + else { + presenceCheck = new NullPresenceCheck( previousPropertyName, true ); + } } } @@ -101,6 +139,7 @@ public NestedPropertyMappingMethod build() { thrownTypes.addAll( ctx.getTypeFactory().getThrownTypes( propertyEntry.getReadAccessor() ) ); previousPropertyName = safeName; + previousPropertyType = propertyEntry.getType(); } method.addThrownTypes( thrownTypes ); return new NestedPropertyMappingMethod( method, safePropertyEntries ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java index 049e4caf37..784342b8d7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java @@ -13,6 +13,7 @@ import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.PresenceCheck; import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; +import org.mapstruct.ap.internal.model.presence.OptionalPresenceCheck; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.ParameterProvidedMethods; import org.mapstruct.ap.internal.model.source.SelectionParameters; @@ -87,7 +88,10 @@ public static PresenceCheck getPresenceCheckForSourceParameter( ); if ( matchingMethods.isEmpty() ) { - if ( !sourceParameter.getType().isPrimitive() ) { + if ( sourceParameter.getType().isOptionalType() ) { + return new OptionalPresenceCheck( sourceParameter.getName(), ctx.getVersionInformation() ); + } + else if ( !sourceParameter.getType().isPrimitive() ) { return new NullPresenceCheck( sourceParameter.getName() ); } return null; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 08e8adbd1b..622ba9cef0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -38,6 +38,7 @@ import org.mapstruct.ap.internal.model.presence.AllPresenceChecksPresenceCheck; import org.mapstruct.ap.internal.model.presence.JavaExpressionPresenceCheck; import org.mapstruct.ap.internal.model.presence.NullPresenceCheck; +import org.mapstruct.ap.internal.model.presence.OptionalPresenceCheck; import org.mapstruct.ap.internal.model.presence.SuffixPresenceCheck; import org.mapstruct.ap.internal.model.source.DelegatingOptions; import org.mapstruct.ap.internal.model.source.MappingControl; @@ -290,7 +291,7 @@ else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.get return new PropertyMapping( sourcePropertyName, targetPropertyName, - rightHandSide.getSourceParameterName(), + sourceReference.getParameter().getOriginalName(), targetWriteAccessor.getSimpleName(), targetReadAccessor, targetType, @@ -703,15 +704,26 @@ private PresenceCheck getSourcePresenceCheckerRef(SourceReference sourceReferenc String variableName = sourceParam.getName() + "." + propertyEntry.getReadAccessor().getReadValueSource(); + Type variableType = propertyEntry.getType(); for (int i = 1; i < sourceReference.getPropertyEntries().size(); i++) { PropertyEntry entry = sourceReference.getPropertyEntries().get( i ); if (entry.getPresenceChecker() != null && entry.getReadAccessor() != null) { - presenceChecks.add( new NullPresenceCheck( variableName ) ); + if ( variableType.isOptionalType() ) { + presenceChecks.add( new OptionalPresenceCheck( + variableName, + ctx.getVersionInformation() + ) ); + variableName = variableName + ".get()"; + } + else { + presenceChecks.add( new NullPresenceCheck( variableName ) ); + } presenceChecks.add( new SuffixPresenceCheck( variableName, entry.getPresenceChecker().getPresenceCheckSuffix() ) ); variableName = variableName + "." + entry.getReadAccessor().getSimpleName() + "()"; + variableType = entry.getType(); } else { break; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.java new file mode 100644 index 0000000000..5925540e10 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.java @@ -0,0 +1,119 @@ +/* + * 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.internal.model; + +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.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; + +/** + * @author Filip Hrisafov + */ +public class ToOptionalTypeConversion extends ModelElement implements Assignment { + + private final Assignment conversionAssignment; + private final Type targetType; + + public ToOptionalTypeConversion(Type targetType, Assignment conversionAssignment) { + this.conversionAssignment = conversionAssignment; + this.targetType = targetType; + } + + public Type getTargetType() { + return targetType; + } + + @Override + public Set getImportTypes() { + Set importTypes = new HashSet<>( conversionAssignment.getImportTypes() ); + importTypes.add( targetType ); + return importTypes; + } + + @Override + public List getThrownTypes() { + return conversionAssignment.getThrownTypes(); + } + + public Assignment getAssignment() { + return conversionAssignment; + } + + @Override + public String getSourceReference() { + return conversionAssignment.getSourceReference(); + } + + @Override + public boolean isSourceReferenceParameter() { + return conversionAssignment.isSourceReferenceParameter(); + } + + @Override + public PresenceCheck getSourcePresenceCheckerReference() { + return conversionAssignment.getSourcePresenceCheckerReference(); + } + + @Override + public Type getSourceType() { + return conversionAssignment.getSourceType(); + } + + @Override + public String createUniqueVarName(String desiredName) { + return conversionAssignment.createUniqueVarName( desiredName ); + } + + @Override + public String getSourceLocalVarName() { + return conversionAssignment.getSourceLocalVarName(); + } + + @Override + public void setSourceLocalVarName(String sourceLocalVarName) { + conversionAssignment.setSourceLocalVarName( sourceLocalVarName ); + } + + @Override + public String getSourceLoopVarName() { + return conversionAssignment.getSourceLoopVarName(); + } + + @Override + public void setSourceLoopVarName(String sourceLoopVarName) { + conversionAssignment.setSourceLoopVarName( sourceLoopVarName ); + } + + @Override + public String getSourceParameterName() { + return conversionAssignment.getSourceParameterName(); + } + + @Override + public void setAssignment(Assignment assignment) { + conversionAssignment.setAssignment( assignment ); + } + + @Override + public AssignmentType getType() { + return conversionAssignment.getType(); + } + + @Override + public boolean isCallingUpdateMethod() { + return false; + } + + @Override + public String toString() { + return targetType.getName() + ".of( " + conversionAssignment.toString() + " )"; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.java new file mode 100644 index 0000000000..9064640bb4 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.java @@ -0,0 +1,35 @@ +/* + * 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.internal.model.assignment; + +import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Strings; + +/** + * @author Filip Hrisafov + */ +public class OptionalGetWrapper extends AssignmentWrapper { + + private final Type optionalType; + + public OptionalGetWrapper(Assignment decoratedAssignment, Type optionalType) { + super( decoratedAssignment, false ); + this.optionalType = optionalType; + } + + public Type getOptionalType() { + return optionalType; + } + + @Override + public String toString() { + if ( optionalType.getFullyQualifiedName().equals( "java.util.Optional" ) ) { + return getAssignment() + ".get()"; + } + return getAssignment() + ".getAs" + Strings.capitalize( optionalType.getOptionalBaseType().getName() ) + "()"; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java index 73ec84b6b7..c37b12e311 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/SourceReference.java @@ -325,6 +325,9 @@ private List matchWithSourceAccessorTypes(Type type, String[] ent for ( int i = 0; i < entryNames.length; i++ ) { boolean matchFound = false; Type noBoundsType = newType.withoutBounds(); + if ( noBoundsType.isOptionalType() ) { + noBoundsType = noBoundsType.getOptionalBaseType(); + } ReadAccessor readAccessor = noBoundsType.getReadAccessor( entryNames[i], i > 0 || allowedMapToBean ); if ( readAccessor != null ) { PresenceCheckAccessor presenceChecker = noBoundsType.getPresenceChecker( entryNames[i] ); @@ -465,4 +468,11 @@ public List push(TypeFactory typeFactory, FormattingMessager me return result; } + public SourceReference withParameter(Parameter parameter) { + if ( parameter == null || getParameter() == parameter ) { + return this; + } + return new SourceReference( parameter, getPropertyEntries(), isValid() ); + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java index 96d3d6fe78..d3a29e30a4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ConversionContext.java @@ -21,6 +21,13 @@ public interface ConversionContext { */ Type getTargetType(); + /** + * Returns the source type of this conversion. + * + * @return The source type of this conversion. + */ + Type getSourceType(); + /** * Returns the date format if this conversion or built-in method is from String to a date type (e.g. {@link Date}) * or vice versa. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java index 159f1663e2..4e5eed47a8 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DefaultConversionContext.java @@ -61,6 +61,11 @@ public Type getTargetType() { return targetType; } + @Override + public Type getSourceType() { + return sourceType; + } + @Override public String getNumberFormat() { return numberFormat; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java index aaab7f46ca..c5091869a6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java @@ -51,12 +51,13 @@ private Parameter(Element element, Type type, boolean varArgs) { this.varArgs = varArgs; } - private Parameter(String name, Type type, boolean mappingTarget, boolean targetType, boolean mappingContext, + private Parameter(String name, String originalName, Type type, boolean mappingTarget, boolean targetType, + boolean mappingContext, boolean sourcePropertyName, boolean targetPropertyName, boolean varArgs) { this.element = null; this.name = name; - this.originalName = name; + this.originalName = originalName; this.type = type; this.mappingTarget = mappingTarget; this.targetType = targetType; @@ -67,7 +68,11 @@ private Parameter(String name, Type type, boolean mappingTarget, boolean targetT } public Parameter(String name, Type type) { - this( name, type, false, false, false, false, false, false ); + this( name, name, type ); + } + + public Parameter(String name, String originalName, Type type) { + this( name, originalName, type, false, false, false, false, false, false ); } public Element getElement() { @@ -141,6 +146,20 @@ public boolean isSourceParameter() { !isTargetPropertyName(); } + public Parameter withName(String name) { + return new Parameter( + name, + this.name, + type, + mappingTarget, + targetType, + mappingContext, + sourcePropertyName, + targetPropertyName, + varArgs + ); + } + @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; @@ -176,6 +195,7 @@ public static Parameter forElementAndType(VariableElement element, Type paramete public static Parameter forForgedMappingTarget(Type parameterType) { return new Parameter( + "mappingTarget", "mappingTarget", parameterType, true, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 8fb10090c2..29bb3cd906 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -397,11 +397,32 @@ private boolean isType(Class type) { return type.getName().equals( getFullyQualifiedName() ); } - private boolean isOptionalType() { + public boolean isOptionalType() { return isType( Optional.class ) || isType( OptionalInt.class ) || isType( OptionalDouble.class ) || isType( OptionalLong.class ); } + public Type getOptionalBaseType() { + if ( isType( Optional.class ) ) { + return getTypeParameters().get( 0 ); + } + + if ( isType( OptionalInt.class ) ) { + return typeFactory.getType( int.class ); + } + + if ( isType( OptionalDouble.class ) ) { + return typeFactory.getType( double.class ); + } + + if ( isType( OptionalLong.class ) ) { + return typeFactory.getType( long.class ); + } + + throw new IllegalStateException( "getOptionalBaseType should only be called for Optional types." ); + + } + public boolean isTypeVar() { return (typeMirror.getKind() == TypeKind.TYPEVAR); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/OptionalPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/OptionalPresenceCheck.java new file mode 100644 index 0000000000..d727c30f24 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/OptionalPresenceCheck.java @@ -0,0 +1,77 @@ +/* + * 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.internal.model.presence; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ModelElement; +import org.mapstruct.ap.internal.model.common.PresenceCheck; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.version.VersionInformation; + +/** + * Presence checker for {@link java.util.Optional} types. + * + * @author Ken Wang + */ +public class OptionalPresenceCheck extends ModelElement implements PresenceCheck { + + private final String sourceReference; + private final VersionInformation versionInformation; + private final boolean negate; + + public OptionalPresenceCheck(String sourceReference, VersionInformation versionInformation) { + this( sourceReference, versionInformation, false ); + } + + public OptionalPresenceCheck(String sourceReference, VersionInformation versionInformation, boolean negate) { + this.sourceReference = sourceReference; + this.versionInformation = versionInformation; + this.negate = negate; + } + + public String getSourceReference() { + return sourceReference; + } + + public VersionInformation getVersionInformation() { + return versionInformation; + } + + @Override + public Set getImportTypes() { + return Collections.emptySet(); + } + + public boolean isNegate() { + return negate; + } + + @Override + public PresenceCheck negate() { + return new OptionalPresenceCheck( sourceReference, versionInformation, !negate ); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + OptionalPresenceCheck that = (OptionalPresenceCheck) o; + return Objects.equals( sourceReference, that.sourceReference ); + } + + @Override + public int hashCode() { + return Objects.hash( sourceReference ); + } + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java index ba32b12b46..d9c9ce41fc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Method.java @@ -182,15 +182,6 @@ default ConditionMethodOptions getConditionOptions() { return ConditionMethodOptions.empty(); } - /** - * - * @return true when @MappingTarget annotated parameter is the same type as the return type. The method has - * to be an update method in order for this to be true. - */ - default boolean isMappingTargetAssignableToReturnType() { - return isUpdateMethod() && getResultType().isAssignableTo( getReturnType() ); - } - /** * @return the first source type, intended for mapping methods from single source to target */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java index 84bd04d7db..79351ae868 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java @@ -106,8 +106,9 @@ public static SelectionContext forMappingMethods(Method mappingMethod, Type sour } public static SelectionContext forLifecycleMethods(Method mappingMethod, Type targetType, - SelectionParameters selectionParameters, - TypeFactory typeFactory) { + SelectionParameters selectionParameters, + TypeFactory typeFactory, + Supplier> additionalParameterBindingsProvider) { SelectionCriteria criteria = SelectionCriteria.forLifecycleMethods( selectionParameters ); return new SelectionContext( null, @@ -115,12 +116,16 @@ public static SelectionContext forLifecycleMethods(Method mappingMethod, Type ta mappingMethod, targetType, mappingMethod.getResultType(), - () -> getAvailableParameterBindingsFromMethod( - mappingMethod, - targetType, - criteria.getSourceRHS(), - typeFactory - ) + () -> { + List parameterBindings = getAvailableParameterBindingsFromMethod( + mappingMethod, + targetType, + criteria.getSourceRHS(), + typeFactory + ); + parameterBindings.addAll( additionalParameterBindingsProvider.get() ); + return parameterBindings; + } ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java index 055bfe6095..0457a66da4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultVersionInformation.java @@ -41,6 +41,7 @@ public class DefaultVersionInformation implements VersionInformation { private final String runtimeVendor; private final String compiler; private final boolean sourceVersionAtLeast9; + private final boolean sourceVersionAtLeast11; private final boolean sourceVersionAtLeast19; private final boolean eclipseJDT; private final boolean javac; @@ -54,6 +55,7 @@ public class DefaultVersionInformation implements VersionInformation { this.javac = compiler.startsWith( COMPILER_NAME_JAVAC ); // If the difference between the source version and RELEASE_6 is more that 2 than we are at least on 9 this.sourceVersionAtLeast9 = sourceVersion.compareTo( SourceVersion.RELEASE_6 ) > 2; + this.sourceVersionAtLeast11 = sourceVersion.compareTo( SourceVersion.RELEASE_6 ) > 4; this.sourceVersionAtLeast19 = sourceVersion.compareTo( SourceVersion.RELEASE_6 ) > 12; } @@ -82,6 +84,11 @@ public boolean isSourceVersionAtLeast9() { return sourceVersionAtLeast9; } + @Override + public boolean isSourceVersionAtLeast11() { + return sourceVersionAtLeast11; + } + @Override public boolean isSourceVersionAtLeast19() { return sourceVersionAtLeast19; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index ba24e868aa..03e8bf977f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -119,6 +119,7 @@ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, L elementUtils, typeUtils, messager, + versionInformation, accessorNaming, context.getEnumMappingStrategy(), context.getEnumTransformationStrategies(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index ba903b6048..2a36f6b890 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -464,7 +464,7 @@ private ConversionAssignment resolveViaConversion(Type sourceType, Type targetTy Assignment conversion = conversionProvider.to( ctx ); if ( conversion != null ) { - return new ConversionAssignment( sourceType, targetType, conversionProvider.to( ctx ) ); + return new ConversionAssignment( sourceType, targetType, conversion ); } return null; } @@ -673,7 +673,16 @@ Assignment getAssignment() { void reportMessageWhenNarrowing(FormattingMessager messager, ResolvingAttempt attempt) { - if ( NativeTypes.isNarrowing( sourceType.getFullyQualifiedName(), targetType.getFullyQualifiedName() ) ) { + Type source = sourceType; + if ( sourceType.isOptionalType() ) { + source = sourceType.getOptionalBaseType(); + } + + Type target = targetType; + if ( targetType.isOptionalType() ) { + target = targetType.getOptionalBaseType(); + } + if ( NativeTypes.isNarrowing( source.getFullyQualifiedName(), target.getFullyQualifiedName() ) ) { ReportingPolicyGem policy = attempt.mappingMethod.getOptions().getMapper().typeConversionPolicy(); if ( policy == ReportingPolicyGem.WARN ) { report( messager, attempt, Message.CONVERSION_LOSSY_WARNING ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java b/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java index 5e1972fcae..411e11bf9e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/version/VersionInformation.java @@ -21,6 +21,8 @@ public interface VersionInformation { boolean isSourceVersionAtLeast9(); + boolean isSourceVersionAtLeast11(); + boolean isSourceVersionAtLeast19(); boolean isEclipseJDTCompiler(); diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl index 059e2d77d4..4ad6ae20db 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/BeanMappingMethod.ftl @@ -21,14 +21,22 @@ <#list beforeMappingReferencesWithFinalizedReturnType as callback> - <@includeModel object=callback targetBeanName=finalizedResultName targetType=returnType/> + <@includeModel object=callback targetBeanName=finalizedResultName targetType=finalizedReturnType/> <#if !callback_has_next> <#if !mapNullToDefault && !sourcePresenceChecks.empty> if ( <#list sourcePresenceChecks as sourcePresenceCheck><@includeModel object=sourcePresenceCheck.negate() /><#if sourcePresenceCheck_has_next> && ) { - return<#if returnType.name != "void"> <#if existingInstanceMapping>${resultName}<#if finalizerMethod??>.<@includeModel object=finalizerMethod /><#else>null; + <#if returnType.name == "void"> + return; + <#else> + <#if existingInstanceMapping> + <@createReturn applyOptionalAfterMapping=false>${resultName}<#if finalizerMethod??>.<@includeModel object=finalizerMethod /> + <#else> + return ${returnType.null}; + + } @@ -52,6 +60,11 @@ <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; if ( <@includeModel object=getPresenceCheckByParameter(sourceParam) /> ) { + <#assign sourceParamReassignment = getSourceParameterReassignment(sourceParam)!'' /> + <#if sourceParamReassignment?has_content> + <@includeModel object=sourceParamReassignment.type /> ${sourceParamReassignment.name} = ${sourceParam.name}.get(); + + <#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> @@ -71,6 +84,11 @@ <@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null}; <#if mapNullToDefault>if ( <@includeModel object=getPresenceCheckByParameter(sourceParameters[0]) /> ) { + <#assign sourceParamReassignment = getSourceParameterReassignment(sourceParameters[0])!'' /> + <#if sourceParamReassignment?has_content> + <@includeModel object=sourceParamReassignment.type /> ${sourceParamReassignment.name} = ${sourceParameters[0].name}.get(); + + <#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping> <@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> @@ -102,6 +120,11 @@ <#list sourceParametersNeedingPresenceCheck as sourceParam> <#if (propertyMappingsByParameter(sourceParam)?size > 0)> if ( <@includeModel object=getPresenceCheckByParameter(sourceParam) /> ) { + <#assign sourceParamReassignment = getSourceParameterReassignment(sourceParam)!'' /> + <#if sourceParamReassignment?has_content> + <@includeModel object=sourceParamReassignment.type /> ${sourceParamReassignment.name} = ${sourceParam.name}.get(); + + <#list propertyMappingsByParameter(sourceParam) as propertyMapping> <@includeModel object=propertyMapping targetBeanName=resultName existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> @@ -117,6 +140,11 @@ <#elseif !propertyMappingsByParameter(sourceParameters[0]).empty> <#if mapNullToDefault>if ( <@includeModel object=getPresenceCheckByParameter(sourceParameters[0]) /> ) { + <#assign sourceParamReassignment = getSourceParameterReassignment(sourceParameters[0])!'' /> + <#if sourceParamReassignment?has_content> + <@includeModel object=sourceParamReassignment.type /> ${sourceParamReassignment.name} = ${sourceParameters[0].name}.get(); + + <#list propertyMappingsByParameter(sourceParameters[0]) as propertyMapping> <@includeModel object=propertyMapping targetBeanName=resultName existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/> @@ -133,24 +161,24 @@ <#if returnType.name != "void"> - <#if finalizerMethod??> - <#if (afterMappingReferencesWithFinalizedReturnType?size > 0)> - <@includeModel object=returnType /> ${finalizedResultName} = ${resultName}.<@includeModel object=finalizerMethod />; + <#if finalizerMethod??> + <#if (afterMappingReferencesWithFinalizedReturnType?size > 0)> + <@includeModel object=finalizedReturnType /> ${finalizedResultName} = ${resultName}.<@includeModel object=finalizerMethod />; - <#list afterMappingReferencesWithFinalizedReturnType as callback> - <#if callback_index = 0> + <#list afterMappingReferencesWithFinalizedReturnType as callback> + <#if callback_index = 0> - - <@includeModel object=callback targetBeanName=finalizedResultName targetType=returnType/> - + + <@includeModel object=callback targetBeanName=finalizedResultName targetType=finalizedReturnType/> + - return ${finalizedResultName}; + <@createReturn>${finalizedResultName} + <#else> + <@createReturn>${resultName}.<@includeModel object=finalizerMethod /> + <#else> - return ${resultName}.<@includeModel object=finalizerMethod />; + <@createReturn>${resultName} - <#else> - return ${resultName}; - <#if hasSubclassMappings()> @@ -164,4 +192,27 @@ <#if exceptionType_has_next>, <#t> + +<#macro createReturn applyOptionalAfterMapping=true> +<#-- <@compress single_line=true>--> + <#if returnType.optionalType> + <#if (afterMappingReferencesWithOptionalReturnType?size > 0)> + <@includeModel object=returnType /> ${optionalResultName} = <@includeModel object=returnType.asRawType()/>.of( <#nested/> ); + + <#list afterMappingReferencesWithOptionalReturnType as callback> + <#if callback_index = 0> + + + <@includeModel object=callback targetBeanName=optionalResultName targetType=returnType/> + + + return ${optionalResultName}; + <#else> + return <@includeModel object=returnType.asRawType()/>.of( <#nested/> ); + + <#else> + return <#nested/>; + +<#-- --> + \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.ftl new file mode 100644 index 0000000000..50ab97e5b4 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.ftl @@ -0,0 +1,16 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.FromOptionalTypeConversion" --> +<@includeModel object=assignment + targetBeanName=ext.targetBeanName + existingInstanceMapping=ext.existingInstanceMapping + targetReadAccessorName=ext.targetReadAccessorName + targetWriteAccessorName=ext.targetWriteAccessorName + sourcePropertyName=ext.sourcePropertyName + targetPropertyName=ext.targetPropertyName + targetType=ext.targetType/> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.ftl new file mode 100644 index 0000000000..48e20e7017 --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.ftl @@ -0,0 +1,21 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.ToOptionalTypeConversion" --> +<@compress single_line=true> +<@includeModel object=targetType.asRawType() />.of( <@_assignment/> ) +<#macro _assignment> + <@includeModel object=assignment + targetBeanName=ext.targetBeanName + existingInstanceMapping=ext.existingInstanceMapping + targetReadAccessorName=ext.targetReadAccessorName + targetWriteAccessorName=ext.targetWriteAccessorName + sourcePropertyName=ext.sourcePropertyName + targetPropertyName=ext.targetPropertyName + targetType=ext.targetType/> + + diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.ftl new file mode 100644 index 0000000000..7bbc089d7c --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.ftl @@ -0,0 +1,15 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.assignment.OptionalGetWrapper" --> +<@compress single_line=true> +<#if optionalType.optionalBaseType.isPrimitive()> +${assignment}.getAs${optionalType.optionalBaseType.name?cap_first}() +<#else> +${assignment}.get() + + \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl index ea8eed2438..d8ed4b53f6 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl @@ -6,6 +6,8 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.assignment.UpdateWrapper" --> +<#-- @ftlvariable name="ext" type="java.util.Map" --> +<#-- @ftlvariable name="ext.targetType" type="org.mapstruct.ap.internal.model.common.Type" --> <#import '../macro/CommonMacros.ftl' as lib > <@lib.handleExceptions> <#if includeSourceNullCheck> @@ -16,7 +18,7 @@ } <#if setExplicitlyToDefault || setExplicitlyToNull> else { - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>null; + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>${ext.targetType.null}; } <#else> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index 278b441aa9..9c4ad26e03 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -13,6 +13,8 @@ requires: caller to implement boolean:getIncludeSourceNullCheck() --> +<#-- @ftlvariable name="ext" type="java.util.Map" --> +<#-- @ftlvariable name="ext.targetType" type="org.mapstruct.ap.internal.model.common.Type" --> <#macro handleSourceReferenceNullCheck> <#if sourcePresenceCheckerReference??> if ( <@includeModel object=sourcePresenceCheckerReference @@ -23,7 +25,7 @@ } <@elseDefaultAssignment/> <#elseif includeSourceNullCheck || ext.defaultValueAssignment??> - if ( <#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference} != null ) { + if ( <#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference}<#if sourceType.optionalType>.isPresent()<#else> != null ) { <#nested> } <@elseDefaultAssignment/> @@ -43,7 +45,7 @@ } <#elseif setExplicitlyToDefault || setExplicitlyToNull> else { - <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>null; + <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>${ext.targetType.null}; } @@ -156,7 +158,7 @@ Performs a default assignment with a default value. <#if factoryMethod??> <@includeModel object=factoryMethod targetType=ext.targetType/> <#else> - <@constructTargetObject/> + <@constructTargetObject targetType=ext.targetType/> <#-- @@ -164,15 +166,18 @@ Performs a default assignment with a default value. purpose: Either call the constructor of the target object directly or of the implementing type. --> -<#macro constructTargetObject><@compress single_line=true> - <#if ext.targetType.implementationType??> - new <@includeModel object=ext.targetType.implementationType/>() - <#elseif ext.targetType.arrayType> - new <@includeModel object=ext.targetType.componentType/>[0] - <#elseif ext.targetType.sensibleDefault??> - ${ext.targetType.sensibleDefault} +<#-- @ftlvariable name="targetType" type="org.mapstruct.ap.internal.model.common.Type" --> +<#macro constructTargetObject targetType><@compress single_line=true> + <#if targetType.implementationType??> + new <@includeModel object=targetType.implementationType/>() + <#elseif targetType.arrayType> + new <@includeModel object=targetType.componentType/>[0] + <#elseif targetType.sensibleDefault??> + ${targetType.sensibleDefault} + <#elseif targetType.optionalType> + <@includeModel object=targetType.asRawType()/>.of( <@constructTargetObject targetType=targetType.optionalBaseType/> ) <#else> - new <@includeModel object=ext.targetType/>() + new <@includeModel object=targetType/>() <#-- @@ -181,6 +186,7 @@ Performs a default assignment with a default value. purpose: assignment for source local variables. The sourceLocalVarName replaces the sourceReference in the assignmentcall. --> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.common.Assignment" --> <#macro sourceLocalVarAssignment> <#if sourceLocalVarName??> <@includeModel object=sourceType/> ${sourceLocalVarName} = ${sourceReference}; diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/OptionalPresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/OptionalPresenceCheck.ftl new file mode 100644 index 0000000000..c321ce20ec --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/presence/OptionalPresenceCheck.ftl @@ -0,0 +1,19 @@ +<#-- + + Copyright MapStruct Authors. + + Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + +--> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.presence.OptionalPresenceCheck" --> +<@compress single_line=true> + <#if isNegate()> + <#if versionInformation.isSourceVersionAtLeast11()> + ${sourceReference}.isEmpty() + <#else> + !${sourceReference}.isPresent() + + <#else> + ${sourceReference}.isPresent() + + \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerConversionTest.java new file mode 100644 index 0000000000..e1e557a897 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerConversionTest.java @@ -0,0 +1,73 @@ +/* + * 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.conversion._enum; + +import java.util.Optional; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@WithClasses({ + OptionalEnumToIntegerSource.class, + EnumToIntegerTarget.class, + OptionalEnumToIntegerMapper.class, + EnumToIntegerEnum.class +}) +public class OptionalEnumToIntegerConversionTest { + + @ProcessorTest + public void shouldApplyEnumToIntegerConversion() { + OptionalEnumToIntegerSource source = new OptionalEnumToIntegerSource(); + + for ( EnumToIntegerEnum value : EnumToIntegerEnum.values() ) { + source.setEnumValue( Optional.of( value ) ); + + EnumToIntegerTarget target = OptionalEnumToIntegerMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getEnumValue() ).isEqualTo( value.ordinal() ); + } + } + + @ProcessorTest + public void shouldApplyReverseEnumToIntegerConversion() { + EnumToIntegerTarget target = new EnumToIntegerTarget(); + + EnumToIntegerEnum[] enumValues = EnumToIntegerEnum.values(); + int numberOfValues = enumValues.length; + for ( int value = 0; value < numberOfValues; value++ ) { + target.setEnumValue( value ); + + OptionalEnumToIntegerSource source = OptionalEnumToIntegerMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getEnumValue() ).contains( enumValues[target.getEnumValue()] ); + } + } + + @ProcessorTest + public void shouldHandleOutOfBoundsEnumOrdinal() { + EnumToIntegerTarget target = new EnumToIntegerTarget(); + target.setInvalidEnumValue( EnumToIntegerEnum.values().length + 1 ); + + assertThatThrownBy( () -> OptionalEnumToIntegerMapper.INSTANCE.targetToSource( target ) ) + .isInstanceOf( ArrayIndexOutOfBoundsException.class ); + } + + @ProcessorTest + public void shouldHandleNullIntegerValue() { + EnumToIntegerTarget target = new EnumToIntegerTarget(); + + OptionalEnumToIntegerSource source = OptionalEnumToIntegerMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getEnumValue() ).isEmpty(); + assertThat( source.getInvalidEnumValue() ).isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerMapper.java new file mode 100644 index 0000000000..d07d9b879c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerMapper.java @@ -0,0 +1,19 @@ +/* + * 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.conversion._enum; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface OptionalEnumToIntegerMapper { + OptionalEnumToIntegerMapper INSTANCE = Mappers.getMapper( OptionalEnumToIntegerMapper.class ); + + EnumToIntegerTarget sourceToTarget(OptionalEnumToIntegerSource source); + + OptionalEnumToIntegerSource targetToSource(EnumToIntegerTarget target); +} + diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerSource.java new file mode 100644 index 0000000000..700c3198bc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/_enum/OptionalEnumToIntegerSource.java @@ -0,0 +1,31 @@ +/* + * 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.conversion._enum; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class OptionalEnumToIntegerSource { + private Optional enumValue = Optional.empty(); + + private Optional invalidEnumValue = Optional.empty(); + + public Optional getEnumValue() { + return enumValue; + } + + public void setEnumValue(Optional enumValue) { + this.enumValue = enumValue; + } + + public Optional getInvalidEnumValue() { + return invalidEnumValue; + } + + public void setInvalidEnumValue(Optional invalidEnumValue) { + this.invalidEnumValue = invalidEnumValue; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigDecimalOptionalMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigDecimalOptionalMapper.java new file mode 100644 index 0000000000..986de6a0ed --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigDecimalOptionalMapper.java @@ -0,0 +1,19 @@ +/* + * 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.conversion.bignumbers; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface BigDecimalOptionalMapper { + + BigDecimalOptionalMapper INSTANCE = Mappers.getMapper( BigDecimalOptionalMapper.class ); + + BigDecimalTarget sourceToTarget(BigDecimalOptionalSource source); + + BigDecimalOptionalSource targetToSource(BigDecimalTarget target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigDecimalOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigDecimalOptionalSource.java new file mode 100644 index 0000000000..bf4e53e5d9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigDecimalOptionalSource.java @@ -0,0 +1,139 @@ +/* + * 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.conversion.bignumbers; + +import java.math.BigDecimal; +import java.util.Optional; + +public class BigDecimalOptionalSource { + + private Optional b; + private Optional bb; + private Optional s; + private Optional ss; + private Optional i; + private Optional ii; + private Optional l; + private Optional ll; + private Optional f; + private Optional ff; + private Optional d; + private Optional dd; + private Optional string; + private Optional bigInteger; + + public Optional getB() { + return b; + } + + public void setB(Optional b) { + this.b = b; + } + + public Optional getBb() { + return bb; + } + + public void setBb(Optional bb) { + this.bb = bb; + } + + public Optional getS() { + return s; + } + + public void setS(Optional s) { + this.s = s; + } + + public Optional getSs() { + return ss; + } + + public void setSs(Optional ss) { + this.ss = ss; + } + + public Optional getI() { + return i; + } + + public void setI(Optional i) { + this.i = i; + } + + public Optional getIi() { + return ii; + } + + public void setIi(Optional ii) { + this.ii = ii; + } + + public Optional getL() { + return l; + } + + public void setL(Optional l) { + this.l = l; + } + + public Optional getLl() { + return ll; + } + + public void setLl(Optional ll) { + this.ll = ll; + } + + public Optional getF() { + return f; + } + + public void setF(Optional f) { + this.f = f; + } + + public Optional getFf() { + return ff; + } + + public void setFf(Optional ff) { + this.ff = ff; + } + + public Optional getD() { + return d; + } + + public void setD(Optional d) { + this.d = d; + } + + public Optional getDd() { + return dd; + } + + public void setDd(Optional dd) { + this.dd = dd; + } + + public Optional getString() { + return string; + } + + public void setString(Optional string) { + this.string = string; + } + + public Optional getBigInteger() { + return bigInteger; + } + + public void setBigInteger(Optional bigInteger) { + this.bigInteger = bigInteger; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigIntegerOptionalMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigIntegerOptionalMapper.java new file mode 100644 index 0000000000..d39b48c7e5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigIntegerOptionalMapper.java @@ -0,0 +1,19 @@ +/* + * 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.conversion.bignumbers; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface BigIntegerOptionalMapper { + + BigIntegerOptionalMapper INSTANCE = Mappers.getMapper( BigIntegerOptionalMapper.class ); + + BigIntegerTarget sourceToTarget(BigIntegerOptionalSource source); + + BigIntegerOptionalSource targetToSource(BigIntegerTarget target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigIntegerOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigIntegerOptionalSource.java new file mode 100644 index 0000000000..f4bb0f03c8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigIntegerOptionalSource.java @@ -0,0 +1,131 @@ +/* + * 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.conversion.bignumbers; + +import java.math.BigInteger; +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class BigIntegerOptionalSource { + + private Optional b; + private Optional bb; + private Optional s; + private Optional ss; + private Optional i; + private Optional ii; + private Optional l; + private Optional ll; + private Optional f; + private Optional ff; + private Optional d; + private Optional dd; + private Optional string; + + public Optional getB() { + return b; + } + + public void setB(Optional b) { + this.b = b; + } + + public Optional getBb() { + return bb; + } + + public void setBb(Optional bb) { + this.bb = bb; + } + + public Optional getS() { + return s; + } + + public void setS(Optional s) { + this.s = s; + } + + public Optional getSs() { + return ss; + } + + public void setSs(Optional ss) { + this.ss = ss; + } + + public Optional getI() { + return i; + } + + public void setI(Optional i) { + this.i = i; + } + + public Optional getIi() { + return ii; + } + + public void setIi(Optional ii) { + this.ii = ii; + } + + public Optional getL() { + return l; + } + + public void setL(Optional l) { + this.l = l; + } + + public Optional getLl() { + return ll; + } + + public void setLl(Optional ll) { + this.ll = ll; + } + + public Optional getF() { + return f; + } + + public void setF(Optional f) { + this.f = f; + } + + public Optional getFf() { + return ff; + } + + public void setFf(Optional ff) { + this.ff = ff; + } + + public Optional getD() { + return d; + } + + public void setD(Optional d) { + this.d = d; + } + + public Optional getDd() { + return dd; + } + + public void setDd(Optional dd) { + this.dd = dd; + } + + public Optional getString() { + return string; + } + + public void setString(Optional string) { + this.string = string; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigNumbersConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigNumbersConversionTest.java index 69f87f3980..2ddddec02e 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigNumbersConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/bignumbers/BigNumbersConversionTest.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Optional; import org.junit.jupiter.api.extension.RegisterExtension; import org.mapstruct.ap.testutil.IssueKey; @@ -100,6 +101,79 @@ public void shouldApplyReverseBigIntegerConversions() { assertThat( source.getString() ).isEqualTo( new BigInteger( "13" ) ); } + @ProcessorTest + @WithClasses({ BigIntegerOptionalSource.class, BigIntegerTarget.class, BigIntegerOptionalMapper.class }) + public void shouldApplyOptionalBigIntegerConversions() { + BigIntegerOptionalSource source = new BigIntegerOptionalSource(); + source.setB( Optional.of( new BigInteger( "1" ) ) ); + source.setBb( Optional.of( new BigInteger( "2" ) ) ); + source.setS( Optional.of( new BigInteger( "3" ) ) ); + source.setSs( Optional.of( new BigInteger( "4" ) ) ); + source.setI( Optional.of( new BigInteger( "5" ) ) ); + source.setIi( Optional.of( new BigInteger( "6" ) ) ); + source.setL( Optional.of( new BigInteger( "7" ) ) ); + source.setLl( Optional.of( new BigInteger( "8" ) ) ); + source.setF( Optional.of( new BigInteger( "9" ) ) ); + source.setFf( Optional.of( new BigInteger( "10" ) ) ); + source.setD( Optional.of( new BigInteger( "11" ) ) ); + source.setDd( Optional.of( new BigInteger( "12" ) ) ); + source.setString( Optional.of( new BigInteger( "13" ) ) ); + + BigIntegerTarget target = BigIntegerOptionalMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( (byte) 2 ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( (short) 4 ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( 6 ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( 8 ); + assertThat( target.getF() ).isEqualTo( 9.0f ); + assertThat( target.getFf() ).isEqualTo( 10.0f ); + assertThat( target.getD() ).isEqualTo( 11.0d ); + assertThat( target.getDd() ).isEqualTo( 12.0d ); + assertThat( target.getString() ).isEqualTo( "13" ); + } + + @ProcessorTest + @IssueKey("21") + @WithClasses({ BigIntegerOptionalSource.class, BigIntegerTarget.class, BigIntegerOptionalMapper.class }) + public void shouldApplyReverseOptionalBigIntegerConversions() { + BigIntegerTarget target = new BigIntegerTarget(); + target.setB( (byte) 1 ); + target.setBb( (byte) 2 ); + target.setS( (short) 3 ); + target.setSs( (short) 4 ); + target.setI( 5 ); + target.setIi( 6 ); + target.setL( 7 ); + target.setLl( 8L ); + target.setF( 9.0f ); + target.setFf( 10.0f ); + target.setD( 11.0d ); + target.setDd( 12.0d ); + target.setString( "13" ); + + BigIntegerOptionalSource source = BigIntegerOptionalMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getB() ).contains( new BigInteger( "1" ) ); + assertThat( source.getBb() ).contains( new BigInteger( "2" ) ); + assertThat( source.getS() ).contains( new BigInteger( "3" ) ); + assertThat( source.getSs() ).contains( new BigInteger( "4" ) ); + assertThat( source.getI() ).contains( new BigInteger( "5" ) ); + assertThat( source.getIi() ).contains( new BigInteger( "6" ) ); + assertThat( source.getL() ).contains( new BigInteger( "7" ) ); + assertThat( source.getLl() ).contains( new BigInteger( "8" ) ); + assertThat( source.getF() ).contains( new BigInteger( "9" ) ); + assertThat( source.getFf() ).contains( new BigInteger( "10" ) ); + assertThat( source.getD() ).contains( new BigInteger( "11" ) ); + assertThat( source.getDd() ).contains( new BigInteger( "12" ) ); + assertThat( source.getString() ).contains( new BigInteger( "13" ) ); + } + @ProcessorTest @IssueKey("21") @WithClasses({ BigDecimalSource.class, BigDecimalTarget.class, BigDecimalMapper.class }) @@ -178,6 +252,82 @@ public void shouldApplyReverseBigDecimalConversions() { assertThat( source.getBigInteger() ).isEqualTo( new BigDecimal( "14" ) ); } + @ProcessorTest + @WithClasses({ BigDecimalOptionalSource.class, BigDecimalTarget.class, BigDecimalOptionalMapper.class }) + public void shouldApplyOptionalBigDecimalConversions() { + BigDecimalOptionalSource source = new BigDecimalOptionalSource(); + source.setB( Optional.of( new BigDecimal( "1.45" ) ) ); + source.setBb( Optional.of( new BigDecimal( "2.45" ) ) ); + source.setS( Optional.of( new BigDecimal( "3.45" ) ) ); + source.setSs( Optional.of( new BigDecimal( "4.45" ) ) ); + source.setI( Optional.of( new BigDecimal( "5.45" ) ) ); + source.setIi( Optional.of( new BigDecimal( "6.45" ) ) ); + source.setL( Optional.of( new BigDecimal( "7.45" ) ) ); + source.setLl( Optional.of( new BigDecimal( "8.45" ) ) ); + source.setF( Optional.of( new BigDecimal( "9.45" ) ) ); + source.setFf( Optional.of( new BigDecimal( "10.45" ) ) ); + source.setD( Optional.of( new BigDecimal( "11.45" ) ) ); + source.setDd( Optional.of( new BigDecimal( "12.45" ) ) ); + source.setString( Optional.of( new BigDecimal( "13.45" ) ) ); + source.setBigInteger( Optional.of( new BigDecimal( "14.45" ) ) ); + + BigDecimalTarget target = BigDecimalOptionalMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( (byte) 2 ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( (short) 4 ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( 6 ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( 8 ); + assertThat( target.getF() ).isEqualTo( 9.45f ); + assertThat( target.getFf() ).isEqualTo( 10.45f ); + assertThat( target.getD() ).isEqualTo( 11.45d ); + assertThat( target.getDd() ).isEqualTo( 12.45d ); + assertThat( target.getString() ).isEqualTo( "13.45" ); + assertThat( target.getBigInteger() ).isEqualTo( new BigInteger( "14" ) ); + } + + @ProcessorTest + @WithClasses({ BigDecimalOptionalSource.class, BigDecimalTarget.class, BigDecimalOptionalMapper.class }) + public void shouldApplyReverseOptionalBigDecimalConversions() { + BigDecimalTarget target = new BigDecimalTarget(); + target.setB( (byte) 1 ); + target.setBb( (byte) 2 ); + target.setS( (short) 3 ); + target.setSs( (short) 4 ); + target.setI( 5 ); + target.setIi( 6 ); + target.setL( 7 ); + target.setLl( 8L ); + target.setF( 9.0f ); + target.setFf( 10.0f ); + target.setD( 11.0d ); + target.setDd( 12.0d ); + target.setString( "13.45" ); + target.setBigInteger( new BigInteger( "14" ) ); + + BigDecimalOptionalSource source = BigDecimalOptionalMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getB() ).contains( new BigDecimal( "1" ) ); + assertThat( source.getBb() ).contains( new BigDecimal( "2" ) ); + assertThat( source.getS() ).contains( new BigDecimal( "3" ) ); + assertThat( source.getSs() ).contains( new BigDecimal( "4" ) ); + assertThat( source.getI() ).contains( new BigDecimal( "5" ) ); + assertThat( source.getIi() ).contains( new BigDecimal( "6" ) ); + assertThat( source.getL() ).contains( new BigDecimal( "7" ) ); + assertThat( source.getLl() ).contains( new BigDecimal( "8" ) ); + assertThat( source.getF() ).contains( new BigDecimal( "9.0" ) ); + assertThat( source.getFf() ).contains( new BigDecimal( "10.0" ) ); + assertThat( source.getD() ).contains( new BigDecimal( "11.0" ) ); + assertThat( source.getDd() ).contains( new BigDecimal( "12.0" ) ); + assertThat( source.getString() ).contains( new BigDecimal( "13.45" ) ); + assertThat( source.getBigInteger() ).contains( new BigDecimal( "14" ) ); + } + @ProcessorTest @IssueKey("1009") @WithClasses({ BigIntegerSource.class, BigIntegerTarget.class, BigIntegerMapper.class }) diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8OptionalTimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8OptionalTimeConversionTest.java new file mode 100644 index 0000000000..1a9bb50b67 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8OptionalTimeConversionTest.java @@ -0,0 +1,466 @@ +/* + * 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.conversion.java8time; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.Date; +import java.util.Optional; +import java.util.TimeZone; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.DefaultTimeZone; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for conversions to/from Java 8 date and time types wrapped in {@link Optional}. + */ +@WithClasses({ OptionalSource.class, Target.class, OptionalSourceTargetMapper.class }) +public class Java8OptionalTimeConversionTest { + + @RegisterExtension + GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + void generatedCode() { + generatedSource.addComparisonToFixtureFor( OptionalSourceTargetMapper.class ); + } + + @ProcessorTest + public void testDateTimeToString() { + OptionalSource src = new OptionalSource(); + src.setZonedDateTime( Optional.of( ZonedDateTime.of( + LocalDateTime.of( 2014, 1, 1, 0, 0 ), + ZoneId.of( "UTC" ) + ) ) ); + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetDateTimeMapped( src ); + assertThat( target ).isNotNull(); + assertThat( target.getZonedDateTime() ).isEqualTo( "01.01.2014 00:00 UTC" ); + } + + @ProcessorTest + public void testLocalDateTimeToString() { + OptionalSource src = new OptionalSource(); + src.setLocalDateTime( Optional.of( LocalDateTime.of( 2014, 1, 1, 0, 0 ) ) ); + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetLocalDateTimeMapped( src ); + assertThat( target ).isNotNull(); + assertThat( target.getLocalDateTime() ).isEqualTo( "01.01.2014 00:00" ); + } + + @ProcessorTest + public void testLocalDateToString() { + OptionalSource src = new OptionalSource(); + src.setLocalDate( Optional.of( LocalDate.of( 2014, 1, 1 ) ) ); + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetLocalDateMapped( src ); + assertThat( target ).isNotNull(); + assertThat( target.getLocalDate() ).isEqualTo( "01.01.2014" ); + } + + @ProcessorTest + public void testLocalTimeToString() { + OptionalSource src = new OptionalSource(); + src.setLocalTime( Optional.ofNullable( LocalTime.of( 0, 0 ) ) ); + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetLocalTimeMapped( src ); + assertThat( target ).isNotNull(); + assertThat( target.getLocalTime() ).isEqualTo( "00:00" ); + } + + @ProcessorTest + public void testSourceToTargetMappingForStrings() { + OptionalSource src = new OptionalSource(); + src.setLocalTime( Optional.ofNullable( LocalTime.of( 0, 0 ) ) ); + src.setLocalDate( Optional.of( LocalDate.of( 2014, 1, 1 ) ) ); + src.setLocalDateTime( Optional.of( LocalDateTime.of( 2014, 1, 1, 0, 0 ) ) ); + src.setZonedDateTime( Optional.of( ZonedDateTime.of( + LocalDateTime.of( 2014, 1, 1, 0, 0 ), + ZoneId.of( "UTC" ) + ) ) ); + + // with given format + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTarget( src ); + + assertThat( target ).isNotNull(); + assertThat( target.getZonedDateTime() ).isEqualTo( "01.01.2014 00:00 UTC" ); + assertThat( target.getLocalDateTime() ).isEqualTo( "01.01.2014 00:00" ); + assertThat( target.getLocalDate() ).isEqualTo( "01.01.2014" ); + assertThat( target.getLocalTime() ).isEqualTo( "00:00" ); + + // and now with default mappings + target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( src ); + assertThat( target ).isNotNull(); + assertThat( target.getZonedDateTime() ).isEqualTo( "01.01.2014 00:00 UTC" ); + assertThat( target.getLocalDateTime() ).isEqualTo( "01.01.2014 00:00" ); + assertThat( target.getLocalDate() ).isEqualTo( "01.01.2014" ); + assertThat( target.getLocalTime() ).isEqualTo( "00:00" ); + } + + @ProcessorTest + public void testStringToDateTime() { + String dateTimeAsString = "01.01.2014 00:00 UTC"; + Target target = new Target(); + target.setZonedDateTime( dateTimeAsString ); + ZonedDateTime sourceDateTime = + ZonedDateTime.of( LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) ); + + OptionalSource src = OptionalSourceTargetMapper.INSTANCE.targetToSourceDateTimeMapped( target ); + assertThat( src ).isNotNull(); + assertThat( src.getZonedDateTime() ).contains( sourceDateTime ); + } + + @ProcessorTest + public void testStringToLocalDateTime() { + String dateTimeAsString = "01.01.2014 00:00"; + Target target = new Target(); + target.setLocalDateTime( dateTimeAsString ); + LocalDateTime sourceDateTime = + LocalDateTime.of( 2014, 1, 1, 0, 0, 0 ); + + OptionalSource src = OptionalSourceTargetMapper.INSTANCE.targetToSourceLocalDateTimeMapped( target ); + assertThat( src ).isNotNull(); + assertThat( src.getLocalDateTime() ).contains( sourceDateTime ); + } + + @ProcessorTest + public void testStringToLocalDate() { + String dateTimeAsString = "01.01.2014"; + Target target = new Target(); + target.setLocalDate( dateTimeAsString ); + LocalDate sourceDate = + LocalDate.of( 2014, 1, 1 ); + + OptionalSource src = OptionalSourceTargetMapper.INSTANCE.targetToSourceLocalDateMapped( target ); + assertThat( src ).isNotNull(); + assertThat( src.getLocalDate() ).contains( sourceDate ); + } + + @ProcessorTest + public void testStringToLocalTime() { + String dateTimeAsString = "00:00"; + Target target = new Target(); + target.setLocalTime( dateTimeAsString ); + LocalTime sourceTime = + LocalTime.of( 0, 0 ); + + OptionalSource src = OptionalSourceTargetMapper.INSTANCE.targetToSourceLocalTimeMapped( target ); + assertThat( src ).isNotNull(); + assertThat( src.getLocalTime() ).contains( sourceTime ); + } + + @ProcessorTest + public void testTargetToSourceNullMapping() { + Target target = new Target(); + OptionalSource src = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( src ).isNotNull(); + assertThat( src.getZonedDateTime() ).isEmpty(); + assertThat( src.getLocalDate() ).isEmpty(); + assertThat( src.getLocalDateTime() ).isEmpty(); + assertThat( src.getLocalTime() ).isEmpty(); + } + + @ProcessorTest + public void testTargetToSourceMappingForStrings() { + Target target = new Target(); + + target.setZonedDateTime( "01.01.2014 00:00 UTC" ); + target.setLocalDateTime( "01.01.2014 00:00" ); + target.setLocalDate( "01.01.2014" ); + target.setLocalTime( "00:00" ); + + OptionalSource src = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( src.getZonedDateTime() ).contains( + ZonedDateTime.of( + LocalDateTime.of( + 2014, + 1, + 1, + 0, + 0 + ), ZoneId.of( "UTC" ) + ) ); + assertThat( src.getLocalDateTime() ).contains( LocalDateTime.of( 2014, 1, 1, 0, 0 ) ); + assertThat( src.getLocalDate() ).contains( LocalDate.of( 2014, 1, 1 ) ); + assertThat( src.getLocalTime() ).contains( LocalTime.of( 0, 0 ) ); + } + + @ProcessorTest + public void testCalendarMapping() { + OptionalSource source = new OptionalSource(); + ZonedDateTime dateTime = ZonedDateTime.of( LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) ); + source.setForCalendarConversion( Optional.of( dateTime ) ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target.getForCalendarConversion() ).isNotNull(); + assertThat( target.getForCalendarConversion().getTimeZone() ).isEqualTo( + TimeZone.getTimeZone( + "UTC" ) ); + assertThat( target.getForCalendarConversion().get( Calendar.YEAR ) ).isEqualTo( dateTime.getYear() ); + assertThat( target.getForCalendarConversion().get( Calendar.MONTH ) ).isEqualTo( + dateTime.getMonthValue() - 1 ); + assertThat( target.getForCalendarConversion().get( Calendar.DATE ) ).isEqualTo( dateTime.getDayOfMonth() ); + assertThat( target.getForCalendarConversion().get( Calendar.MINUTE ) ).isEqualTo( dateTime.getMinute() ); + assertThat( target.getForCalendarConversion().get( Calendar.HOUR ) ).isEqualTo( dateTime.getHour() ); + + source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source.getForCalendarConversion() ).contains( dateTime ); + } + + @ProcessorTest + @DefaultTimeZone("UTC") + public void testZonedDateTimeToDateMapping() { + OptionalSource source = new OptionalSource(); + ZonedDateTime dateTime = ZonedDateTime.of( LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) ); + source.setForDateConversionWithZonedDateTime( Optional.of( dateTime ) ); + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( source ); + + assertThat( target.getForDateConversionWithZonedDateTime() ).isNotNull(); + + Calendar instance = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); + instance.setTimeInMillis( target.getForDateConversionWithZonedDateTime().getTime() ); + + assertThat( instance.get( Calendar.YEAR ) ).isEqualTo( dateTime.getYear() ); + assertThat( instance.get( Calendar.MONTH ) ).isEqualTo( dateTime.getMonthValue() - 1 ); + assertThat( instance.get( Calendar.DATE ) ).isEqualTo( dateTime.getDayOfMonth() ); + assertThat( instance.get( Calendar.MINUTE ) ).isEqualTo( dateTime.getMinute() ); + assertThat( instance.get( Calendar.HOUR ) ).isEqualTo( dateTime.getHour() ); + + source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source.getForDateConversionWithZonedDateTime() ).contains( dateTime ); + } + + @ProcessorTest + public void testInstantToDateMapping() { + Instant instant = Instant.ofEpochMilli( 1539366615000L ); + + OptionalSource source = new OptionalSource(); + source.setForDateConversionWithInstant( Optional.ofNullable( instant ) ); + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( source ); + Date date = target.getForDateConversionWithInstant(); + assertThat( date ).isNotNull(); + assertThat( date.getTime() ).isEqualTo( 1539366615000L ); + + source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source.getForDateConversionWithInstant() ).contains( instant ); + } + + @ProcessorTest + public void testLocalDateTimeToLocalDateMapping() { + LocalDate localDate = LocalDate.of( 2014, 1, 1 ); + + OptionalSource source = new OptionalSource(); + source.setForLocalDateTimeConversionWithLocalDate( Optional.of( localDate ) ); + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( source ); + LocalDateTime localDateTime = target.getForLocalDateTimeConversionWithLocalDate(); + assertThat( localDateTime ).isNotNull(); + assertThat( localDateTime ).isEqualTo( LocalDateTime.of( 2014, 1, 1, 0, 0 ) ); + + source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source.getForLocalDateTimeConversionWithLocalDate() ).contains( localDate ); + } + + @ProcessorTest + @DefaultTimeZone("Australia/Melbourne") + public void testLocalDateTimeToDateMapping() { + + OptionalSource source = new OptionalSource(); + LocalDateTime dateTime = LocalDateTime.of( 2014, 1, 1, 0, 0 ); + source.setForDateConversionWithLocalDateTime( Optional.of( dateTime ) ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( source ); + + assertThat( target.getForDateConversionWithLocalDateTime() ).isNotNull(); + + Calendar instance = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); + instance.setTimeInMillis( target.getForDateConversionWithLocalDateTime().getTime() ); + + assertThat( instance.get( Calendar.YEAR ) ).isEqualTo( dateTime.getYear() ); + assertThat( instance.get( Calendar.MONTH ) ).isEqualTo( dateTime.getMonthValue() - 1 ); + assertThat( instance.get( Calendar.DATE ) ).isEqualTo( dateTime.getDayOfMonth() ); + assertThat( instance.get( Calendar.MINUTE ) ).isEqualTo( dateTime.getMinute() ); + assertThat( instance.get( Calendar.HOUR ) ).isEqualTo( dateTime.getHour() ); + + source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source.getForDateConversionWithLocalDateTime() ).contains( dateTime ); + } + + @ProcessorTest + @DefaultTimeZone("Australia/Melbourne") + public void testLocalDateToDateMapping() { + + OptionalSource source = new OptionalSource(); + LocalDate localDate = LocalDate.of( 2016, 3, 1 ); + source.setForDateConversionWithLocalDate( Optional.of( localDate ) ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( source ); + + assertThat( target.getForDateConversionWithLocalDate() ).isNotNull(); + + Calendar instance = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); + instance.setTimeInMillis( target.getForDateConversionWithLocalDate().getTime() ); + + assertThat( instance.get( Calendar.YEAR ) ).isEqualTo( localDate.getYear() ); + assertThat( instance.get( Calendar.MONTH ) ).isEqualTo( localDate.getMonthValue() - 1 ); + assertThat( instance.get( Calendar.DATE ) ).isEqualTo( localDate.getDayOfMonth() ); + + source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source.getForDateConversionWithLocalDate() ).contains( localDate ); + } + + @ProcessorTest + @DefaultTimeZone("Australia/Melbourne") + public void testLocalDateToSqlDateMapping() { + + OptionalSource source = new OptionalSource(); + LocalDate localDate = LocalDate.of( 2016, 3, 1 ); + source.setForSqlDateConversionWithLocalDate( Optional.of( localDate ) ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( source ); + + assertThat( target.getForSqlDateConversionWithLocalDate() ).isNotNull(); + + Calendar instance = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); + instance.setTimeInMillis( target.getForSqlDateConversionWithLocalDate().getTime() ); + + assertThat( instance.get( Calendar.YEAR ) ).isEqualTo( localDate.getYear() ); + assertThat( instance.get( Calendar.MONTH ) ).isEqualTo( localDate.getMonthValue() - 1 ); + assertThat( instance.get( Calendar.DATE ) ).isEqualTo( localDate.getDayOfMonth() ); + + source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + + assertThat( source.getForSqlDateConversionWithLocalDate() ).contains( localDate ); + } + + @ProcessorTest + public void testInstantToStringMapping() { + OptionalSource source = new OptionalSource(); + source.setForInstantConversionWithString( Optional.ofNullable( Instant.ofEpochSecond( 42L ) ) ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTarget( source ); + String periodString = target.getForInstantConversionWithString(); + assertThat( periodString ).isEqualTo( "1970-01-01T00:00:42Z" ); + } + + @ProcessorTest + public void testInstantToStringNullMapping() { + OptionalSource source = new OptionalSource(); + source.setForInstantConversionWithString( Optional.empty() ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTarget( source ); + String periodString = target.getForInstantConversionWithString(); + assertThat( periodString ).isNull(); + } + + @ProcessorTest + public void testStringToInstantMapping() { + Target target = new Target(); + target.setForInstantConversionWithString( "1970-01-01T00:00:00.000Z" ); + + OptionalSource source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source.getForInstantConversionWithString() ).contains( Instant.EPOCH ); + } + + @ProcessorTest + public void testStringToInstantNullMapping() { + Target target = new Target(); + target.setForInstantConversionWithString( null ); + + OptionalSource source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source.getForInstantConversionWithString() ).isEmpty(); + } + + @ProcessorTest + public void testPeriodToStringMapping() { + OptionalSource source = new OptionalSource(); + source.setForPeriodConversionWithString( Optional.ofNullable( Period.ofDays( 42 ) ) ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTarget( source ); + String periodString = target.getForPeriodConversionWithString(); + assertThat( periodString ).isEqualTo( "P42D" ); + } + + @ProcessorTest + public void testPeriodToStringNullMapping() { + OptionalSource source = new OptionalSource(); + source.setForPeriodConversionWithString( Optional.empty() ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTarget( source ); + String periodString = target.getForPeriodConversionWithString(); + assertThat( periodString ).isNull(); + } + + @ProcessorTest + public void testStringToPeriodMapping() { + Target target = new Target(); + target.setForPeriodConversionWithString( "P1Y2M3D" ); + + OptionalSource source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source.getForPeriodConversionWithString() ).contains( Period.of( 1, 2, 3 ) ); + } + + @ProcessorTest + public void testStringToPeriodNullMapping() { + Target target = new Target(); + target.setForPeriodConversionWithString( null ); + + OptionalSource source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source.getForPeriodConversionWithString() ).isEmpty(); + } + + @ProcessorTest + public void testDurationToStringMapping() { + OptionalSource source = new OptionalSource(); + source.setForDurationConversionWithString( Optional.ofNullable( Duration.ofMinutes( 42L ) ) ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTarget( source ); + String durationString = target.getForDurationConversionWithString(); + assertThat( durationString ).isEqualTo( "PT42M" ); + } + + @ProcessorTest + public void testDurationToStringNullMapping() { + OptionalSource source = new OptionalSource(); + source.setForDurationConversionWithString( Optional.empty() ); + + Target target = OptionalSourceTargetMapper.INSTANCE.sourceToTarget( source ); + String durationString = target.getForDurationConversionWithString(); + assertThat( durationString ).isNull(); + } + + @ProcessorTest + public void testStringToDurationMapping() { + Target target = new Target(); + target.setForDurationConversionWithString( "PT20.345S" ); + + OptionalSource source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source.getForDurationConversionWithString() ).contains( Duration.ofSeconds( 20L, 345000000L ) ); + } + + @ProcessorTest + public void testStringToDurationNullMapping() { + Target target = new Target(); + target.setForDurationConversionWithString( null ); + + OptionalSource source = OptionalSourceTargetMapper.INSTANCE.targetToSource( target ); + assertThat( source.getForDurationConversionWithString() ).isEmpty(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/OptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/OptionalSource.java new file mode 100644 index 0000000000..cf94f31869 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/OptionalSource.java @@ -0,0 +1,160 @@ +/* + * 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.conversion.java8time; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Period; +import java.time.ZonedDateTime; +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class OptionalSource { + + private Optional zonedDateTime = Optional.empty(); + + private Optional localDateTime = Optional.empty(); + + private Optional localDate = Optional.empty(); + + private Optional localTime = Optional.empty(); + + private Optional forCalendarConversion = Optional.empty(); + + private Optional forDateConversionWithZonedDateTime = Optional.empty(); + + private Optional forDateConversionWithLocalDateTime = Optional.empty(); + + private Optional forDateConversionWithLocalDate = Optional.empty(); + + private Optional forSqlDateConversionWithLocalDate = Optional.empty(); + + private Optional forDateConversionWithInstant = Optional.empty(); + + private Optional forLocalDateTimeConversionWithLocalDate = Optional.empty(); + + private Optional forInstantConversionWithString = Optional.empty(); + + private Optional forPeriodConversionWithString = Optional.empty(); + + private Optional forDurationConversionWithString = Optional.empty(); + + public Optional getZonedDateTime() { + return zonedDateTime; + } + + public void setZonedDateTime(Optional dateTime) { + this.zonedDateTime = dateTime; + } + + public Optional getLocalDateTime() { + return localDateTime; + } + + public void setLocalDateTime(Optional localDateTime) { + this.localDateTime = localDateTime; + } + + public Optional getLocalDate() { + return localDate; + } + + public void setLocalDate(Optional localDate) { + this.localDate = localDate; + } + + public Optional getLocalTime() { + return localTime; + } + + public void setLocalTime(Optional localTime) { + this.localTime = localTime; + } + + public Optional getForCalendarConversion() { + return forCalendarConversion; + } + + public void setForCalendarConversion(Optional forCalendarConversion) { + this.forCalendarConversion = forCalendarConversion; + } + + public Optional getForDateConversionWithZonedDateTime() { + return forDateConversionWithZonedDateTime; + } + + public void setForDateConversionWithZonedDateTime(Optional forDateConversionWithZonedDateTime) { + this.forDateConversionWithZonedDateTime = forDateConversionWithZonedDateTime; + } + + public Optional getForDateConversionWithLocalDateTime() { + return forDateConversionWithLocalDateTime; + } + + public void setForDateConversionWithLocalDateTime(Optional forDateConversionWithLocalDateTime) { + this.forDateConversionWithLocalDateTime = forDateConversionWithLocalDateTime; + } + + public Optional getForDateConversionWithLocalDate() { + return forDateConversionWithLocalDate; + } + + public void setForDateConversionWithLocalDate(Optional forDateConversionWithLocalDate) { + this.forDateConversionWithLocalDate = forDateConversionWithLocalDate; + } + + public Optional getForSqlDateConversionWithLocalDate() { + return forSqlDateConversionWithLocalDate; + } + + public void setForSqlDateConversionWithLocalDate(Optional forSqlDateConversionWithLocalDate) { + this.forSqlDateConversionWithLocalDate = forSqlDateConversionWithLocalDate; + } + + public Optional getForDateConversionWithInstant() { + return forDateConversionWithInstant; + } + + public void setForDateConversionWithInstant(Optional forDateConversionWithInstant) { + this.forDateConversionWithInstant = forDateConversionWithInstant; + } + + public Optional getForLocalDateTimeConversionWithLocalDate() { + return forLocalDateTimeConversionWithLocalDate; + } + + public void setForLocalDateTimeConversionWithLocalDate( + Optional forLocalDateTimeConversionWithLocalDate) { + this.forLocalDateTimeConversionWithLocalDate = forLocalDateTimeConversionWithLocalDate; + } + + public Optional getForInstantConversionWithString() { + return forInstantConversionWithString; + } + + public void setForInstantConversionWithString(Optional forInstantConversionWithString) { + this.forInstantConversionWithString = forInstantConversionWithString; + } + + public Optional getForPeriodConversionWithString() { + return forPeriodConversionWithString; + } + + public void setForPeriodConversionWithString(Optional forPeriodConversionWithString) { + this.forPeriodConversionWithString = forPeriodConversionWithString; + } + + public Optional getForDurationConversionWithString() { + return forDurationConversionWithString; + } + + public void setForDurationConversionWithString(Optional forDurationConversionWithString) { + this.forDurationConversionWithString = forDurationConversionWithString; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/OptionalSourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/OptionalSourceTargetMapper.java new file mode 100644 index 0000000000..a266309b61 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/OptionalSourceTargetMapper.java @@ -0,0 +1,70 @@ +/* + * 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.conversion.java8time; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface OptionalSourceTargetMapper { + + String DATE_TIME_FORMAT = "dd.MM.yyyy HH:mm z"; + + String LOCAL_DATE_TIME_FORMAT = "dd.MM.yyyy HH:mm"; + + String LOCAL_DATE_FORMAT = "dd.MM.yyyy"; + + String LOCAL_TIME_FORMAT = "HH:mm"; + + OptionalSourceTargetMapper INSTANCE = Mappers.getMapper( OptionalSourceTargetMapper.class ); + + @Mapping(target = "zonedDateTime", dateFormat = DATE_TIME_FORMAT) + @Mapping(target = "localDateTime", dateFormat = LOCAL_DATE_TIME_FORMAT) + @Mapping(target = "localDate", dateFormat = LOCAL_DATE_FORMAT) + @Mapping(target = "localTime", dateFormat = LOCAL_TIME_FORMAT) + Target sourceToTarget(OptionalSource source); + + @Mapping(target = "zonedDateTime", dateFormat = DATE_TIME_FORMAT) + @Mapping(target = "localDateTime", dateFormat = LOCAL_DATE_TIME_FORMAT) + @Mapping(target = "localDate", dateFormat = LOCAL_DATE_FORMAT) + @Mapping(target = "localTime", dateFormat = LOCAL_TIME_FORMAT) + Target sourceToTargetDefaultMapping(OptionalSource source); + + @Mapping(target = "zonedDateTime", dateFormat = DATE_TIME_FORMAT) + Target sourceToTargetDateTimeMapped(OptionalSource source); + + @Mapping(target = "localDateTime", dateFormat = LOCAL_DATE_TIME_FORMAT) + Target sourceToTargetLocalDateTimeMapped(OptionalSource source); + + @Mapping(target = "localDate", dateFormat = LOCAL_DATE_FORMAT) + Target sourceToTargetLocalDateMapped(OptionalSource source); + + @Mapping(target = "localTime", dateFormat = LOCAL_TIME_FORMAT) + Target sourceToTargetLocalTimeMapped(OptionalSource source); + + @Mapping(target = "zonedDateTime", dateFormat = DATE_TIME_FORMAT) + @Mapping(target = "localDateTime", dateFormat = LOCAL_DATE_TIME_FORMAT) + @Mapping(target = "localDate", dateFormat = LOCAL_DATE_FORMAT) + @Mapping(target = "localTime", dateFormat = LOCAL_TIME_FORMAT) + OptionalSource targetToSource(Target target); + + @Mapping(target = "zonedDateTime", dateFormat = DATE_TIME_FORMAT) + OptionalSource targetToSourceDateTimeMapped(Target target); + + @Mapping(target = "localDateTime", dateFormat = LOCAL_DATE_TIME_FORMAT) + OptionalSource targetToSourceLocalDateTimeMapped(Target target); + + @Mapping(target = "localDate", dateFormat = LOCAL_DATE_FORMAT) + OptionalSource targetToSourceLocalDateMapped(Target target); + + @Mapping(target = "localTime", dateFormat = LOCAL_TIME_FORMAT) + OptionalSource targetToSourceLocalTimeMapped(Target target); + + @InheritInverseConfiguration(name = "sourceToTarget") + OptionalSource targetToSourceDefaultMapping(Target target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper1.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper1.java new file mode 100644 index 0000000000..d83b368bce --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper1.java @@ -0,0 +1,23 @@ +/* + * 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.conversion.lossy; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper( typeConversionPolicy = ReportingPolicy.ERROR ) +public interface ErroneousKitchenDrawerOptionalMapper1 { + + ErroneousKitchenDrawerOptionalMapper1 INSTANCE = Mappers.getMapper( ErroneousKitchenDrawerOptionalMapper1.class ); + + @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "numberOfForks", source = "numberOfForks" ) + RegularKitchenDrawerEntity map( OversizedKitchenDrawerOptionalDto dto ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper3.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper3.java new file mode 100644 index 0000000000..7d1824a5b1 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper3.java @@ -0,0 +1,23 @@ +/* + * 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.conversion.lossy; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper( typeConversionPolicy = ReportingPolicy.ERROR, uses = VerySpecialNumberMapper.class ) +public interface ErroneousKitchenDrawerOptionalMapper3 { + + ErroneousKitchenDrawerOptionalMapper3 INSTANCE = Mappers.getMapper( ErroneousKitchenDrawerOptionalMapper3.class ); + + @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "numberOfSpoons", source = "numberOfSpoons" ) + RegularKitchenDrawerEntity map( OversizedKitchenDrawerOptionalDto dto ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper4.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper4.java new file mode 100644 index 0000000000..edd33c71d9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper4.java @@ -0,0 +1,23 @@ +/* + * 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.conversion.lossy; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper( typeConversionPolicy = ReportingPolicy.ERROR ) +public interface ErroneousKitchenDrawerOptionalMapper4 { + + ErroneousKitchenDrawerOptionalMapper4 INSTANCE = Mappers.getMapper( ErroneousKitchenDrawerOptionalMapper4.class ); + + @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "depth", source = "depth" ) + RegularKitchenDrawerEntity map( OversizedKitchenDrawerOptionalDto dto ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper5.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper5.java new file mode 100644 index 0000000000..bad921a7c2 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper5.java @@ -0,0 +1,23 @@ +/* + * 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.conversion.lossy; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper( typeConversionPolicy = ReportingPolicy.ERROR ) +public interface ErroneousKitchenDrawerOptionalMapper5 { + + ErroneousKitchenDrawerOptionalMapper5 INSTANCE = Mappers.getMapper( ErroneousKitchenDrawerOptionalMapper5.class ); + + @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "length", source = "length" ) + RegularKitchenDrawerEntity map( OversizedKitchenDrawerOptionalDto dto ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper6.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper6.java new file mode 100644 index 0000000000..acd2e0876f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/ErroneousKitchenDrawerOptionalMapper6.java @@ -0,0 +1,20 @@ +/* + * 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.conversion.lossy; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +@Mapper( typeConversionPolicy = ReportingPolicy.ERROR ) +public interface ErroneousKitchenDrawerOptionalMapper6 { + + @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "drawerId", source = "drawerId" ) + RegularKitchenDrawerEntity map( OversizedKitchenDrawerOptionalDto dto ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/KitchenDrawerOptionalMapper2.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/KitchenDrawerOptionalMapper2.java new file mode 100644 index 0000000000..92862d3320 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/KitchenDrawerOptionalMapper2.java @@ -0,0 +1,23 @@ +/* + * 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.conversion.lossy; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper( typeConversionPolicy = ReportingPolicy.WARN ) +public interface KitchenDrawerOptionalMapper2 { + + KitchenDrawerOptionalMapper2 INSTANCE = Mappers.getMapper( KitchenDrawerOptionalMapper2.class ); + + @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "numberOfKnifes", source = "numberOfKnifes" ) + RegularKitchenDrawerEntity map( OversizedKitchenDrawerOptionalDto dto ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/KitchenDrawerOptionalMapper6.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/KitchenDrawerOptionalMapper6.java new file mode 100644 index 0000000000..2b3e982584 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/KitchenDrawerOptionalMapper6.java @@ -0,0 +1,23 @@ +/* + * 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.conversion.lossy; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper( typeConversionPolicy = ReportingPolicy.WARN, uses = VerySpecialNumberMapper.class ) +public interface KitchenDrawerOptionalMapper6 { + + KitchenDrawerOptionalMapper6 INSTANCE = Mappers.getMapper( KitchenDrawerOptionalMapper6.class ); + + @BeanMapping( ignoreByDefault = true ) + @Mapping( target = "height", source = "height" ) + RegularKitchenDrawerEntity map( OversizedKitchenDrawerOptionalDto dto ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java index 629f727273..604ff9bffa 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/LossyConversionTest.java @@ -22,6 +22,7 @@ */ @WithClasses({ OversizedKitchenDrawerDto.class, + OversizedKitchenDrawerOptionalDto.class, RegularKitchenDrawerEntity.class, VerySpecialNumber.class, VerySpecialNumberMapper.class, @@ -63,6 +64,19 @@ public void testNoErrorCase() { public void testConversionFromLongToInt() { } + @ProcessorTest + @WithClasses(ErroneousKitchenDrawerOptionalMapper1.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousKitchenDrawerOptionalMapper1.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = "Can't map property \"OptionalLong numberOfForks\". It has a possibly lossy conversion from " + + "OptionalLong to int.") + }) + public void testConversionFromOptionalLongToInt() { + } + @ProcessorTest @WithClasses(KitchenDrawerMapper2.class) @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, @@ -76,6 +90,19 @@ public void testConversionFromLongToInt() { public void testConversionFromBigIntegerToInteger() { } + @ProcessorTest + @WithClasses(KitchenDrawerOptionalMapper2.class) + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = KitchenDrawerOptionalMapper2.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 20, + message = "property \"Optional numberOfKnifes\" has a possibly lossy conversion " + + "from Optional to Integer.") + }) + public void testConversionFromOptionalBigIntegerToInteger() { + } + @ProcessorTest @WithClasses(ErroneousKitchenDrawerMapper6.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, @@ -89,6 +116,19 @@ public void testConversionFromBigIntegerToInteger() { public void testConversionFromStringToInt() { } + @ProcessorTest + @WithClasses(ErroneousKitchenDrawerOptionalMapper6.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousKitchenDrawerOptionalMapper6.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 17, + message = "Can't map property \"Optional drawerId\". It has a possibly lossy conversion from " + + "Optional to int.") + }) + public void testConversionFromOptionalStringToInt() { + } + @ProcessorTest @WithClasses(ErroneousKitchenDrawerMapper3.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, @@ -102,6 +142,19 @@ public void testConversionFromStringToInt() { public void test2StepConversionFromBigIntegerToLong() { } + @ProcessorTest + @WithClasses(ErroneousKitchenDrawerOptionalMapper3.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousKitchenDrawerOptionalMapper3.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = "Can't map property \"Optional numberOfSpoons\". " + + "It has a possibly lossy conversion from BigInteger to Long.") + }) + public void test2StepConversionFromOptionalBigIntegerToLong() { + } + @ProcessorTest @WithClasses(ErroneousKitchenDrawerMapper4.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, @@ -115,6 +168,20 @@ public void test2StepConversionFromBigIntegerToLong() { public void testConversionFromDoubleToFloat() { } + @ProcessorTest + @WithClasses(ErroneousKitchenDrawerOptionalMapper4.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousKitchenDrawerOptionalMapper4.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = + "Can't map property \"Optional depth\". " + + "It has a possibly lossy conversion from Optional to float.") + }) + public void testConversionFromOptionalDoubleToFloat() { + } + @ProcessorTest @WithClasses(ErroneousKitchenDrawerMapper5.class) @ExpectedCompilationOutcome(value = CompilationResult.FAILED, @@ -128,6 +195,20 @@ public void testConversionFromDoubleToFloat() { public void testConversionFromBigDecimalToFloat() { } + @ProcessorTest + @WithClasses(ErroneousKitchenDrawerOptionalMapper5.class) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousKitchenDrawerOptionalMapper5.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + line = 20, + message = + "Can't map property \"Optional length\". " + + "It has a possibly lossy conversion from Optional to Float.") + }) + public void testConversionFromOptionalBigDecimalToFloat() { + } + @ProcessorTest @WithClasses(KitchenDrawerMapper6.class) @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, @@ -140,6 +221,19 @@ public void testConversionFromBigDecimalToFloat() { public void test2StepConversionFromDoubleToFloat() { } + @ProcessorTest + @WithClasses(KitchenDrawerOptionalMapper6.class) + @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = KitchenDrawerOptionalMapper6.class, + kind = javax.tools.Diagnostic.Kind.WARNING, + line = 20, + message = "property \"OptionalDouble height\" has a possibly lossy conversion from " + + "OptionalDouble to float.") + }) + public void test2StepConversionFromOptionalDoubleToFloat() { + } + @ProcessorTest @WithClasses(ListMapper.class) @ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED, diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/OversizedKitchenDrawerOptionalDto.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/OversizedKitchenDrawerOptionalDto.java new file mode 100644 index 0000000000..f214fecd1d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/OversizedKitchenDrawerOptionalDto.java @@ -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.conversion.lossy; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalLong; + +/** + * @author Filip Hrisafov + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class OversizedKitchenDrawerOptionalDto { + + /* yes, its a big drawer */ + private OptionalLong numberOfForks; + private Optional numberOfKnifes; + private Optional numberOfSpoons; + private Optional depth; + private Optional length; + private OptionalDouble height; + private Optional drawerId; + + public OptionalLong getNumberOfForks() { + return numberOfForks; + } + + public void setNumberOfForks(OptionalLong numberOfForks) { + this.numberOfForks = numberOfForks; + } + + public Optional getNumberOfKnifes() { + return numberOfKnifes; + } + + public void setNumberOfKnifes(Optional numberOfKnifes) { + this.numberOfKnifes = numberOfKnifes; + } + + public Optional getNumberOfSpoons() { + return numberOfSpoons; + } + + public void setNumberOfSpoons(Optional numberOfSpoons) { + this.numberOfSpoons = numberOfSpoons; + } + + public Optional getDepth() { + return depth; + } + + public void setDepth(Optional depth) { + this.depth = depth; + } + + public Optional getLength() { + return length; + } + + public void setLength(Optional length) { + this.length = length; + } + + public OptionalDouble getHeight() { + return height; + } + + public void setHeight(OptionalDouble height) { + this.height = height; + } + + public Optional getDrawerId() { + return drawerId; + } + + public void setDrawerId(Optional drawerId) { + this.drawerId = drawerId; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/VerySpecialNumberMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/VerySpecialNumberMapper.java index d49a263deb..00b543c728 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/VerySpecialNumberMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/lossy/VerySpecialNumberMapper.java @@ -6,6 +6,7 @@ package org.mapstruct.ap.test.conversion.lossy; import java.math.BigInteger; +import java.util.Optional; /** * @author Sjaak Derksen @@ -19,4 +20,9 @@ VerySpecialNumber fromFloat(float f) { BigInteger toBigInteger(VerySpecialNumber v) { return new BigInteger( "10" ); } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + BigInteger toBigInteger(Optional v) { + return new BigInteger( "10" ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/ByteWrapperOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/ByteWrapperOptionalSource.java new file mode 100644 index 0000000000..a92b029b14 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/ByteWrapperOptionalSource.java @@ -0,0 +1,121 @@ +/* + * 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.conversion.nativetypes; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class ByteWrapperOptionalSource { + + private Optional b = Optional.empty(); + private Optional bb = Optional.empty(); + private Optional s = Optional.empty(); + private Optional ss = Optional.empty(); + private Optional i = Optional.empty(); + private Optional ii = Optional.empty(); + private Optional l = Optional.empty(); + private Optional ll = Optional.empty(); + private Optional f = Optional.empty(); + private Optional ff = Optional.empty(); + private Optional d = Optional.empty(); + private Optional dd = Optional.empty(); + + public Optional getB() { + return b; + } + + public void setB(Optional b) { + this.b = b; + } + + public Optional getBb() { + return bb; + } + + public void setBb(Optional bb) { + this.bb = bb; + } + + public Optional getS() { + return s; + } + + public void setS(Optional s) { + this.s = s; + } + + public Optional getSs() { + return ss; + } + + public void setSs(Optional ss) { + this.ss = ss; + } + + public Optional getI() { + return i; + } + + public void setI(Optional i) { + this.i = i; + } + + public Optional getIi() { + return ii; + } + + public void setIi(Optional ii) { + this.ii = ii; + } + + public Optional getL() { + return l; + } + + public void setL(Optional l) { + this.l = l; + } + + public Optional getLl() { + return ll; + } + + public void setLl(Optional ll) { + this.ll = ll; + } + + public Optional getF() { + return f; + } + + public void setF(Optional f) { + this.f = f; + } + + public Optional getFf() { + return ff; + } + + public void setFf(Optional ff) { + this.ff = ff; + } + + public Optional getD() { + return d; + } + + public void setD(Optional d) { + this.d = d; + } + + public Optional getDd() { + return dd; + } + + public void setDd(Optional dd) { + this.dd = dd; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/DoubleOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/DoubleOptionalSource.java new file mode 100644 index 0000000000..e469c44390 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/DoubleOptionalSource.java @@ -0,0 +1,121 @@ +/* + * 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.conversion.nativetypes; + +import java.util.OptionalDouble; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class DoubleOptionalSource { + + private OptionalDouble b = OptionalDouble.empty(); + private OptionalDouble bb = OptionalDouble.empty(); + private OptionalDouble s = OptionalDouble.empty(); + private OptionalDouble ss = OptionalDouble.empty(); + private OptionalDouble i = OptionalDouble.empty(); + private OptionalDouble ii = OptionalDouble.empty(); + private OptionalDouble l = OptionalDouble.empty(); + private OptionalDouble ll = OptionalDouble.empty(); + private OptionalDouble f = OptionalDouble.empty(); + private OptionalDouble ff = OptionalDouble.empty(); + private OptionalDouble d = OptionalDouble.empty(); + private OptionalDouble dd = OptionalDouble.empty(); + + public OptionalDouble getB() { + return b; + } + + public void setB(OptionalDouble b) { + this.b = b; + } + + public OptionalDouble getBb() { + return bb; + } + + public void setBb(OptionalDouble bb) { + this.bb = bb; + } + + public OptionalDouble getS() { + return s; + } + + public void setS(OptionalDouble s) { + this.s = s; + } + + public OptionalDouble getSs() { + return ss; + } + + public void setSs(OptionalDouble ss) { + this.ss = ss; + } + + public OptionalDouble getI() { + return i; + } + + public void setI(OptionalDouble i) { + this.i = i; + } + + public OptionalDouble getIi() { + return ii; + } + + public void setIi(OptionalDouble ii) { + this.ii = ii; + } + + public OptionalDouble getL() { + return l; + } + + public void setL(OptionalDouble l) { + this.l = l; + } + + public OptionalDouble getLl() { + return ll; + } + + public void setLl(OptionalDouble ll) { + this.ll = ll; + } + + public OptionalDouble getF() { + return f; + } + + public void setF(OptionalDouble f) { + this.f = f; + } + + public OptionalDouble getFf() { + return ff; + } + + public void setFf(OptionalDouble ff) { + this.ff = ff; + } + + public OptionalDouble getD() { + return d; + } + + public void setD(OptionalDouble d) { + this.d = d; + } + + public OptionalDouble getDd() { + return dd; + } + + public void setDd(OptionalDouble dd) { + this.dd = dd; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/DoubleWrapperOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/DoubleWrapperOptionalSource.java new file mode 100644 index 0000000000..ecf0d9bc41 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/DoubleWrapperOptionalSource.java @@ -0,0 +1,121 @@ +/* + * 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.conversion.nativetypes; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class DoubleWrapperOptionalSource { + + private Optional b = Optional.empty(); + private Optional bb = Optional.empty(); + private Optional s = Optional.empty(); + private Optional ss = Optional.empty(); + private Optional i = Optional.empty(); + private Optional ii = Optional.empty(); + private Optional l = Optional.empty(); + private Optional ll = Optional.empty(); + private Optional f = Optional.empty(); + private Optional ff = Optional.empty(); + private Optional d = Optional.empty(); + private Optional dd = Optional.empty(); + + public Optional getB() { + return b; + } + + public void setB(Optional b) { + this.b = b; + } + + public Optional getBb() { + return bb; + } + + public void setBb(Optional bb) { + this.bb = bb; + } + + public Optional getS() { + return s; + } + + public void setS(Optional s) { + this.s = s; + } + + public Optional getSs() { + return ss; + } + + public void setSs(Optional ss) { + this.ss = ss; + } + + public Optional getI() { + return i; + } + + public void setI(Optional i) { + this.i = i; + } + + public Optional getIi() { + return ii; + } + + public void setIi(Optional ii) { + this.ii = ii; + } + + public Optional getL() { + return l; + } + + public void setL(Optional l) { + this.l = l; + } + + public Optional getLl() { + return ll; + } + + public void setLl(Optional ll) { + this.ll = ll; + } + + public Optional getF() { + return f; + } + + public void setF(Optional f) { + this.f = f; + } + + public Optional getFf() { + return ff; + } + + public void setFf(Optional ff) { + this.ff = ff; + } + + public Optional getD() { + return d; + } + + public void setD(Optional d) { + this.d = d; + } + + public Optional getDd() { + return dd; + } + + public void setDd(Optional dd) { + this.dd = dd; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/FloatWrapperOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/FloatWrapperOptionalSource.java new file mode 100644 index 0000000000..f20e6fa5d9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/FloatWrapperOptionalSource.java @@ -0,0 +1,121 @@ +/* + * 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.conversion.nativetypes; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class FloatWrapperOptionalSource { + + private Optional b = Optional.empty(); + private Optional bb = Optional.empty(); + private Optional s = Optional.empty(); + private Optional ss = Optional.empty(); + private Optional i = Optional.empty(); + private Optional ii = Optional.empty(); + private Optional l = Optional.empty(); + private Optional ll = Optional.empty(); + private Optional f = Optional.empty(); + private Optional ff = Optional.empty(); + private Optional d = Optional.empty(); + private Optional dd = Optional.empty(); + + public Optional getB() { + return b; + } + + public void setB(Optional b) { + this.b = b; + } + + public Optional getBb() { + return bb; + } + + public void setBb(Optional bb) { + this.bb = bb; + } + + public Optional getS() { + return s; + } + + public void setS(Optional s) { + this.s = s; + } + + public Optional getSs() { + return ss; + } + + public void setSs(Optional ss) { + this.ss = ss; + } + + public Optional getI() { + return i; + } + + public void setI(Optional i) { + this.i = i; + } + + public Optional getIi() { + return ii; + } + + public void setIi(Optional ii) { + this.ii = ii; + } + + public Optional getL() { + return l; + } + + public void setL(Optional l) { + this.l = l; + } + + public Optional getLl() { + return ll; + } + + public void setLl(Optional ll) { + this.ll = ll; + } + + public Optional getF() { + return f; + } + + public void setF(Optional f) { + this.f = f; + } + + public Optional getFf() { + return ff; + } + + public void setFf(Optional ff) { + this.ff = ff; + } + + public Optional getD() { + return d; + } + + public void setD(Optional d) { + this.d = d; + } + + public Optional getDd() { + return dd; + } + + public void setDd(Optional dd) { + this.dd = dd; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/IntOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/IntOptionalSource.java new file mode 100644 index 0000000000..6543b7bc0b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/IntOptionalSource.java @@ -0,0 +1,121 @@ +/* + * 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.conversion.nativetypes; + +import java.util.OptionalInt; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class IntOptionalSource { + + private OptionalInt b = OptionalInt.empty(); + private OptionalInt bb = OptionalInt.empty(); + private OptionalInt s = OptionalInt.empty(); + private OptionalInt ss = OptionalInt.empty(); + private OptionalInt i = OptionalInt.empty(); + private OptionalInt ii = OptionalInt.empty(); + private OptionalInt l = OptionalInt.empty(); + private OptionalInt ll = OptionalInt.empty(); + private OptionalInt f = OptionalInt.empty(); + private OptionalInt ff = OptionalInt.empty(); + private OptionalInt d = OptionalInt.empty(); + private OptionalInt dd = OptionalInt.empty(); + + public OptionalInt getB() { + return b; + } + + public void setB(OptionalInt b) { + this.b = b; + } + + public OptionalInt getBb() { + return bb; + } + + public void setBb(OptionalInt bb) { + this.bb = bb; + } + + public OptionalInt getS() { + return s; + } + + public void setS(OptionalInt s) { + this.s = s; + } + + public OptionalInt getSs() { + return ss; + } + + public void setSs(OptionalInt ss) { + this.ss = ss; + } + + public OptionalInt getI() { + return i; + } + + public void setI(OptionalInt i) { + this.i = i; + } + + public OptionalInt getIi() { + return ii; + } + + public void setIi(OptionalInt ii) { + this.ii = ii; + } + + public OptionalInt getL() { + return l; + } + + public void setL(OptionalInt l) { + this.l = l; + } + + public OptionalInt getLl() { + return ll; + } + + public void setLl(OptionalInt ll) { + this.ll = ll; + } + + public OptionalInt getF() { + return f; + } + + public void setF(OptionalInt f) { + this.f = f; + } + + public OptionalInt getFf() { + return ff; + } + + public void setFf(OptionalInt ff) { + this.ff = ff; + } + + public OptionalInt getD() { + return d; + } + + public void setD(OptionalInt d) { + this.d = d; + } + + public OptionalInt getDd() { + return dd; + } + + public void setDd(OptionalInt dd) { + this.dd = dd; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/IntWrapperOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/IntWrapperOptionalSource.java new file mode 100644 index 0000000000..b1243d9c1c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/IntWrapperOptionalSource.java @@ -0,0 +1,121 @@ +/* + * 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.conversion.nativetypes; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class IntWrapperOptionalSource { + + private Optional b = Optional.empty(); + private Optional bb = Optional.empty(); + private Optional s = Optional.empty(); + private Optional ss = Optional.empty(); + private Optional i = Optional.empty(); + private Optional ii = Optional.empty(); + private Optional l = Optional.empty(); + private Optional ll = Optional.empty(); + private Optional f = Optional.empty(); + private Optional ff = Optional.empty(); + private Optional d = Optional.empty(); + private Optional dd = Optional.empty(); + + public Optional getB() { + return b; + } + + public void setB(Optional b) { + this.b = b; + } + + public Optional getBb() { + return bb; + } + + public void setBb(Optional bb) { + this.bb = bb; + } + + public Optional getS() { + return s; + } + + public void setS(Optional s) { + this.s = s; + } + + public Optional getSs() { + return ss; + } + + public void setSs(Optional ss) { + this.ss = ss; + } + + public Optional getI() { + return i; + } + + public void setI(Optional i) { + this.i = i; + } + + public Optional getIi() { + return ii; + } + + public void setIi(Optional ii) { + this.ii = ii; + } + + public Optional getL() { + return l; + } + + public void setL(Optional l) { + this.l = l; + } + + public Optional getLl() { + return ll; + } + + public void setLl(Optional ll) { + this.ll = ll; + } + + public Optional getF() { + return f; + } + + public void setF(Optional f) { + this.f = f; + } + + public Optional getFf() { + return ff; + } + + public void setFf(Optional ff) { + this.ff = ff; + } + + public Optional getD() { + return d; + } + + public void setD(Optional d) { + this.d = d; + } + + public Optional getDd() { + return dd; + } + + public void setDd(Optional dd) { + this.dd = dd; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/LongOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/LongOptionalSource.java new file mode 100644 index 0000000000..1df7baaec9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/LongOptionalSource.java @@ -0,0 +1,121 @@ +/* + * 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.conversion.nativetypes; + +import java.util.OptionalLong; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class LongOptionalSource { + + private OptionalLong b = OptionalLong.empty(); + private OptionalLong bb = OptionalLong.empty(); + private OptionalLong s = OptionalLong.empty(); + private OptionalLong ss = OptionalLong.empty(); + private OptionalLong i = OptionalLong.empty(); + private OptionalLong ii = OptionalLong.empty(); + private OptionalLong l = OptionalLong.empty(); + private OptionalLong ll = OptionalLong.empty(); + private OptionalLong f = OptionalLong.empty(); + private OptionalLong ff = OptionalLong.empty(); + private OptionalLong d = OptionalLong.empty(); + private OptionalLong dd = OptionalLong.empty(); + + public OptionalLong getB() { + return b; + } + + public void setB(OptionalLong b) { + this.b = b; + } + + public OptionalLong getBb() { + return bb; + } + + public void setBb(OptionalLong bb) { + this.bb = bb; + } + + public OptionalLong getS() { + return s; + } + + public void setS(OptionalLong s) { + this.s = s; + } + + public OptionalLong getSs() { + return ss; + } + + public void setSs(OptionalLong ss) { + this.ss = ss; + } + + public OptionalLong getI() { + return i; + } + + public void setI(OptionalLong i) { + this.i = i; + } + + public OptionalLong getIi() { + return ii; + } + + public void setIi(OptionalLong ii) { + this.ii = ii; + } + + public OptionalLong getL() { + return l; + } + + public void setL(OptionalLong l) { + this.l = l; + } + + public OptionalLong getLl() { + return ll; + } + + public void setLl(OptionalLong ll) { + this.ll = ll; + } + + public OptionalLong getF() { + return f; + } + + public void setF(OptionalLong f) { + this.f = f; + } + + public OptionalLong getFf() { + return ff; + } + + public void setFf(OptionalLong ff) { + this.ff = ff; + } + + public OptionalLong getD() { + return d; + } + + public void setD(OptionalLong d) { + this.d = d; + } + + public OptionalLong getDd() { + return dd; + } + + public void setDd(OptionalLong dd) { + this.dd = dd; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/LongWrapperOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/LongWrapperOptionalSource.java new file mode 100644 index 0000000000..bed8ab6b59 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/LongWrapperOptionalSource.java @@ -0,0 +1,121 @@ +/* + * 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.conversion.nativetypes; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class LongWrapperOptionalSource { + + private Optional b = Optional.empty(); + private Optional bb = Optional.empty(); + private Optional s = Optional.empty(); + private Optional ss = Optional.empty(); + private Optional i = Optional.empty(); + private Optional ii = Optional.empty(); + private Optional l = Optional.empty(); + private Optional ll = Optional.empty(); + private Optional f = Optional.empty(); + private Optional ff = Optional.empty(); + private Optional d = Optional.empty(); + private Optional dd = Optional.empty(); + + public Optional getB() { + return b; + } + + public void setB(Optional b) { + this.b = b; + } + + public Optional getBb() { + return bb; + } + + public void setBb(Optional bb) { + this.bb = bb; + } + + public Optional getS() { + return s; + } + + public void setS(Optional s) { + this.s = s; + } + + public Optional getSs() { + return ss; + } + + public void setSs(Optional ss) { + this.ss = ss; + } + + public Optional getI() { + return i; + } + + public void setI(Optional i) { + this.i = i; + } + + public Optional getIi() { + return ii; + } + + public void setIi(Optional ii) { + this.ii = ii; + } + + public Optional getL() { + return l; + } + + public void setL(Optional l) { + this.l = l; + } + + public Optional getLl() { + return ll; + } + + public void setLl(Optional ll) { + this.ll = ll; + } + + public Optional getF() { + return f; + } + + public void setF(Optional f) { + this.f = f; + } + + public Optional getFf() { + return ff; + } + + public void setFf(Optional ff) { + this.ff = ff; + } + + public Optional getD() { + return d; + } + + public void setD(Optional d) { + this.d = d; + } + + public Optional getDd() { + return dd; + } + + public void setDd(Optional dd) { + this.dd = dd; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/NumberOptionalConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/NumberOptionalConversionTest.java new file mode 100644 index 0000000000..0857b20473 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/NumberOptionalConversionTest.java @@ -0,0 +1,357 @@ +/* + * 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.conversion.nativetypes; + +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +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; + +@WithClasses({ + ByteTarget.class, + ByteWrapperOptionalSource.class, + ByteWrapperTarget.class, + ShortTarget.class, + ShortWrapperOptionalSource.class, + ShortWrapperTarget.class, + IntOptionalSource.class, + IntTarget.class, + IntWrapperOptionalSource.class, + IntWrapperTarget.class, + LongOptionalSource.class, + LongTarget.class, + LongWrapperOptionalSource.class, + LongWrapperTarget.class, + FloatWrapperOptionalSource.class, + FloatWrapperTarget.class, + DoubleOptionalSource.class, + DoubleTarget.class, + DoubleWrapperOptionalSource.class, + DoubleWrapperTarget.class, + OptionalNumberConversionMapper.class +}) +public class NumberOptionalConversionTest { + + @ProcessorTest + public void shouldApplyByteWrapperConversions() { + ByteWrapperOptionalSource source = new ByteWrapperOptionalSource(); + source.setB( Optional.of( (byte) 1 ) ); + source.setBb( Optional.of( (byte) 2 ) ); + source.setS( Optional.of( (byte) 3 ) ); + source.setSs( Optional.of( (byte) 4 ) ); + source.setI( Optional.of( (byte) 5 ) ); + source.setIi( Optional.of( (byte) 6 ) ); + source.setL( Optional.of( (byte) 7 ) ); + source.setLl( Optional.of( (byte) 8 ) ); + source.setF( Optional.of( (byte) 9 ) ); + source.setFf( Optional.of( (byte) 10 ) ); + source.setD( Optional.of( (byte) 11 ) ); + source.setDd( Optional.of( (byte) 12 ) ); + + ByteWrapperTarget target = OptionalNumberConversionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( Byte.valueOf( (byte) 2 ) ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( Short.valueOf( (short) 4 ) ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( Integer.valueOf( 6 ) ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( Long.valueOf( 8 ) ); + assertThat( target.getF() ).isEqualTo( 9f ); + assertThat( target.getFf() ).isEqualTo( Float.valueOf( 10f ) ); + assertThat( target.getD() ).isEqualTo( 11d ); + assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); + } + + @ProcessorTest + public void shouldApplyShortWrapperConversions() { + ShortWrapperOptionalSource source = new ShortWrapperOptionalSource(); + source.setB( Optional.of( (short) 1 ) ); + source.setBb( Optional.of( (short) 2 ) ); + source.setS( Optional.of( (short) 3 ) ); + source.setSs( Optional.of( (short) 4 ) ); + source.setI( Optional.of( (short) 5 ) ); + source.setIi( Optional.of( (short) 6 ) ); + source.setL( Optional.of( (short) 7 ) ); + source.setLl( Optional.of( (short) 8 ) ); + source.setF( Optional.of( (short) 9 ) ); + source.setFf( Optional.of( (short) 10 ) ); + source.setD( Optional.of( (short) 11 ) ); + source.setDd( Optional.of( (short) 12 ) ); + + ShortWrapperTarget target = OptionalNumberConversionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( Byte.valueOf( (byte) 2 ) ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( Short.valueOf( (short) 4 ) ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( Integer.valueOf( 6 ) ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( Long.valueOf( 8 ) ); + assertThat( target.getF() ).isEqualTo( 9f ); + assertThat( target.getFf() ).isEqualTo( Float.valueOf( 10f ) ); + assertThat( target.getD() ).isEqualTo( 11d ); + assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); + } + + @ProcessorTest + public void shouldApplyIntConversions() { + IntOptionalSource source = new IntOptionalSource(); + source.setB( OptionalInt.of( 1 ) ); + source.setBb( OptionalInt.of( 2 ) ); + source.setS( OptionalInt.of( 3 ) ); + source.setSs( OptionalInt.of( 4 ) ); + source.setI( OptionalInt.of( 5 ) ); + source.setIi( OptionalInt.of( 6 ) ); + source.setL( OptionalInt.of( 7 ) ); + source.setLl( OptionalInt.of( 8 ) ); + source.setF( OptionalInt.of( 9 ) ); + source.setFf( OptionalInt.of( 10 ) ); + source.setD( OptionalInt.of( 11 ) ); + source.setDd( OptionalInt.of( 12 ) ); + + IntTarget target = OptionalNumberConversionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( Byte.valueOf( (byte) 2 ) ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( Short.valueOf( (short) 4 ) ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( Integer.valueOf( 6 ) ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( Long.valueOf( 8 ) ); + assertThat( target.getF() ).isEqualTo( 9f ); + assertThat( target.getFf() ).isEqualTo( Float.valueOf( 10f ) ); + assertThat( target.getD() ).isEqualTo( 11d ); + assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); + } + + @ProcessorTest + public void shouldApplyIntWrapperConversions() { + IntWrapperOptionalSource source = new IntWrapperOptionalSource(); + source.setB( Optional.of( 1 ) ); + source.setBb( Optional.of( 2 ) ); + source.setS( Optional.of( 3 ) ); + source.setSs( Optional.of( 4 ) ); + source.setI( Optional.of( 5 ) ); + source.setIi( Optional.of( 6 ) ); + source.setL( Optional.of( 7 ) ); + source.setLl( Optional.of( 8 ) ); + source.setF( Optional.of( 9 ) ); + source.setFf( Optional.of( 10 ) ); + source.setD( Optional.of( 11 ) ); + source.setDd( Optional.of( 12 ) ); + + IntWrapperTarget target = OptionalNumberConversionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( Byte.valueOf( (byte) 2 ) ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( Short.valueOf( (short) 4 ) ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( Integer.valueOf( 6 ) ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( Long.valueOf( 8 ) ); + assertThat( target.getF() ).isEqualTo( 9f ); + assertThat( target.getFf() ).isEqualTo( Float.valueOf( 10f ) ); + assertThat( target.getD() ).isEqualTo( 11d ); + assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); + } + + @ProcessorTest + public void shouldApplyLongConversions() { + LongOptionalSource source = new LongOptionalSource(); + source.setB( OptionalLong.of( 1 ) ); + source.setBb( OptionalLong.of( 2 ) ); + source.setS( OptionalLong.of( 3 ) ); + source.setSs( OptionalLong.of( 4 ) ); + source.setI( OptionalLong.of( 5 ) ); + source.setIi( OptionalLong.of( 6 ) ); + source.setL( OptionalLong.of( 7 ) ); + source.setLl( OptionalLong.of( 8 ) ); + source.setF( OptionalLong.of( 9 ) ); + source.setFf( OptionalLong.of( 10 ) ); + source.setD( OptionalLong.of( 11 ) ); + source.setDd( OptionalLong.of( 12 ) ); + + LongTarget target = OptionalNumberConversionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( Byte.valueOf( (byte) 2 ) ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( Short.valueOf( (short) 4 ) ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( Integer.valueOf( 6 ) ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( Long.valueOf( 8 ) ); + assertThat( target.getF() ).isEqualTo( 9f ); + assertThat( target.getFf() ).isEqualTo( Float.valueOf( 10f ) ); + assertThat( target.getD() ).isEqualTo( 11d ); + assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); + } + + @ProcessorTest + public void shouldApplyLongWrapperConversions() { + LongWrapperOptionalSource source = new LongWrapperOptionalSource(); + source.setB( Optional.of( (long) 1 ) ); + source.setBb( Optional.of( (long) 2 ) ); + source.setS( Optional.of( (long) 3 ) ); + source.setSs( Optional.of( (long) 4 ) ); + source.setI( Optional.of( (long) 5 ) ); + source.setIi( Optional.of( (long) 6 ) ); + source.setL( Optional.of( (long) 7 ) ); + source.setLl( Optional.of( (long) 8 ) ); + source.setF( Optional.of( (long) 9 ) ); + source.setFf( Optional.of( (long) 10 ) ); + source.setD( Optional.of( (long) 11 ) ); + source.setDd( Optional.of( (long) 12 ) ); + + LongWrapperTarget target = OptionalNumberConversionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( Byte.valueOf( (byte) 2 ) ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( Short.valueOf( (short) 4 ) ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( Integer.valueOf( 6 ) ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( Long.valueOf( 8 ) ); + assertThat( target.getF() ).isEqualTo( 9f ); + assertThat( target.getFf() ).isEqualTo( Float.valueOf( 10f ) ); + assertThat( target.getD() ).isEqualTo( 11d ); + assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); + } + + @ProcessorTest + public void shouldApplyFloatWrapperConversions() { + FloatWrapperOptionalSource source = new FloatWrapperOptionalSource(); + source.setB( Optional.of( 1f ) ); + source.setBb( Optional.of( 2f ) ); + source.setS( Optional.of( 3f ) ); + source.setSs( Optional.of( 4f ) ); + source.setI( Optional.of( 5f ) ); + source.setIi( Optional.of( 6f ) ); + source.setL( Optional.of( 7f ) ); + source.setLl( Optional.of( 8f ) ); + source.setF( Optional.of( 9f ) ); + source.setFf( Optional.of( 10f ) ); + source.setD( Optional.of( 11f ) ); + source.setDd( Optional.of( 12f ) ); + + FloatWrapperTarget target = OptionalNumberConversionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( Byte.valueOf( (byte) 2 ) ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( Short.valueOf( (short) 4 ) ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( Integer.valueOf( 6 ) ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( Long.valueOf( 8 ) ); + assertThat( target.getF() ).isEqualTo( 9f ); + assertThat( target.getFf() ).isEqualTo( Float.valueOf( 10f ) ); + assertThat( target.getD() ).isEqualTo( 11d ); + assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); + } + + @ProcessorTest + public void shouldApplyDoubleConversions() { + DoubleOptionalSource source = new DoubleOptionalSource(); + source.setB( OptionalDouble.of( 1 ) ); + source.setBb( OptionalDouble.of( 2 ) ); + source.setS( OptionalDouble.of( 3 ) ); + source.setSs( OptionalDouble.of( 4 ) ); + source.setI( OptionalDouble.of( 5 ) ); + source.setIi( OptionalDouble.of( 6 ) ); + source.setL( OptionalDouble.of( 7 ) ); + source.setLl( OptionalDouble.of( 8 ) ); + source.setF( OptionalDouble.of( 9 ) ); + source.setFf( OptionalDouble.of( 10 ) ); + source.setD( OptionalDouble.of( 11 ) ); + source.setDd( OptionalDouble.of( 12 ) ); + + DoubleTarget target = OptionalNumberConversionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( Byte.valueOf( (byte) 2 ) ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( Short.valueOf( (short) 4 ) ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( Integer.valueOf( 6 ) ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( Long.valueOf( 8 ) ); + assertThat( target.getF() ).isEqualTo( 9f ); + assertThat( target.getFf() ).isEqualTo( Float.valueOf( 10f ) ); + assertThat( target.getD() ).isEqualTo( 11d ); + assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); + } + + @ProcessorTest + public void shouldApplyDoubleWrapperConversions() { + DoubleWrapperOptionalSource source = new DoubleWrapperOptionalSource(); + source.setB( Optional.of( 1d ) ); + source.setBb( Optional.of( 2d ) ); + source.setS( Optional.of( 3d ) ); + source.setSs( Optional.of( 4d ) ); + source.setI( Optional.of( 5d ) ); + source.setIi( Optional.of( 6d ) ); + source.setL( Optional.of( 7d ) ); + source.setLl( Optional.of( 8d ) ); + source.setF( Optional.of( 9d ) ); + source.setFf( Optional.of( 10d ) ); + source.setD( Optional.of( 11d ) ); + source.setDd( Optional.of( 12d ) ); + + DoubleWrapperTarget target = OptionalNumberConversionMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getB() ).isEqualTo( (byte) 1 ); + assertThat( target.getBb() ).isEqualTo( Byte.valueOf( (byte) 2 ) ); + assertThat( target.getS() ).isEqualTo( (short) 3 ); + assertThat( target.getSs() ).isEqualTo( Short.valueOf( (short) 4 ) ); + assertThat( target.getI() ).isEqualTo( 5 ); + assertThat( target.getIi() ).isEqualTo( Integer.valueOf( 6 ) ); + assertThat( target.getL() ).isEqualTo( 7 ); + assertThat( target.getLl() ).isEqualTo( Long.valueOf( 8 ) ); + assertThat( target.getF() ).isEqualTo( 9f ); + assertThat( target.getFf() ).isEqualTo( Float.valueOf( 10f ) ); + assertThat( target.getD() ).isEqualTo( 11d ); + assertThat( target.getDd() ).isEqualTo( Double.valueOf( 12d ) ); + } + + @ProcessorTest + @IssueKey("229") + public void wrapperToPrimitiveIsNullSafe() { + assertThat( OptionalNumberConversionMapper.INSTANCE.sourceToTarget( new ByteWrapperOptionalSource() ) ) + .isNotNull(); + assertThat( OptionalNumberConversionMapper.INSTANCE.sourceToTarget( new DoubleWrapperOptionalSource() ) ) + .isNotNull(); + assertThat( OptionalNumberConversionMapper.INSTANCE.sourceToTarget( new ShortWrapperOptionalSource() ) ) + .isNotNull(); + assertThat( OptionalNumberConversionMapper.INSTANCE.sourceToTarget( new IntWrapperOptionalSource() ) ) + .isNotNull(); + assertThat( OptionalNumberConversionMapper.INSTANCE.sourceToTarget( new FloatWrapperOptionalSource() ) ) + .isNotNull(); + assertThat( OptionalNumberConversionMapper.INSTANCE.sourceToTarget( new LongWrapperOptionalSource() ) ) + .isNotNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/OptionalNumberConversionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/OptionalNumberConversionMapper.java new file mode 100644 index 0000000000..94472586fa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/OptionalNumberConversionMapper.java @@ -0,0 +1,52 @@ +/* + * 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.conversion.nativetypes; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface OptionalNumberConversionMapper { + + OptionalNumberConversionMapper INSTANCE = Mappers.getMapper( OptionalNumberConversionMapper.class ); + + ByteWrapperTarget sourceToTarget(ByteWrapperOptionalSource source); + + ByteWrapperOptionalSource targetToSource(ByteWrapperTarget target); + + ShortWrapperTarget sourceToTarget(ShortWrapperOptionalSource source); + + ShortWrapperOptionalSource targetToSource(ShortWrapperTarget target); + + IntTarget sourceToTarget(IntOptionalSource source); + + IntOptionalSource targetToSource(IntTarget target); + + IntWrapperTarget sourceToTarget(IntWrapperOptionalSource source); + + IntWrapperOptionalSource targetToSource(IntWrapperTarget target); + + LongTarget sourceToTarget(LongOptionalSource source); + + LongOptionalSource targetToSource(LongTarget target); + + LongWrapperTarget sourceToTarget(LongWrapperOptionalSource source); + + LongWrapperOptionalSource targetToSource(LongWrapperTarget target); + + FloatWrapperTarget sourceToTarget(FloatWrapperOptionalSource source); + + FloatWrapperOptionalSource targetToSource(FloatWrapperTarget target); + + DoubleTarget sourceToTarget(DoubleOptionalSource source); + + DoubleOptionalSource targetToSource(DoubleTarget target); + + DoubleWrapperTarget sourceToTarget(DoubleWrapperOptionalSource source); + + DoubleWrapperOptionalSource targetToSource(DoubleWrapperTarget target); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/ShortWrapperOptionalSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/ShortWrapperOptionalSource.java new file mode 100644 index 0000000000..16acf6c69b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/nativetypes/ShortWrapperOptionalSource.java @@ -0,0 +1,121 @@ +/* + * 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.conversion.nativetypes; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class ShortWrapperOptionalSource { + + private Optional b = Optional.empty(); + private Optional bb = Optional.empty(); + private Optional s = Optional.empty(); + private Optional ss = Optional.empty(); + private Optional i = Optional.empty(); + private Optional ii = Optional.empty(); + private Optional l = Optional.empty(); + private Optional ll = Optional.empty(); + private Optional f = Optional.empty(); + private Optional ff = Optional.empty(); + private Optional d = Optional.empty(); + private Optional dd = Optional.empty(); + + public Optional getB() { + return b; + } + + public void setB(Optional b) { + this.b = b; + } + + public Optional getBb() { + return bb; + } + + public void setBb(Optional bb) { + this.bb = bb; + } + + public Optional getS() { + return s; + } + + public void setS(Optional s) { + this.s = s; + } + + public Optional getSs() { + return ss; + } + + public void setSs(Optional ss) { + this.ss = ss; + } + + public Optional getI() { + return i; + } + + public void setI(Optional i) { + this.i = i; + } + + public Optional getIi() { + return ii; + } + + public void setIi(Optional ii) { + this.ii = ii; + } + + public Optional getL() { + return l; + } + + public void setL(Optional l) { + this.l = l; + } + + public Optional getLl() { + return ll; + } + + public void setLl(Optional ll) { + this.ll = ll; + } + + public Optional getF() { + return f; + } + + public void setF(Optional f) { + this.f = f; + } + + public Optional getFf() { + return ff; + } + + public void setFf(Optional ff) { + this.ff = ff; + } + + public Optional getD() { + return d; + } + + public void setD(Optional d) { + this.d = d; + } + + public Optional getDd() { + return dd; + } + + public void setDd(Optional dd) { + this.dd = dd; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/OptionalBeforeAfterMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/OptionalBeforeAfterMapper.java new file mode 100644 index 0000000000..30dca2cf39 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/OptionalBeforeAfterMapper.java @@ -0,0 +1,76 @@ +/* + * 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.optional.beforeafter; + +import java.util.Optional; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.TargetType; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface OptionalBeforeAfterMapper { + + OptionalBeforeAfterMapper INSTANCE = Mappers.getMapper( OptionalBeforeAfterMapper.class ); + + Target toTarget(Source source); + + @BeforeMapping + default void beforeDeepOptionalSourceWithNoTargetType(Optional source) { + } + + @BeforeMapping + default void beforeDeepOptionalSourceWithNonOptionalTargetType(@TargetType Class targetType, + Optional source) { + } + + @AfterMapping + default void afterDeepOptionalSourceWithNoTarget(Optional source) { + + } + + @AfterMapping + default void afterDeepOptionalSourceWithNonOptionalTarget(@MappingTarget Target.SubType target, + Optional source) { + } + + @AfterMapping + default void afterDeepOptionalSourceWithOptionalTarget(@MappingTarget Optional target, + Optional source) { + } + + @AfterMapping + default void afterDeepNonOptionalSourceOptionalTarget(@MappingTarget Optional target, + Source.SubType source) { + } + + @BeforeMapping + default void beforeShallowOptionalSourceWithNoTargetType(Optional source) { + } + + @BeforeMapping + default void beforeShallowOptionalSourceWithNonOptionalTargetType(@TargetType Class targetType, + Optional source) { + } + + @AfterMapping + default void afterShallowOptionalSourceWithNoTarget(Optional source) { + + } + + @AfterMapping + default void afterShallowOptionalSourceWithNonOptionalTarget(@MappingTarget String target, + Optional source) { + } + + @AfterMapping + default void afterShallowNonOptionalSourceOptionalTarget(@MappingTarget Optional target, String source) { + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/OptionalBeforeAfterTest.java b/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/OptionalBeforeAfterTest.java new file mode 100644 index 0000000000..ba1a7b09d4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/OptionalBeforeAfterTest.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.ap.test.optional.beforeafter; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +@WithClasses({ + OptionalBeforeAfterMapper.class, Source.class, Target.class +}) +class OptionalBeforeAfterTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + void generatedCode() { + generatedSource.addComparisonToFixtureFor( OptionalBeforeAfterMapper.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/Source.java b/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/Source.java new file mode 100644 index 0000000000..1284bca7ea --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/Source.java @@ -0,0 +1,87 @@ +/* + * 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.optional.beforeafter; + +import java.util.Objects; +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class Source { + + private final Optional deepOptionalToOptional; + private final Optional deepOptionalToNonOptional; + private final SubType deepNonOptionalToOptional; + + private final Optional shallowOptionalToOptional; + private final Optional shallowOptionalToNonOptional; + private final String shallowNonOptionalToOptional; + + public Source(Optional deepOptionalToOptional, Optional deepOptionalToNonOptional, + SubType deepNonOptionalToOptional, Optional shallowOptionalToOptional, + Optional shallowOptionalToNonOptional, String shallowNonOptionalToOptional) { + this.deepOptionalToOptional = deepOptionalToOptional; + this.deepOptionalToNonOptional = deepOptionalToNonOptional; + this.deepNonOptionalToOptional = deepNonOptionalToOptional; + this.shallowOptionalToOptional = shallowOptionalToOptional; + this.shallowOptionalToNonOptional = shallowOptionalToNonOptional; + this.shallowNonOptionalToOptional = shallowNonOptionalToOptional; + } + + public Optional getDeepOptionalToOptional() { + return deepOptionalToOptional; + } + + public Optional getDeepOptionalToNonOptional() { + return deepOptionalToNonOptional; + } + + public SubType getDeepNonOptionalToOptional() { + return deepNonOptionalToOptional; + } + + public Optional getShallowOptionalToOptional() { + return shallowOptionalToOptional; + } + + public Optional getShallowOptionalToNonOptional() { + return shallowOptionalToNonOptional; + } + + public String getShallowNonOptionalToOptional() { + return shallowNonOptionalToOptional; + } + + public static class SubType { + + private final String value; + + public SubType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + SubType subType = (SubType) o; + return Objects.equals( value, subType.value ); + } + + @Override + public int hashCode() { + return Objects.hash( value ); + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/Target.java b/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/Target.java new file mode 100644 index 0000000000..592e3338fe --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/beforeafter/Target.java @@ -0,0 +1,86 @@ +/* + * 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.optional.beforeafter; + +import java.util.Objects; +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class Target { + + private final Optional deepOptionalToOptional; + private final SubType deepOptionalToNonOptional; + private final Optional deepNonOptionalToOptional; + + private final Optional shallowOptionalToOptional; + private final String shallowOptionalToNonOptional; + private final Optional shallowNonOptionalToOptional; + + public Target(Optional deepOptionalToOptional, SubType deepOptionalToNonOptional, + Optional deepNonOptionalToOptional, Optional shallowOptionalToOptional, + String shallowOptionalToNonOptional, Optional shallowNonOptionalToOptional) { + this.deepOptionalToOptional = deepOptionalToOptional; + this.deepOptionalToNonOptional = deepOptionalToNonOptional; + this.deepNonOptionalToOptional = deepNonOptionalToOptional; + this.shallowOptionalToOptional = shallowOptionalToOptional; + this.shallowOptionalToNonOptional = shallowOptionalToNonOptional; + this.shallowNonOptionalToOptional = shallowNonOptionalToOptional; + } + + public Optional getDeepOptionalToOptional() { + return deepOptionalToOptional; + } + + public SubType getDeepOptionalToNonOptional() { + return deepOptionalToNonOptional; + } + + public Optional getDeepNonOptionalToOptional() { + return deepNonOptionalToOptional; + } + + public Optional getShallowOptionalToOptional() { + return shallowOptionalToOptional; + } + + public String getShallowOptionalToNonOptional() { + return shallowOptionalToNonOptional; + } + + public Optional getShallowNonOptionalToOptional() { + return shallowNonOptionalToOptional; + } + + public static class SubType { + + private final String value; + + public SubType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + SubType subType = (SubType) o; + return Objects.equals( value, subType.value ); + } + + @Override + public int hashCode() { + return Objects.hash( value ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/builder/OptionalBuilderTest.java b/processor/src/test/java/org/mapstruct/ap/test/optional/builder/OptionalBuilderTest.java new file mode 100644 index 0000000000..9dd4b01256 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/builder/OptionalBuilderTest.java @@ -0,0 +1,31 @@ +/* + * 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.optional.builder; + +import java.util.Optional; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +class OptionalBuilderTest { + + @ProcessorTest + @WithClasses(SimpleOptionalBuilderMapper.class) + void simpleOptionalBuilder() { + Optional targetOpt = SimpleOptionalBuilderMapper.INSTANCE.map( null ); + assertThat( targetOpt ).isEmpty(); + + targetOpt = SimpleOptionalBuilderMapper.INSTANCE.map( new SimpleOptionalBuilderMapper.Source( "test" ) ); + assertThat( targetOpt ).isNotEmpty(); + SimpleOptionalBuilderMapper.Target target = targetOpt.get(); + assertThat( target.getValue() ).isEqualTo( "test" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/builder/SimpleOptionalBuilderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/builder/SimpleOptionalBuilderMapper.java new file mode 100644 index 0000000000..5f24aa0cae --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/builder/SimpleOptionalBuilderMapper.java @@ -0,0 +1,67 @@ +/* + * 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.optional.builder; + +import java.util.Optional; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface SimpleOptionalBuilderMapper { + + SimpleOptionalBuilderMapper INSTANCE = Mappers.getMapper( SimpleOptionalBuilderMapper.class ); + + Optional map(Source source); + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Target { + + private final String value; + + private Target(TargetBuilder builder) { + this.value = builder.value; + } + + public String getValue() { + return value; + } + + public static TargetBuilder builder() { + return new TargetBuilder(); + } + + } + + class TargetBuilder { + + private String value; + + public TargetBuilder value(String value) { + this.value = value; + return this; + } + + public Target build() { + return new Target(this); + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/custom/CustomOptionalMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/custom/CustomOptionalMapper.java new file mode 100644 index 0000000000..a6f8c155d4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/custom/CustomOptionalMapper.java @@ -0,0 +1,63 @@ +/* + * 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.optional.custom; + +import java.util.Optional; + +import org.mapstruct.Condition; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) +@SuppressWarnings("OptionalAssignedToNull") +public interface CustomOptionalMapper { + + CustomOptionalMapper INSTANCE = Mappers.getMapper( CustomOptionalMapper.class ); + + Target map(Source source); + + void update(@MappingTarget Target target, Source source); + + @Condition + default boolean isPresent(Optional optional) { + return optional != null; + } + + default E unwrapFromOptional(Optional optional) { + return optional == null + ? null + : optional.orElse( null ); + } + + class Target { + private String value = "initial"; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + class Source { + private final Optional value; + + public Source(Optional value) { + this.value = value; + } + + public Optional getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/custom/CustomOptionalTest.java b/processor/src/test/java/org/mapstruct/ap/test/optional/custom/CustomOptionalTest.java new file mode 100644 index 0000000000..51991ac478 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/custom/CustomOptionalTest.java @@ -0,0 +1,60 @@ +/* + * 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.optional.custom; + +import java.util.Optional; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses(CustomOptionalMapper.class) +class CustomOptionalTest { + + @ProcessorTest + void shouldUseCustomMethodsWhenMapping() { + CustomOptionalMapper.Target target = CustomOptionalMapper.INSTANCE. + map( new CustomOptionalMapper.Source( null ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "initial" ); + + target = CustomOptionalMapper.INSTANCE.map( new CustomOptionalMapper.Source( Optional.empty() ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isNull(); + + target = CustomOptionalMapper.INSTANCE.map( new CustomOptionalMapper.Source( Optional.of( "test" ) ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "test" ); + } + + @ProcessorTest + void shouldUseCustomMethodsWhenUpdating() { + CustomOptionalMapper.Target target = new CustomOptionalMapper.Target(); + + CustomOptionalMapper.INSTANCE.update( target, new CustomOptionalMapper.Source( null ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "initial" ); + + CustomOptionalMapper.INSTANCE.update( target, new CustomOptionalMapper.Source( Optional.empty() ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isNull(); + + target = new CustomOptionalMapper.Target(); + CustomOptionalMapper.INSTANCE.update( target, new CustomOptionalMapper.Source( Optional.of( "test" ) ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "test" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/OptionalDifferentTypesMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/OptionalDifferentTypesMapper.java new file mode 100644 index 0000000000..5a276b4745 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/OptionalDifferentTypesMapper.java @@ -0,0 +1,18 @@ +/* + * 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.optional.differenttypes; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface OptionalDifferentTypesMapper { + + OptionalDifferentTypesMapper INSTANCE = Mappers.getMapper( OptionalDifferentTypesMapper.class ); + + Target toTarget(Source source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/OptionalDifferentTypesTest.java b/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/OptionalDifferentTypesTest.java new file mode 100644 index 0000000000..74ab85e174 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/OptionalDifferentTypesTest.java @@ -0,0 +1,204 @@ +/* + * 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.optional.differenttypes; + +import java.util.Optional; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +@WithClasses({ + OptionalDifferentTypesMapper.class, Source.class, Target.class +}) +class OptionalDifferentTypesTest { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + void generatedCode() { + generatedSource.addComparisonToFixtureFor( OptionalDifferentTypesMapper.class ); + } + + @ProcessorTest + void constructorOptionalToOptionalWhenPresent() { + Source source = new Source( Optional.of( new Source.SubType( "some value" ) ), Optional.empty(), null ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getConstructorOptionalToOptional() ) + .hasValueSatisfying( subType -> + assertThat( subType.getValue() ).isEqualTo( "some value" ) ); + } + + @ProcessorTest + void constructorOptionalToOptionalWhenEmpty() { + Source source = new Source(); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getConstructorOptionalToOptional() ).isEmpty(); + } + + @ProcessorTest + void constructorOptionalToNonOptionalWhenPresent() { + Source source = new Source( Optional.empty(), Optional.of( new Source.SubType( "some value" ) ), null ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + Target.SubType subType = target.getConstructorOptionalToNonOptional(); + assertThat( subType ).isNotNull(); + assertThat( subType.getValue() ).isEqualTo( "some value" ); + } + + @ProcessorTest + void constructorOptionalToNonOptionalWhenEmpty() { + Source source = new Source(); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getConstructorOptionalToNonOptional() ).isNull(); + } + + @ProcessorTest + void constructorNonOptionalToOptionalWhenNotNull() { + Source source = new Source( Optional.empty(), Optional.empty(), new Source.SubType( "some value" ) ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getConstructorNonOptionalToOptional() ) + .hasValueSatisfying( subType -> + assertThat( subType.getValue() ).isEqualTo( "some value" ) ); + } + + @ProcessorTest + void constructorNonOptionalToOptionalWhenNull() { + Source source = new Source(); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getConstructorNonOptionalToOptional() ).isEmpty(); + } + + @ProcessorTest + void optionalToOptionalWhenPresent() { + Source source = new Source(); + source.setOptionalToOptional( Optional.of( new Source.SubType( "some value" ) ) ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getOptionalToOptional() ) + .hasValueSatisfying( subType -> + assertThat( subType.getValue() ).isEqualTo( "some value" ) ); + } + + @ProcessorTest + void optionalToOptionalWhenEmpty() { + Source source = new Source(); + source.setOptionalToOptional( Optional.empty() ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getOptionalToOptional() ).isEmpty(); + } + + @ProcessorTest + void optionalToNonOptionalWhenPresent() { + Source source = new Source(); + source.setOptionalToNonOptional( Optional.of( new Source.SubType( "some value" ) ) ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + Target.SubType subType = target.getOptionalToNonOptional(); + assertThat( subType ).isNotNull(); + assertThat( subType.getValue() ).isEqualTo( "some value" ); + } + + @ProcessorTest + void optionalToNonOptionalWhenEmpty() { + Source source = new Source(); + source.setOptionalToNonOptional( Optional.empty() ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getOptionalToNonOptional() ).isNull(); + } + + @ProcessorTest + void nonOptionalToOptionalWhenNotNull() { + Source source = new Source(); + source.setNonOptionalToOptional( new Source.SubType( "some value" ) ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getNonOptionalToOptional() ) + .hasValueSatisfying( subType -> + assertThat( subType.getValue() ).isEqualTo( "some value" ) ); + } + + @ProcessorTest + void nonOptionalToOptionalWhenNull() { + Source source = new Source(); + source.setNonOptionalToOptional( null ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.getNonOptionalToOptional() ).isEmpty(); + } + + @ProcessorTest + void publicOptionalToOptionalWhenPresent() { + Source source = new Source(); + source.publicOptionalToOptional = Optional.of( new Source.SubType( "some value" ) ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.publicOptionalToOptional ) + .hasValueSatisfying( subType -> + assertThat( subType.getValue() ).isEqualTo( "some value" ) ); + } + + @ProcessorTest + void publicOptionalToOptionalWhenEmpty() { + Source source = new Source(); + source.publicOptionalToOptional = Optional.empty(); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.publicOptionalToOptional ).isEmpty(); + } + + @ProcessorTest + void publicOptionalToNonOptionalWhenPresent() { + Source source = new Source(); + source.publicOptionalToNonOptional = Optional.of( new Source.SubType( "some value" ) ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + Target.SubType subType = target.publicOptionalToNonOptional; + assertThat( subType ).isNotNull(); + assertThat( subType.getValue() ).isEqualTo( "some value" ); + } + + @ProcessorTest + void publicOptionalToNonOptionalWhenEmpty() { + Source source = new Source(); + source.publicOptionalToNonOptional = Optional.empty(); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.publicOptionalToNonOptional ).isNull(); + } + + @ProcessorTest + void publicNonOptionalToOptionalWhenNotNull() { + Source source = new Source(); + source.publicNonOptionalToOptional = new Source.SubType( "some value" ); + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.publicNonOptionalToOptional ) + .hasValueSatisfying( subType -> + assertThat( subType.getValue() ).isEqualTo( "some value" ) ); + } + + @ProcessorTest + void publicNonOptionalToOptionalWhenNull() { + Source source = new Source(); + source.publicNonOptionalToOptional = null; + + Target target = OptionalDifferentTypesMapper.INSTANCE.toTarget( source ); + assertThat( target.publicNonOptionalToOptional ).isEmpty(); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/Source.java b/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/Source.java new file mode 100644 index 0000000000..0c0648000a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/Source.java @@ -0,0 +1,89 @@ +/* + * 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.optional.differenttypes; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class Source { + + private final Optional constructorOptionalToOptional; + private final Optional constructorOptionalToNonOptional; + private final SubType constructorNonOptionalToOptional; + + private Optional optionalToOptional = Optional.empty(); + private Optional optionalToNonOptional = Optional.empty(); + private SubType nonOptionalToOptional; + + @SuppressWarnings( "VisibilityModifier" ) + public Optional publicOptionalToOptional = Optional.empty(); + @SuppressWarnings( "VisibilityModifier" ) + public Optional publicOptionalToNonOptional = Optional.empty(); + @SuppressWarnings( "VisibilityModifier" ) + public SubType publicNonOptionalToOptional; + + public Source() { + this( Optional.empty(), Optional.empty(), null ); + } + + public Source(Optional constructorOptionalToOptional, Optional constructorOptionalToNonOptional, + SubType constructorNonOptionalToOptional) { + this.constructorOptionalToOptional = constructorOptionalToOptional; + this.constructorOptionalToNonOptional = constructorOptionalToNonOptional; + this.constructorNonOptionalToOptional = constructorNonOptionalToOptional; + } + + public Optional getConstructorOptionalToOptional() { + return constructorOptionalToOptional; + } + + public Optional getConstructorOptionalToNonOptional() { + return constructorOptionalToNonOptional; + } + + public SubType getConstructorNonOptionalToOptional() { + return constructorNonOptionalToOptional; + } + + public Optional getOptionalToOptional() { + return optionalToOptional; + } + + public void setOptionalToOptional(Optional optionalToOptional) { + this.optionalToOptional = optionalToOptional; + } + + public Optional getOptionalToNonOptional() { + return optionalToNonOptional; + } + + public void setOptionalToNonOptional(Optional optionalToNonOptional) { + this.optionalToNonOptional = optionalToNonOptional; + } + + public SubType getNonOptionalToOptional() { + return nonOptionalToOptional; + } + + public void setNonOptionalToOptional(SubType nonOptionalToOptional) { + this.nonOptionalToOptional = nonOptionalToOptional; + } + + public static class SubType { + + private final String value; + + public SubType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/Target.java b/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/Target.java new file mode 100644 index 0000000000..4f2b21c8a7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/differenttypes/Target.java @@ -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.optional.differenttypes; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class Target { + + private final Optional constructorOptionalToOptional; + private final SubType constructorOptionalToNonOptional; + private final Optional constructorNonOptionalToOptional; + + private Optional optionalToOptional = Optional.of( new SubType( "initial" ) ); + private SubType optionalToNonOptional; + private Optional nonOptionalToOptional = Optional.of( new SubType( "initial" ) ); + + @SuppressWarnings( "VisibilityModifier" ) + public Optional publicOptionalToOptional = Optional.of( new SubType( "initial" ) ); + @SuppressWarnings( "VisibilityModifier" ) + public SubType publicOptionalToNonOptional; + @SuppressWarnings( "VisibilityModifier" ) + public Optional publicNonOptionalToOptional = Optional.of( new SubType( "initial" ) ); + + public Target(Optional constructorOptionalToOptional, SubType constructorOptionalToNonOptional, + Optional constructorNonOptionalToOptional) { + this.constructorOptionalToOptional = constructorOptionalToOptional; + this.constructorOptionalToNonOptional = constructorOptionalToNonOptional; + this.constructorNonOptionalToOptional = constructorNonOptionalToOptional; + } + + public Optional getConstructorOptionalToOptional() { + return constructorOptionalToOptional; + } + + public SubType getConstructorOptionalToNonOptional() { + return constructorOptionalToNonOptional; + } + + public Optional getConstructorNonOptionalToOptional() { + return constructorNonOptionalToOptional; + } + + public Optional getOptionalToOptional() { + return optionalToOptional; + } + + public void setOptionalToOptional(Optional optionalToOptional) { + this.optionalToOptional = optionalToOptional; + } + + public SubType getOptionalToNonOptional() { + return optionalToNonOptional; + } + + public void setOptionalToNonOptional(SubType optionalToNonOptional) { + this.optionalToNonOptional = optionalToNonOptional; + } + + public Optional getNonOptionalToOptional() { + return nonOptionalToOptional; + } + + public void setNonOptionalToOptional(Optional nonOptionalToOptional) { + this.nonOptionalToOptional = nonOptionalToOptional; + } + + public static class SubType { + + private final String value; + + public SubType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/MappingContext.java b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/MappingContext.java new file mode 100644 index 0000000000..3624efb00e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/MappingContext.java @@ -0,0 +1,108 @@ +/* + * 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.optional.lifecycle; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.mapstruct.AfterMapping; +import org.mapstruct.BeforeMapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.TargetType; + +/** + * @author Filip Hrisafov + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class MappingContext { + + private final List invokedMethods = new ArrayList<>(); + + @BeforeMapping + public void beforeWithoutParameters() { + invokedMethods.add( "beforeWithoutParameters" ); + } + + @BeforeMapping + public void beforeWithOptionalSource(Optional source) { + invokedMethods.add( "beforeWithOptionalSource" ); + } + + @BeforeMapping + public void beforeWithSource(Source source) { + invokedMethods.add( "beforeWithSource" ); + } + + @BeforeMapping + public void beforeWithTargetType(@TargetType Class targetClass) { + invokedMethods.add( "beforeWithTargetType" ); + } + + @BeforeMapping + public void beforeWithBuilderTargetType(@TargetType Class targetBuilderClass) { + invokedMethods.add( "beforeWithBuilderTargetType" ); + } + + @BeforeMapping + public void beforeWithOptionalTarget(@MappingTarget Optional target) { + invokedMethods.add( "beforeWithOptionalTarget" ); + } + + @BeforeMapping + public void beforeWithTarget(@MappingTarget Target target) { + invokedMethods.add( "beforeWithTarget" ); + } + + @BeforeMapping + public void beforeWithTargetBuilder(@MappingTarget Target.Builder target) { + invokedMethods.add( "beforeWithTargetBuilder" ); + } + + @AfterMapping + public void afterWithoutParameters() { + invokedMethods.add( "afterWithoutParameters" ); + } + + @AfterMapping + public void afterWithOptionalSource(Optional source) { + invokedMethods.add( "afterWithOptionalSource" ); + } + + @AfterMapping + public void afterWithSource(Source source) { + invokedMethods.add( "afterWithSource" ); + } + + @AfterMapping + public void afterWithTargetType(@TargetType Class targetClass) { + invokedMethods.add( "afterWithTargetType" ); + } + + @AfterMapping + public void afterWithBuilderTargetType(@TargetType Class targetClass) { + invokedMethods.add( "afterWithBuilderTargetType" ); + } + + @AfterMapping + public void afterWithTarget(@MappingTarget Target target) { + invokedMethods.add( "afterWithTarget" ); + } + + @AfterMapping + public void afterWithTargetBuilder(@MappingTarget Target.Builder target) { + invokedMethods.add( "afterWithTargetBuilder" ); + } + + @AfterMapping + public void afterWithOptionalTarget(@MappingTarget Optional target) { + invokedMethods.add( "afterWithOptionalTarget" ); + } + + public List getInvokedMethods() { + return invokedMethods; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalLifecycleTest.java b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalLifecycleTest.java new file mode 100644 index 0000000000..c80dfcb6b6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalLifecycleTest.java @@ -0,0 +1,165 @@ +/* + * 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.optional.lifecycle; + +import java.util.Optional; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@WithClasses({ + MappingContext.class, + Source.class, + Target.class, +}) +public class OptionalLifecycleTest { + + @ProcessorTest + @WithClasses(OptionalToOptionalMapper.class) + void optionalToOptional() { + MappingContext context = new MappingContext(); + + OptionalToOptionalMapper.INSTANCE.map( Optional.empty(), context ); + + assertThat( context.getInvokedMethods() ) + .containsExactlyInAnyOrder( + "beforeWithoutParameters", + "beforeWithOptionalSource", + "beforeWithTargetType" + ); + + context = new MappingContext(); + OptionalToOptionalMapper.INSTANCE.map( Optional.of( new Source() ), context ); + + assertThat( context.getInvokedMethods() ) + .containsExactlyInAnyOrder( + "beforeWithoutParameters", + "beforeWithOptionalSource", + "beforeWithTargetType", + "beforeWithTarget", + "afterWithoutParameters", + "afterWithOptionalSource", + "afterWithTargetType", + "afterWithTarget", + "afterWithOptionalTarget" + ); + } + + @ProcessorTest + @WithClasses(OptionalToOptionalWithBuilderMapper.class) + void optionalToOptionalWithBuilder() { + MappingContext context = new MappingContext(); + + OptionalToOptionalWithBuilderMapper.INSTANCE.map( Optional.empty(), context ); + + assertThat( context.getInvokedMethods() ) + .containsExactlyInAnyOrder( + "beforeWithoutParameters", + "beforeWithOptionalSource", + "beforeWithTargetType", + "beforeWithBuilderTargetType" + ); + + context = new MappingContext(); + OptionalToOptionalWithBuilderMapper.INSTANCE.map( Optional.of( new Source() ), context ); + + assertThat( context.getInvokedMethods() ) + .containsExactlyInAnyOrder( + "beforeWithoutParameters", + "beforeWithOptionalSource", + "beforeWithTargetType", + "beforeWithBuilderTargetType", + "beforeWithTargetBuilder", + "afterWithoutParameters", + "afterWithOptionalSource", + "afterWithTargetType", + "afterWithBuilderTargetType", + "afterWithTarget", + "afterWithTargetBuilder", + "afterWithOptionalTarget" + ); + } + + @ProcessorTest + @WithClasses(OptionalToTypeMapper.class) + void optionalToType() { + MappingContext context = new MappingContext(); + + OptionalToTypeMapper.INSTANCE.map( Optional.empty(), context ); + + assertThat( context.getInvokedMethods() ) + .containsExactlyInAnyOrder( + "beforeWithoutParameters", + "beforeWithOptionalSource", + "beforeWithTargetType" + ); + + context = new MappingContext(); + OptionalToTypeMapper.INSTANCE.map( Optional.of( new Source() ), context ); + + assertThat( context.getInvokedMethods() ) + .containsExactlyInAnyOrder( + "beforeWithoutParameters", + "beforeWithOptionalSource", + "beforeWithTargetType", + "beforeWithTarget", + "afterWithoutParameters", + "afterWithOptionalSource", + "afterWithTargetType", + "afterWithTarget" + ); + } + + @ProcessorTest + @WithClasses(OptionalToTypeMultiSourceMapper.class) + void optionalToTypeMultiSource() { + MappingContext context = new MappingContext(); + + OptionalToTypeMultiSourceMapper.INSTANCE.map( Optional.empty(), null, context ); + + assertThat( context.getInvokedMethods() ) + .containsExactlyInAnyOrder( + "beforeWithoutParameters", + "beforeWithOptionalSource", + "beforeWithTargetType" + ); + + context = new MappingContext(); + OptionalToTypeMultiSourceMapper.INSTANCE.map( Optional.of( new Source() ), null, context ); + + assertThat( context.getInvokedMethods() ) + .containsExactlyInAnyOrder( + "beforeWithoutParameters", + "beforeWithOptionalSource", + "beforeWithTargetType", + "beforeWithTarget", + "afterWithoutParameters", + "afterWithOptionalSource", + "afterWithTargetType", + "afterWithTarget" + ); + + context = new MappingContext(); + OptionalToTypeMultiSourceMapper.INSTANCE.map( Optional.empty(), "Test", context ); + + assertThat( context.getInvokedMethods() ) + .containsExactlyInAnyOrder( + "beforeWithoutParameters", + "beforeWithOptionalSource", + "beforeWithTargetType", + "beforeWithTarget", + "afterWithoutParameters", + "afterWithOptionalSource", + "afterWithTargetType", + "afterWithTarget" + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToOptionalMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToOptionalMapper.java new file mode 100644 index 0000000000..d7c5d08173 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToOptionalMapper.java @@ -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.optional.lifecycle; + +import java.util.Optional; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface OptionalToOptionalMapper { + + OptionalToOptionalMapper INSTANCE = Mappers.getMapper( OptionalToOptionalMapper.class ); + + @BeanMapping(builder = @Builder(disableBuilder = true)) + Optional map(Optional source, @Context MappingContext context); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToOptionalWithBuilderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToOptionalWithBuilderMapper.java new file mode 100644 index 0000000000..8cfab7fd00 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToOptionalWithBuilderMapper.java @@ -0,0 +1,24 @@ +/* + * 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.optional.lifecycle; + +import java.util.Optional; + +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface OptionalToOptionalWithBuilderMapper { + + OptionalToOptionalWithBuilderMapper INSTANCE = Mappers.getMapper( OptionalToOptionalWithBuilderMapper.class ); + + Optional map(Optional source, @Context MappingContext context); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToTypeMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToTypeMapper.java new file mode 100644 index 0000000000..6a6f3e096e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToTypeMapper.java @@ -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.optional.lifecycle; + +import java.util.Optional; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface OptionalToTypeMapper { + + OptionalToTypeMapper INSTANCE = Mappers.getMapper( OptionalToTypeMapper.class ); + + @BeanMapping(builder = @Builder(disableBuilder = true)) + Target map(Optional source, @Context MappingContext context); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToTypeMultiSourceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToTypeMultiSourceMapper.java new file mode 100644 index 0000000000..d672f2ec05 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/OptionalToTypeMultiSourceMapper.java @@ -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.optional.lifecycle; + +import java.util.Optional; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface OptionalToTypeMultiSourceMapper { + + OptionalToTypeMultiSourceMapper INSTANCE = Mappers.getMapper( OptionalToTypeMultiSourceMapper.class ); + + @BeanMapping(builder = @Builder(disableBuilder = true)) + Target map(Optional source, String otherValue, @Context MappingContext context); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/Source.java b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/Source.java new file mode 100644 index 0000000000..c7005c6ca6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/Source.java @@ -0,0 +1,21 @@ +/* + * 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.optional.lifecycle; + +/** + * @author Filip Hrisafov + */ +public class Source { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/Target.java b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/Target.java new file mode 100644 index 0000000000..d5f68a1844 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/lifecycle/Target.java @@ -0,0 +1,39 @@ +/* + * 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.optional.lifecycle; + +/** + * @author Filip Hrisafov + */ +public class Target { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public static class Builder { + + private String value; + + public Builder value(String value) { + this.value = value; + return this; + } + + public Target build() { + Target target = new Target(); + target.setValue( value ); + return target; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/optional/nested/Artist.java b/processor/src/test/java/org/mapstruct/ap/test/optional/nested/Artist.java new file mode 100644 index 0000000000..e6ce6faa65 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/optional/nested/Artist.java @@ -0,0 +1,81 @@ +/* + * 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.optional.nested; + +import java.util.Optional; + +/** + * @author Filip Hrisafov + */ +public class Artist { + private final String name; + private final Label label; + + public Artist(String name, Label label) { + this.name = name; + this.label = label; + } + + public boolean hasName() { + return name != null; + } + + public String getName() { + return name; + } + + public boolean hasLabel() { + return label != null; + } + + public Optional
      *

      @@ -446,13 +445,11 @@ * If not possible, MapStruct will try to apply a user defined mapping method. * * - *

      * *

    18. other *

      * MapStruct handles the constant as {@code String}. The value will be converted by applying a matching method, * type conversion method or built-in conversion. - *

      *

    19. * *

      diff --git a/core/src/main/java/org/mapstruct/SubclassMapping.java b/core/src/main/java/org/mapstruct/SubclassMapping.java index f5463b9026..4d635d8aa3 100644 --- a/core/src/main/java/org/mapstruct/SubclassMapping.java +++ b/core/src/main/java/org/mapstruct/SubclassMapping.java @@ -73,12 +73,17 @@ @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Experimental public @interface SubclassMapping { + /** + * The source subclass to check for before using the default mapping as fallback. + * * @return the source subclass to check for before using the default mapping as fallback. */ Class source(); /** + * The target subclass to map the source to. + * * @return the target subclass to map the source to. */ Class target(); diff --git a/core/src/main/java/org/mapstruct/SubclassMappings.java b/core/src/main/java/org/mapstruct/SubclassMappings.java index 2ddafaf516..d6aac264d4 100644 --- a/core/src/main/java/org/mapstruct/SubclassMappings.java +++ b/core/src/main/java/org/mapstruct/SubclassMappings.java @@ -51,6 +51,8 @@ public @interface SubclassMappings { /** + * The subclassMappings that should be applied. + * * @return the subclassMappings to apply. */ SubclassMapping[] value(); diff --git a/core/src/main/java/org/mapstruct/ValueMappings.java b/core/src/main/java/org/mapstruct/ValueMappings.java index bb593f53d1..95d4c7d5ce 100644 --- a/core/src/main/java/org/mapstruct/ValueMappings.java +++ b/core/src/main/java/org/mapstruct/ValueMappings.java @@ -44,6 +44,11 @@ @Retention(RetentionPolicy.CLASS) public @interface ValueMappings { + /** + * The value mappings that should be applied. + * + * @return the value mappings + */ ValueMapping[] value(); } diff --git a/core/src/main/java/org/mapstruct/control/MappingControl.java b/core/src/main/java/org/mapstruct/control/MappingControl.java index c44797602a..1cb5bf2bb1 100644 --- a/core/src/main/java/org/mapstruct/control/MappingControl.java +++ b/core/src/main/java/org/mapstruct/control/MappingControl.java @@ -99,8 +99,16 @@ @MappingControl( MappingControl.Use.COMPLEX_MAPPING ) public @interface MappingControl { + /** + * The type of mapping control that should be used. + * + * @return What should be used for the mapping control + */ Use value(); + /** + * Defines the options that can be used for the mapping control. + */ enum Use { /** diff --git a/core/src/main/java/org/mapstruct/control/MappingControls.java b/core/src/main/java/org/mapstruct/control/MappingControls.java index a3efef661f..c1663e551e 100644 --- a/core/src/main/java/org/mapstruct/control/MappingControls.java +++ b/core/src/main/java/org/mapstruct/control/MappingControls.java @@ -21,5 +21,10 @@ @Target(ElementType.ANNOTATION_TYPE) public @interface MappingControls { + /** + * The mapping controls that should be applied to the annotated class. + * + * @return The mapping controls that should be applied to the annotated class. + */ MappingControl[] value(); } diff --git a/core/src/main/java/org/mapstruct/util/Experimental.java b/core/src/main/java/org/mapstruct/util/Experimental.java index a945a87d7c..5fd5eb3b5c 100644 --- a/core/src/main/java/org/mapstruct/util/Experimental.java +++ b/core/src/main/java/org/mapstruct/util/Experimental.java @@ -17,5 +17,11 @@ @Documented @Retention(RetentionPolicy.SOURCE) public @interface Experimental { + + /** + * The reason why the feature is considered experimental. + * + * @return the reason why the feature is considered experimental. + */ String value() default ""; } diff --git a/processor/pom.xml b/processor/pom.xml index 01b8cbe263..622ba924ee 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -172,6 +172,13 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + + all,-missing + + org.apache.maven.plugins maven-surefire-plugin diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java index 31d4d27585..b392281bf4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java @@ -46,6 +46,8 @@ public interface ConversionProvider { Assignment from(ConversionContext conversionContext); /** + * Retrieves any helper methods required for creating the conversion. + * * @param conversionContext ConversionContext providing optional information required for creating the conversion. * * @return any helper methods when required. @@ -53,6 +55,8 @@ public interface ConversionProvider { List getRequiredHelperMethods(ConversionContext conversionContext); /** + * Retrieves any fields required for creating the conversion. + * * @param conversionContext ConversionContext providing optional information required for creating the conversion. * * @return any fields when required. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CurrencyToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CurrencyToStringConversion.java index 99797f898c..3f32994734 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/CurrencyToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/CurrencyToStringConversion.java @@ -15,6 +15,8 @@ import static org.mapstruct.ap.internal.conversion.ConversionUtils.currency; /** + * Conversion between {@link Currency} and {@link String}. + * * @author Darren Rambaud */ public class CurrencyToStringConversion extends SimpleConversion { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/OptionalWrapperConversionProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/OptionalWrapperConversionProvider.java index 2b3e1bd960..db9480662d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/OptionalWrapperConversionProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/OptionalWrapperConversionProvider.java @@ -17,6 +17,9 @@ import org.mapstruct.ap.internal.model.common.TypeFactory; /** + * A conversion provider that wraps / unwraps the underlying conversion in Optional. + * e.g., For conversion from {@code Optional} to {@code Integer}. + * * @author Filip Hrisafov */ public class OptionalWrapperConversionProvider implements ConversionProvider { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/TypeToOptionalConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/TypeToOptionalConversion.java index ad488d1a8f..dbab6bc4ca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/TypeToOptionalConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/TypeToOptionalConversion.java @@ -15,6 +15,8 @@ import static org.mapstruct.ap.internal.conversion.ReverseConversion.inverse; /** + * Conversion between {@link java.util.Optional Optional} and its base type. + * * @author Filip Hrisafov */ public class TypeToOptionalConversion extends SimpleConversion { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index 3b6e3c1686..80b900bb57 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -19,6 +19,8 @@ /** * An abstract builder that can be reused for building {@link MappingMethod}(s). * + * @param the builder itself that needs to be used for chaining + * @param the method that the builder builds * @author Filip Hrisafov */ public abstract class AbstractMappingMethodBuilder, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java index 3f0b2123ae..cb9289e057 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AdditionalAnnotationsBuilder.java @@ -50,6 +50,8 @@ import static javax.lang.model.util.ElementFilter.methodsIn; /** + * Helper class which is responsible for collecting all additional annotations that should be added. + * * @author Ben Zegveld * @since 1.5 */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedSetter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedSetter.java index 5614376ea4..8f60b052b9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedSetter.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AnnotatedSetter.java @@ -12,6 +12,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * A method in a generated type that represents a setter with annotations. + * * @author Lucas Resch */ public class AnnotatedSetter extends GeneratedTypeMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java index 9b7912f752..9b0507f399 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BuilderFinisherMethodResolver.java @@ -19,6 +19,8 @@ import static org.mapstruct.ap.internal.util.Collections.first; /** + * Factory for creating the appropriate builder finisher method. + * * @author Filip Hrisafov */ public class BuilderFinisherMethodResolver { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.java index f8c596a368..e1c320e16b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/FromOptionalTypeConversion.java @@ -15,6 +15,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * An inline conversion from an optional source to it's value. + * * @author Filip Hrisafov */ public class FromOptionalTypeConversion extends ModelElement implements Assignment { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedTypeMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedTypeMethod.java index f02cc16b2c..8d6a999665 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedTypeMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedTypeMethod.java @@ -8,6 +8,8 @@ import org.mapstruct.ap.internal.model.common.ModelElement; /** + * Base class for methods available in a generated type. + * * @author Filip Hrisafov */ public abstract class GeneratedTypeMethod extends ModelElement { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java index 879a76efbe..29904ff416 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.java @@ -13,6 +13,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * A {@link PresenceCheck} that is based on a {@link MethodReference}. + * * @author Filip Hrisafov */ public class MethodReferencePresenceCheck extends ModelElement implements PresenceCheck { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index 89f295981f..8b526b9177 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -26,6 +26,7 @@ import static org.mapstruct.ap.internal.util.Collections.first; /** + * Factory for creating the appropriate object factory method. * * @author Sjaak Derksen */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java index 784342b8d7..2de61089ca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java @@ -25,6 +25,8 @@ import org.mapstruct.ap.internal.util.Message; /** + * Factory for creating {@link PresenceCheck}s. + * * @author Filip Hrisafov */ public final class PresenceCheckMethodResolver { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ServicesEntry.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ServicesEntry.java index 727518dad8..0d97c1a523 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ServicesEntry.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ServicesEntry.java @@ -12,6 +12,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * Represents a service entry for the service loader file. + * * @author Christophe Labouisse on 14/07/2015. */ public class ServicesEntry extends ModelElement { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.java index 5925540e10..45bbc9d4b0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ToOptionalTypeConversion.java @@ -15,6 +15,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * An inline conversion from a source to an optional of the source. + * * @author Filip Hrisafov */ public class ToOptionalTypeConversion extends ModelElement implements Assignment { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/AnnotationElement.java b/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/AnnotationElement.java index 6ecde886bf..f242b51f47 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/AnnotationElement.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/annotation/AnnotationElement.java @@ -15,6 +15,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * Represents an annotation element. + * * @author Ben Zegveld */ public class AnnotationElement extends ModelElement { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.java index a334312007..eaa2fe68ff 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.java @@ -12,6 +12,7 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * Decorates the assignment as an {@link Enum} constant access. * * @author Sjaak Derksen */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.java index 9064640bb4..6c405c871d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/OptionalGetWrapper.java @@ -10,6 +10,8 @@ import org.mapstruct.ap.internal.util.Strings; /** + * Decorates the assignment as an {@link java.util.Optional#get()} call. + * * @author Filip Hrisafov */ public class OptionalGetWrapper extends AssignmentWrapper { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java index b94bb82efd..9300f683ce 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java @@ -14,6 +14,9 @@ import org.mapstruct.ap.spi.BuilderInfo; /** + * Represents the information about a builder. + * How it can be constructed, the type it is building etc. + * * @author Filip Hrisafov */ public class BuilderType { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/FormattingParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/FormattingParameters.java index 48cee4bbe5..7ef818630a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/FormattingParameters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/FormattingParameters.java @@ -10,6 +10,7 @@ import javax.lang.model.element.Element; /** + * Represent information about the configured formatting. * * @author Sjaak Derksen */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.java index f8844edcdd..78357affb8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/NegatePresenceCheck.java @@ -9,6 +9,8 @@ import java.util.Set; /** + * A {@link PresenceCheck} that negates the result of another presence check. + * * @author Filip Hrisafov */ public class NegatePresenceCheck extends ModelElement implements PresenceCheck { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java index 6bb191214f..d71f67a2a4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AllPresenceChecksPresenceCheck.java @@ -16,6 +16,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * A {@link PresenceCheck} that checks if all the given presence checks are present. + * * @author Filip Hrisafov */ public class AllPresenceChecksPresenceCheck extends ModelElement implements PresenceCheck { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.java index 0b5770e42f..5f8fac9623 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/AnyPresenceChecksPresenceCheck.java @@ -16,6 +16,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * A {@link PresenceCheck} that checks if any of the given presence checks are present. + * * @author Filip Hrisafov */ public class AnyPresenceChecksPresenceCheck extends ModelElement implements PresenceCheck { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java index e100be7e79..0c7c132023 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/JavaExpressionPresenceCheck.java @@ -15,6 +15,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * A {@link PresenceCheck} that calls a Java expression. + * * @author Filip Hrisafov */ public class JavaExpressionPresenceCheck extends ModelElement implements PresenceCheck { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java index 3496486e1d..2da4ae4023 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/NullPresenceCheck.java @@ -14,6 +14,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * A presence check that checks if the source reference is null. + * * @author Filip Hrisafov */ public class NullPresenceCheck extends ModelElement implements PresenceCheck { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java index 0f75fbba69..d35e89fce2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/presence/SuffixPresenceCheck.java @@ -15,6 +15,8 @@ import org.mapstruct.ap.internal.model.common.Type; /** + * A {@link PresenceCheck} that calls the suffix on the source reference. + * * @author Filip Hrisafov */ public class SuffixPresenceCheck extends ModelElement implements PresenceCheck { 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 936d049af8..83e2bf2819 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 @@ -23,6 +23,8 @@ import org.mapstruct.ap.internal.util.Message; /** + * Represents a condition configuration as configured via {@code @Condition}. + * * @author Filip Hrisafov */ public class ConditionOptions { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java index 85734c3083..41b74aa13a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/EnumMappingOptions.java @@ -21,6 +21,8 @@ import static org.mapstruct.ap.internal.util.Message.ENUMMAPPING_NO_ELEMENTS; /** + * Represents an enum mapping as configured via {@code @EnumMapping}. + * * @author Filip Hrisafov */ public class EnumMappingOptions extends DelegatingOptions { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java index 50ea5df6e0..7d0ab3a7ec 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodUtils.java @@ -10,6 +10,8 @@ import static org.mapstruct.ap.internal.util.Collections.first; /** + * Utility class for mapping methods. + * * @author Filip Hrisafov */ public final class MappingMethodUtils { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java index e2ccc3f476..df9c8e0b0c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/AbstractToXmlGregorianCalendar.java @@ -17,6 +17,8 @@ import org.mapstruct.ap.internal.util.XmlConstants; /** + * Base class for built-in methods for converting from a particular type to {@code XMLGregorianCalendar}. + * * @author Sjaak Derksen */ public abstract class AbstractToXmlGregorianCalendar extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/CalendarToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/CalendarToXmlGregorianCalendar.java index c8cd89169b..3461538c41 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/CalendarToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/CalendarToXmlGregorianCalendar.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@link Calendar} to {@code XMLGregorianCalendar}. + * * @author Sjaak Derksen */ public class CalendarToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/DateToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/DateToXmlGregorianCalendar.java index 036edd83a2..fb556e68a1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/DateToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/DateToXmlGregorianCalendar.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@link Date} to {@code XMLGregorianCalendar}. + * * @author Sjaak Derksen */ public class DateToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaDateTimeToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaDateTimeToXmlGregorianCalendar.java index d0b06300c4..ef57e03175 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaDateTimeToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaDateTimeToXmlGregorianCalendar.java @@ -13,6 +13,8 @@ import org.mapstruct.ap.internal.util.JodaTimeConstants; /** + * A built-in method for converting from Joda {@code DateTime} to {@code XMLGregorianCalendar}. + * * @author Sjaak Derksen */ public class JodaDateTimeToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateTimeToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateTimeToXmlGregorianCalendar.java index 80fa9ba326..285ebb29fe 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateTimeToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateTimeToXmlGregorianCalendar.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from Joda {@code LocalDateTime} to {@code XMLGregorianCalendar}. + * * @author Sjaak Derksen */ public class JodaLocalDateTimeToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateToXmlGregorianCalendar.java index 37c89ef360..8163aa2fb2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalDateToXmlGregorianCalendar.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from Joda {@code LocalDate} to {@code XMLGregorianCalendar}. + * * @author Sjaak Derksen */ public class JodaLocalDateToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalTimeToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalTimeToXmlGregorianCalendar.java index b29c7aec54..4fca4e0ed3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalTimeToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/JodaLocalTimeToXmlGregorianCalendar.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from Joda {@code LocalTime} to {@code XMLGregorianCalendar}. + * * @author Sjaak Derksen */ public class JodaLocalTimeToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateTimeToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateTimeToXmlGregorianCalendar.java index 178a77b5fa..b52c7fb586 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateTimeToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateTimeToXmlGregorianCalendar.java @@ -17,6 +17,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@link LocalDateTime} to {@code XMLGregorianCalendar}. + * * @author Andrei Arlou */ public class LocalDateTimeToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateToXmlGregorianCalendar.java index 2e2544ade0..e2ec7d7696 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/LocalDateToXmlGregorianCalendar.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@link LocalDate} to {@code XMLGregorianCalendar}. + * * @author Gunnar Morling */ public class LocalDateToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/StringToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/StringToXmlGregorianCalendar.java index 9f0d2ad207..635853d0fe 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/StringToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/StringToXmlGregorianCalendar.java @@ -19,6 +19,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@link String} to {@code XMLGregorianCalendar}. + * * @author Sjaak Derksen */ public class StringToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToCalendar.java index 03927fd869..e52b306af0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToCalendar.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@code XMLGregorianCalendar} to {@link Calendar}. + * * @author Sjaak Derksen */ public class XmlGregorianCalendarToCalendar extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToDate.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToDate.java index 38f76a66ee..fd3d9a33a6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToDate.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToDate.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@code XMLGregorianCalendar} to {@link Date}. + * * @author Sjaak Derksen */ public class XmlGregorianCalendarToDate extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaDateTime.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaDateTime.java index ab6658bfc5..93ec3e8f96 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaDateTime.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaDateTime.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.internal.model.source.builtin; +import java.util.Calendar; import java.util.Set; import org.mapstruct.ap.internal.model.common.Parameter; @@ -16,6 +17,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from Joda {@code DateTime} to {@link Calendar}. + * * @author Sjaak Derksen */ public class XmlGregorianCalendarToJodaDateTime extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDate.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDate.java index f45488755c..9a9c1561fc 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDate.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDate.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@code XMLGregorianCalendar} to Joda {@code LocalDate}. + * * @author Sjaak Derksen */ public class XmlGregorianCalendarToJodaLocalDate extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDateTime.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDateTime.java index af7fd9e9fd..a8d36a13cf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDateTime.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalDateTime.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@code XMLGregorianCalendar} to Joda {@code LocalDateTime}. + * * @author Sjaak Derksen */ public class XmlGregorianCalendarToJodaLocalDateTime extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalTime.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalTime.java index 3725691134..b26450d560 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalTime.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToJodaLocalTime.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * Conversion from {@code XMLGregorianCalendar} to Joda {@code LocalTime}. + * * @author Sjaak Derksen */ public class XmlGregorianCalendarToJodaLocalTime extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDate.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDate.java index 2c9ced3413..7bcd5902a5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDate.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDate.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@code XMLGregorianCalendar} to {@link LocalDate}. + * * @author Gunnar Morling */ public class XmlGregorianCalendarToLocalDate extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDateTime.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDateTime.java index b676bd03a4..21b4c57854 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDateTime.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToLocalDateTime.java @@ -17,6 +17,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@code XMLGregorianCalendar} to {@link LocalDateTime}. + * * @author Andrei Arlou */ public class XmlGregorianCalendarToLocalDateTime extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToString.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToString.java index c932da6675..e293a9c80b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToString.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/XmlGregorianCalendarToString.java @@ -18,6 +18,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@code XMLGregorianCalendar} to {@link String}. + * * @author Sjaak Derksen */ public class XmlGregorianCalendarToString extends BuiltInMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.java index 27a39cd2b7..d9e39b388e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/ZonedDateTimeToXmlGregorianCalendar.java @@ -16,6 +16,8 @@ import static org.mapstruct.ap.internal.util.Collections.asSet; /** + * A built-in method for converting from {@link ZonedDateTime} to {@code XMLGregorianCalendar}. + * * @author Christian Bandowski */ public class ZonedDateTimeToXmlGregorianCalendar extends AbstractToXmlGregorianCalendar { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java index 23af0a55f9..87ec3b4dc1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java @@ -12,6 +12,8 @@ import org.mapstruct.ap.internal.model.source.Method; /** + * A {@link MethodSelector} that selects the most specific result type. + * * @author Filip Hrisafov */ public class MostSpecificResultTypeSelector implements MethodSelector { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectedMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectedMethod.java index 7acb1af210..1702a8d4d6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectedMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectedMethod.java @@ -14,6 +14,8 @@ /** * A selected method with additional metadata that might be required for further usage of the selected method. * + * @param the type of the method + * * @author Andreas Gudian */ public class SelectedMethod { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/option/MappingOption.java b/processor/src/main/java/org/mapstruct/ap/internal/option/MappingOption.java index d35b2c0dc6..fedbe997a6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/option/MappingOption.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/option/MappingOption.java @@ -6,6 +6,8 @@ package org.mapstruct.ap.internal.option; /** + * The different compiler mapping options that are available in MapStruct. + * * @author Filip Hrisafov */ public enum MappingOption { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java index 72a44d106f..67ce6e10ff 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java @@ -34,6 +34,11 @@ import static javax.lang.model.util.ElementFilter.fieldsIn; import static javax.lang.model.util.ElementFilter.methodsIn; +/** + * MapStruct specific abstract implementation of {@link ElementUtils}. + * This allows us to provide different implementations for different compilers and / or + * to allow us for easier implementation of using the module system. + */ public abstract class AbstractElementUtilsDecorator implements ElementUtils { private final Elements delegate; @@ -312,6 +317,8 @@ private TypeElement asTypeElement(TypeMirror mirror) { } /** + * Checks whether the {@code executable} does not have a private modifier. + * * @param executable the executable to check * @return {@code true}, iff the executable does not have a private modifier */ diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/MetaAnnotations.java b/processor/src/main/java/org/mapstruct/ap/internal/util/MetaAnnotations.java index f2328a91cc..96768daf56 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/MetaAnnotations.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/MetaAnnotations.java @@ -16,6 +16,11 @@ import org.mapstruct.tools.gem.Gem; /** + * A base helper class that provides utility methods for working with meta annotations. + * + * @param The type of the processed annotation + * @param The type of the underlying holder for the processed annotation + * * @author Filip Hrisafov */ public abstract class MetaAnnotations { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/RepeatableAnnotations.java b/processor/src/main/java/org/mapstruct/ap/internal/util/RepeatableAnnotations.java index accae77718..76126bf488 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/RepeatableAnnotations.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/RepeatableAnnotations.java @@ -16,6 +16,12 @@ import org.mapstruct.tools.gem.Gem; /** + * A base helper class that provides utility methods for working with repeatable annotations. + * + * @param The singular annotation type + * @param The multiple annotation type + * @param The underlying holder for the processed annotations + * * @author Ben Zegveld */ public abstract class RepeatableAnnotations { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/DelegateAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/DelegateAccessor.java index 9b2981d7c5..5ebc835ba1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/DelegateAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/DelegateAccessor.java @@ -11,6 +11,8 @@ import javax.lang.model.type.TypeMirror; /** + * An {@link Accessor} which delegates all calls to another {@link Accessor}. + * * @author Filip Hrisafov */ public abstract class DelegateAccessor implements Accessor { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/PresenceCheckAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/PresenceCheckAccessor.java index cc974b939f..d96788b7f7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/PresenceCheckAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/PresenceCheckAccessor.java @@ -8,6 +8,8 @@ import javax.lang.model.element.ExecutableElement; /** + * Accessor for presence checks. + * * @author Filip Hrisafov */ public interface PresenceCheckAccessor { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java index 31edf3a6da..4be3c26ff9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadAccessor.java @@ -11,6 +11,8 @@ import javax.lang.model.type.TypeMirror; /** + * An {@link Accessor} that can be used for reading a property from a bean. + * * @author Filip Hrisafov */ public interface ReadAccessor extends Accessor { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadDelegateAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadDelegateAccessor.java index 608c1b21cf..aa93936662 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadDelegateAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ReadDelegateAccessor.java @@ -6,6 +6,9 @@ package org.mapstruct.ap.internal.util.accessor; /** + * {@link ReadAccessor} that delegates to another {@link Accessor} and requires an implementation of + * {@link #getSimpleName()} + * * @author Filip Hrisafov */ public abstract class ReadDelegateAccessor extends DelegateAccessor implements ReadAccessor { diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java index 511504c937..e7529dcd08 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java @@ -188,7 +188,6 @@ private boolean isAdderWithUpperCase4thCharacter(ExecutableElement method) { * {@link #getElementName(ExecutableElement) }. *

      * The calling MapStruct code guarantees there's only one argument. - *

      * * @param method to be analyzed * diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java index afcee7d247..333a11dbcc 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java @@ -317,7 +317,7 @@ protected boolean isPossibleBuilderCreationMethod(ExecutableElement method, Type *

      * The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the * {@code builderElement} should be ignored, i.e. not checked for build elements. - *

      + * * @param builderElement the element for the builder * @param typeElement the element for the type that is being built * @return the build method for the {@code typeElement} if it exists, or {@code null} if it does not diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumMappingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumMappingStrategy.java index 62d6c32809..11e1257be1 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumMappingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultEnumMappingStrategy.java @@ -10,6 +10,8 @@ import javax.lang.model.util.Types; /** + * The default implementation of the {@link EnumMappingStrategy} service provider interface. + * * @author Filip Hrisafov * * @since 1.4 diff --git a/processor/src/main/java/org/mapstruct/ap/spi/MappingExclusionProvider.java b/processor/src/main/java/org/mapstruct/ap/spi/MappingExclusionProvider.java index 9dae81cda2..2a1cacfb1f 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/MappingExclusionProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/MappingExclusionProvider.java @@ -15,7 +15,6 @@ *

      * When generating the implementation of a mapping method, MapStruct will apply the following routine for each * attribute pair in the source and target object: - *

      *

        *
      • If source and target attribute have the same type, the value will be simply copied from source to target. * If the attribute is a collection (e.g. a `List`) a copy of the collection will be set into the target diff --git a/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java index abb6e9cb4f..226251dfab 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/PrefixEnumTransformationStrategy.java @@ -6,6 +6,8 @@ package org.mapstruct.ap.spi; /** + * An {@link EnumTransformationStrategy} that prepends a prefix to the enum value. + * * @author Filip Hrisafov * * @since 1.4 diff --git a/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java index d3f0c515b3..dec7af6de4 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/StripPrefixEnumTransformationStrategy.java @@ -6,6 +6,8 @@ package org.mapstruct.ap.spi; /** + * An {@link EnumTransformationStrategy} that strips a prefix from the enum value. + * * @author Filip Hrisafov * * @since 1.4 diff --git a/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java index cf239600d4..3b76354fa7 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/StripSuffixEnumTransformationStrategy.java @@ -6,6 +6,8 @@ package org.mapstruct.ap.spi; /** + * An {@link EnumTransformationStrategy} that strips a suffix from the enum value. + * * @author Filip Hrisafov * * @since 1.4 diff --git a/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java index 4cd92b9af8..1f4eb89217 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/SuffixEnumTransformationStrategy.java @@ -6,6 +6,8 @@ package org.mapstruct.ap.spi; /** + * An {@link EnumTransformationStrategy} that appends a suffix to the enum value. + * * @author Filip Hrisafov * * @since 1.4 diff --git a/processor/src/main/java/org/mapstruct/ap/spi/util/package-info.java b/processor/src/main/java/org/mapstruct/ap/spi/util/package-info.java index 7b8f374cf7..93bccf51d0 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/util/package-info.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/util/package-info.java @@ -4,4 +4,7 @@ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ +/** + * Utility classes for the SPI package. + */ package org.mapstruct.ap.spi.util; From d59e6a04aa3fb4bb1ca313959d4eecc9891287c9 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 31 Jan 2026 17:09:36 +0100 Subject: [PATCH 522/597] Cleanup checks for JRE prior 21 in processor test module The processor test module is compiled using Java 21. Therefore, all tests that were configured to run prior Java 21 are no longer executed and can be safely removed --- .../conversion/date/DateConversionTest.java | 75 ------------------- .../jodatime/JodaConversionTest.java | 32 -------- .../runner/JdkCompilingExtension.java | 33 -------- 3 files changed, 140 deletions(-) diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java index bc90228206..346f34a054 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/date/DateConversionTest.java @@ -13,9 +13,6 @@ import java.util.GregorianCalendar; import java.util.List; -import org.junit.jupiter.api.condition.EnabledForJreRange; -import org.junit.jupiter.api.condition.EnabledOnJre; -import org.junit.jupiter.api.condition.JRE; import org.junitpioneer.jupiter.DefaultLocale; import org.junitpioneer.jupiter.ReadsDefaultTimeZone; import org.mapstruct.ap.testutil.IssueKey; @@ -40,38 +37,6 @@ public class DateConversionTest { @ProcessorTest - @EnabledOnJre( JRE.JAVA_8 ) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ - public void shouldApplyDateFormatForConversions() { - Source source = new Source(); - source.setDate( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); - source.setAnotherDate( new GregorianCalendar( 2013, Calendar.FEBRUARY, 14 ).getTime() ); - - Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); - - assertThat( target ).isNotNull(); - assertThat( target.getDate() ).isEqualTo( "06.07.2013" ); - assertThat( target.getAnotherDate() ).isEqualTo( "14.02.13 00:00" ); - } - - @ProcessorTest - @EnabledOnJre( JRE.JAVA_8 ) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ - public void shouldApplyDateFormatForConversionsWithCustomLocale() { - Source source = new Source(); - source.setDate( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); - source.setAnotherDate( new GregorianCalendar( 2013, Calendar.FEBRUARY, 14 ).getTime() ); - - Target target = SourceTargetMapper.INSTANCE.sourceToTargetWithCustomLocale( source ); - - assertThat( target ).isNotNull(); - assertThat( target.getDate() ).isEqualTo( "juillet 06, 2013" ); - assertThat( target.getAnotherDate() ).isEqualTo( "14.02.13, 00:00" ); - } - - @ProcessorTest - @EnabledForJreRange(min = JRE.JAVA_11) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionsJdk11() { Source source = new Source(); source.setDate( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); @@ -85,8 +50,6 @@ public void shouldApplyDateFormatForConversionsJdk11() { } @ProcessorTest - @EnabledForJreRange(min = JRE.JAVA_11) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionsJdk11WithCustomLocale() { Source source = new Source(); source.setDate( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); @@ -100,42 +63,6 @@ public void shouldApplyDateFormatForConversionsJdk11WithCustomLocale() { } @ProcessorTest - @EnabledOnJre(JRE.JAVA_8) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ - public void shouldApplyDateFormatForConversionInReverseMapping() { - Target target = new Target(); - target.setDate( "06.07.2013" ); - target.setAnotherDate( "14.02.13 8:30" ); - - Source source = SourceTargetMapper.INSTANCE.targetToSource( target ); - - assertThat( source ).isNotNull(); - assertThat( source.getDate() ).isEqualTo( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); - assertThat( source.getAnotherDate() ).isEqualTo( - new GregorianCalendar( 2013, Calendar.FEBRUARY, 14, 8, 30 ).getTime() - ); - } - - @ProcessorTest - @EnabledOnJre(JRE.JAVA_8) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ - public void shouldApplyDateFormatForConversionInReverseMappingWithCustomLocale() { - Target target = new Target(); - target.setDate( "juillet 06, 2013" ); - target.setAnotherDate( "14.02.13 8:30" ); - - Source source = SourceTargetMapper.INSTANCE.targetToSourceWithCustomLocale( target ); - - assertThat( source ).isNotNull(); - assertThat( source.getDate() ).isEqualTo( new GregorianCalendar( 2013, Calendar.JULY, 6 ).getTime() ); - assertThat( source.getAnotherDate() ).isEqualTo( - new GregorianCalendar( 2013, Calendar.FEBRUARY, 14, 8, 30 ).getTime() - ); - } - - @ProcessorTest - @EnabledForJreRange(min = JRE.JAVA_11) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionInReverseMappingJdk11() { Target target = new Target(); target.setDate( "06.07.2013" ); @@ -151,8 +78,6 @@ public void shouldApplyDateFormatForConversionInReverseMappingJdk11() { } @ProcessorTest - @EnabledForJreRange(min = JRE.JAVA_11) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void shouldApplyDateFormatForConversionInReverseMappingJdk11WithCustomLocale() { Target target = new Target(); target.setDate( "juillet 06, 2013" ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java index 8574c7a6be..d447c455ff 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/jodatime/JodaConversionTest.java @@ -13,8 +13,6 @@ import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; -import org.junit.jupiter.api.condition.EnabledForJreRange; -import org.junit.jupiter.api.condition.JRE; import org.junitpioneer.jupiter.DefaultLocale; import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; @@ -71,8 +69,6 @@ public void testLocalTimeToString() { } @ProcessorTest - @EnabledForJreRange(min = JRE.JAVA_21) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ public void testSourceToTargetMappingForStrings() { Source src = new Source(); src.setLocalTime( new LocalTime( 0, 0 ) ); @@ -98,34 +94,6 @@ public void testSourceToTargetMappingForStrings() { assertThat( target.getLocalTime() ).isEqualTo( "00:00:00" ); } - @ProcessorTest - @EnabledForJreRange(min = JRE.JAVA_11, max = JRE.JAVA_17) - // See https://bugs.openjdk.java.net/browse/JDK-8211262, there is a difference in the default formats on Java 9+ - public void testSourceToTargetMappingForStringsJdk11() { - Source src = new Source(); - src.setLocalTime( new LocalTime( 0, 0 ) ); - src.setLocalDate( new LocalDate( 2014, 1, 1 ) ); - src.setLocalDateTime( new LocalDateTime( 2014, 1, 1, 0, 0 ) ); - src.setDateTime( new DateTime( 2014, 1, 1, 0, 0, 0, DateTimeZone.UTC ) ); - - // with given format - Target target = SourceTargetMapper.INSTANCE.sourceToTarget( src ); - - assertThat( target ).isNotNull(); - assertThat( target.getDateTime() ).isEqualTo( "01.01.2014 00:00 UTC" ); - assertThat( target.getLocalDateTime() ).isEqualTo( "01.01.2014 00:00" ); - assertThat( target.getLocalDate() ).isEqualTo( "01.01.2014" ); - assertThat( target.getLocalTime() ).isEqualTo( "00:00" ); - - // and now with default mappings - target = SourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( src ); - assertThat( target ).isNotNull(); - assertThat( target.getDateTime() ).isEqualTo( "1. Januar 2014 um 00:00:00 UTC" ); - assertThat( target.getLocalDateTime() ).isEqualTo( "1. Januar 2014 um 00:00:00" ); - assertThat( target.getLocalDate() ).isEqualTo( "1. Januar 2014" ); - assertThat( target.getLocalTime() ).isEqualTo( "00:00:00" ); - } - @ProcessorTest public void testStringToDateTime() { String dateTimeAsString = "01.01.2014 00:00 UTC"; diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java index 115618382c..1e9a1bc22f 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/JdkCompilingExtension.java @@ -12,9 +12,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Objects; import javax.annotation.processing.Processor; -import javax.tools.Diagnostic.Kind; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; @@ -23,10 +21,8 @@ import javax.tools.StandardLocation; import javax.tools.ToolProvider; -import org.junit.jupiter.api.condition.JRE; import org.mapstruct.ap.MappingProcessor; import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor; -import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor; /** * Extension that uses the JDK compiler to compile. @@ -140,35 +136,6 @@ private static List asFiles(List paths) { return classpath; } - /** - * The JDK 8 compiler needs some special treatment for the diagnostics. - * See comment in the function. - */ - @Override - protected List filterExpectedDiagnostics(List expectedDiagnostics) { - if ( JRE.currentVersion() != JRE.JAVA_8 ) { - // The JDK 8+ compilers report all ERROR diagnostics properly. Also when there are multiple per line. - return expectedDiagnostics; - } - List filtered = new ArrayList<>(expectedDiagnostics.size()); - - // The JDK 8 compiler only reports the first message of kind ERROR that is reported for one source file line, - // so we filter out the surplus diagnostics. The input list is already sorted by file name and line number, - // with the order for the diagnostics in the same line being kept at the order as given in the test. - DiagnosticDescriptor previous = null; - for ( DiagnosticDescriptor diag : expectedDiagnostics ) { - if ( diag.getKind() != Kind.ERROR - || previous == null - || !previous.getSourceFileName().equals( diag.getSourceFileName() ) - || !Objects.equals( previous.getLine(), diag.getLine() ) ) { - filtered.add( diag ); - previous = diag; - } - } - - return filtered; - } - private static FilteringParentClassLoader newFilteringClassLoaderForJdk() { return new FilteringParentClassLoader( "kotlin.", From 73f042bdc5906a3d5a0774c6535dabce64a83799 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 31 Jan 2026 17:25:06 +0100 Subject: [PATCH 523/597] Use explicit version instead of JRE#OTHER to disable on Java 27-ea `@DisabledOnJre(JRE.OTHER)` no longer disables the test if the JRE is unknown. --- .../org/mapstruct/itest/tests/MavenIntegrationTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java index 4596efd630..60784d206a 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java @@ -89,7 +89,7 @@ void jsr330Test() { @ProcessorTest(baseDir = "lombokBuilderTest", processorTypes = { ProcessorTest.ProcessorType.JAVAC }) - @DisabledOnJre(JRE.OTHER) + @DisabledOnJre(versions = 27) void lombokBuilderTest() { } @@ -98,7 +98,7 @@ void lombokBuilderTest() { ProcessorTest.ProcessorType.JAVAC_WITH_PATHS }) @EnabledForJreRange(min = JRE.JAVA_11) - @DisabledOnJre(JRE.OTHER) + @DisabledOnJre(versions = 27) void lombokModuleTest() { } @@ -155,7 +155,7 @@ void expressionTextBlocksTest() { }, forkJvm = true) // We have to fork the jvm because there is an NPE in com.intellij.openapi.util.SystemInfo.getRtVersion // and the kotlin-maven-plugin uses that. See also https://youtrack.jetbrains.com/issue/IDEA-238907 - @DisabledOnJre(JRE.OTHER) + @DisabledOnJre(versions = 27) void kotlinDataTest() { } From 6708e3592539ec5b4f94c09643c039e359b78eae Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 31 Jan 2026 19:49:02 +0100 Subject: [PATCH 524/597] Remove testing with no longer maintained org.bsc.maven:maven-processor-plugin The last release of the plugin was in April 2024. In addition to that, the maven-compiler-plugin is de-facto the standard for compiling Java. Therefore, we are removing testing with the maven-processor-plugin. --- .../ProcessorInvocationInterceptor.java | 23 +++++------- .../testutil/extension/ProcessorTest.java | 5 +-- integrationtest/src/test/resources/pom.xml | 37 ------------------- 3 files changed, 10 insertions(+), 55 deletions(-) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java index 85ce64952f..39cd5fdae6 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorInvocationInterceptor.java @@ -136,21 +136,16 @@ private void addAdditionalCliArguments(Verifier verifier) private void configureProcessor(Verifier verifier) { ProcessorTest.ProcessorType processor = processorTestContext.getProcessor(); String compilerId = processor.getCompilerId(); - if ( compilerId != null ) { - String profile = processor.getProfile(); - if ( profile == null ) { - profile = "generate-via-compiler-plugin"; + String profile = processor.getProfile(); + if ( profile == null ) { + profile = "generate-via-compiler-plugin"; + } + verifier.addCliOption( "-P" + profile ); + verifier.addCliOption( "-Dcompiler-id=" + compilerId ); + if ( processor == ProcessorTest.ProcessorType.JAVAC ) { + if ( CURRENT_VERSION.ordinal() >= JRE.JAVA_21.ordinal() ) { + verifier.addCliOption( "-Dmaven.compiler.proc=full" ); } - verifier.addCliOption( "-P" + profile ); - verifier.addCliOption( "-Dcompiler-id=" + compilerId ); - if ( processor == ProcessorTest.ProcessorType.JAVAC ) { - if ( CURRENT_VERSION.ordinal() >= JRE.JAVA_21.ordinal() ) { - verifier.addCliOption( "-Dmaven.compiler.proc=full" ); - } - } - } - else { - verifier.addCliOption( "-Pgenerate-via-processor-plugin" ); } } diff --git a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java index 95480eee9d..d5b4860d5b 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/testutil/extension/ProcessorTest.java @@ -46,9 +46,7 @@ enum ProcessorType { JAVAC( "javac" ), JAVAC_WITH_PATHS( "javac", JRE.OTHER, "generate-via-compiler-plugin-with-annotation-processor-paths" ), - ECLIPSE_JDT( "jdt", JRE.JAVA_8 ), - - PROCESSOR_PLUGIN( null, JRE.JAVA_8 ); + ECLIPSE_JDT( "jdt", JRE.JAVA_8 ); private final String compilerId; private final JRE max; @@ -111,7 +109,6 @@ ProcessorType[] processorTypes() default { ProcessorType.JAVAC, ProcessorType.JAVAC_WITH_PATHS, ProcessorType.ECLIPSE_JDT, - ProcessorType.PROCESSOR_PLUGIN }; /** diff --git a/integrationtest/src/test/resources/pom.xml b/integrationtest/src/test/resources/pom.xml index c8a19228f0..c2ece3b38d 100644 --- a/integrationtest/src/test/resources/pom.xml +++ b/integrationtest/src/test/resources/pom.xml @@ -97,43 +97,6 @@ - - generate-via-processor-plugin - - false - - - - - org.bsc.maven - maven-processor-plugin - 3.3.3 - - - process - generate-sources - - process - - - - - \${project.build.directory}/generated-sources/mapstruct - - org.mapstruct.ap.MappingProcessor - - - - - ${project.groupId} - mapstruct-processor - ${mapstruct.version} - - - - - - debug-forked-javac From df97305df070ee5a33233a9072bd4c38d8d99e53 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 1 Feb 2026 12:09:53 +0100 Subject: [PATCH 525/597] #3940 Use deterministic order for supporting fields and methods --- .../ap/internal/model/BeanMappingMethod.java | 4 ++-- .../ap/internal/model/SupportingField.java | 2 +- .../creation/MappingResolverImpl.java | 7 ++++--- .../numbers/SourceTargetMapperImpl.java | 8 ++++---- .../OptionalSourceTargetMapperImpl.java | 20 +++++++++---------- .../java8time/SourceTargetMapperImpl.java | 20 +++++++++---------- .../numbers/SourceTargetMapperImpl.java | 8 ++++---- 7 files changed, 35 insertions(+), 34 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index c6fc7753f2..d67b550ad4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -301,8 +301,8 @@ else if ( !method.isUpdateMethod() ) { } // get bean mapping (when specified as annotation ) - this.missingIgnoredSourceProperties = new HashSet<>(); - this.redundantIgnoredSourceProperties = new HashSet<>(); + this.missingIgnoredSourceProperties = new LinkedHashSet<>(); + this.redundantIgnoredSourceProperties = new LinkedHashSet<>(); if ( beanMapping != null && !beanMapping.getIgnoreUnmappedSourceProperties().isEmpty() ) { // Get source properties explicitly mapped using @Mapping annotations Set mappedSourceProperties = method.getOptions().getMappings().stream() diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingField.java b/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingField.java index caf6d6160e..2cac1ef351 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingField.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/SupportingField.java @@ -47,7 +47,7 @@ public static void addAllFieldsIn(Set supportingMapping for ( SupportingMappingMethod supportingMappingMethod : supportingMappingMethods ) { Field field = supportingMappingMethod.getSupportingField(); if ( field != null ) { - targets.add( supportingMappingMethod.getSupportingField() ); + targets.add( field ); } } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index 2a36f6b890..67cdc09fc9 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -13,6 +13,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -97,12 +98,12 @@ public class MappingResolverImpl implements MappingResolver { * Private methods which are not present in the original mapper interface and are added to map certain property * types. */ - private final Set usedSupportedMappings = new HashSet<>(); + private final Set usedSupportedMappings = new LinkedHashSet<>(); /** * Private fields which are added to map certain property types. */ - private final Set usedSupportedFields = new HashSet<>(); + private final Set usedSupportedFields = new LinkedHashSet<>(); public MappingResolverImpl(FormattingMessager messager, ElementUtils elementUtils, TypeUtils typeUtils, TypeFactory typeFactory, List sourceModel, @@ -199,7 +200,7 @@ private ResolvingAttempt(List sourceModel, Method mappingMethod, ForgedM this.formattingParameters = formattingParameters == null ? FormattingParameters.EMPTY : formattingParameters; this.sourceRHS = sourceRHS; - this.supportingMethodCandidates = new HashSet<>(); + this.supportingMethodCandidates = new LinkedHashSet<>(); this.selectionCriteria = criteria; this.positionHint = positionHint; this.forger = forger; diff --git a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java index ccf8042e6b..941bd929a7 100644 --- a/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java +++ b/processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/conversion/numbers/SourceTargetMapperImpl.java @@ -509,16 +509,16 @@ public Map targetToSourceWithCustomLocale(Map targetToSourceWithCustomLocale(Map Date: Sun, 1 Feb 2026 12:41:20 +0100 Subject: [PATCH 526/597] #1830 Add support for NullValuePropertyMappingStrategy.CLEAR strategy Using this strategy will clear target collections / maps when the source property is null --- NEXT_RELEASE_CHANGELOG.md | 1 + .../NullValuePropertyMappingStrategy.java | 9 +- ...apter-10-advanced-mapping-options.asciidoc | 3 + .../NullValuePropertyMappingStrategyGem.java | 3 +- ...nceSetterWrapperForCollectionsAndMaps.java | 27 ++-- .../model/source/MapperConfigOptions.java | 2 +- .../internal/model/source/MapperOptions.java | 2 +- ...anceSetterWrapperForCollectionsAndMaps.ftl | 8 +- .../nullvaluepropertymapping/clear/Bean.java | 40 ++++++ .../clear/BeanDTO.java | 40 ++++++ .../clear/BeanDTOWithId.java | 19 +++ .../clear/BeanMapper.java | 34 +++++ .../clear/BeanMapperWithStrategyOnMapper.java | 25 ++++ .../clear/NestedBean.java | 18 +++ .../NullValuePropertyMappingClearTest.java | 116 ++++++++++++++++++ 15 files changed, 329 insertions(+), 18 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/Bean.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanDTO.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanDTOWithId.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanMapperWithStrategyOnMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/NestedBean.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/NullValuePropertyMappingClearTest.java diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 59fed4fed3..476f3e025d 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -22,6 +22,7 @@ it will be treated as a potential builder. Builders through static methods on the type have a precedence. * Behaviour change: Warning when the target has no target properties (#1140) +* Add new `NullValuePropertyMappingStrategy#CLEAR` for clearing Collection and Map properties when updating a bean (#1830) diff --git a/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java index a58eed8a2a..0ab30d5271 100644 --- a/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java +++ b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java @@ -56,5 +56,12 @@ public enum NullValuePropertyMappingStrategy { * If a source bean property equals {@code null} the target bean property will be ignored and retain its * existing value. */ - IGNORE; + IGNORE, + + /** + * If a source bean property equals {@code null} the target bean property will be cleared. + * This strategy is only applicable to target properties that are of type {@link java.util.Collection Collection} + * or {@link java.util.Map Map}. + */ + CLEAR; } diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc index 43f8fc38a0..34f1e19817 100644 --- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc +++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc @@ -254,6 +254,9 @@ For all other objects an new instance is created. Please note that a default con 2. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MapperConfig`, the mapping result will be equal to the original value of the `@MappingTarget` annotated target. +3. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.CLEAR` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MapperConfig`, the target collection or map will be cleared when the source property is `null`. +This strategy only applies to `Collection` and `Map` target properties. + The strategy works in a hierarchical fashion. Setting `nullValuePropertyMappingStrategy` on mapping method level will override `@Mapper#nullValuePropertyMappingStrategy`, and `@Mapper#nullValuePropertyMappingStrategy` will override `@MapperConfig#nullValuePropertyMappingStrategy`. [NOTE] diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValuePropertyMappingStrategyGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValuePropertyMappingStrategyGem.java index 77e4aa48d6..93c98b73ac 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValuePropertyMappingStrategyGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/NullValuePropertyMappingStrategyGem.java @@ -15,5 +15,6 @@ public enum NullValuePropertyMappingStrategyGem { SET_TO_NULL, SET_TO_DEFAULT, - IGNORE; + IGNORE, + CLEAR; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java index 2dd7f81a0b..875368b014 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java @@ -5,19 +5,18 @@ */ package org.mapstruct.ap.internal.model.assignment; -import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; -import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; -import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; - import java.util.HashSet; import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; +import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem; -import org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem; + +import static org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem.ALWAYS; +import static org.mapstruct.ap.internal.gem.NullValuePropertyMappingStrategyGem.IGNORE; /** * This wrapper handles the situation where an assignment is done for an update method. @@ -34,8 +33,8 @@ public class ExistingInstanceSetterWrapperForCollectionsAndMaps extends SetterWrapperForCollectionsAndMapsWithNullCheck { - private final boolean includeElseBranch; - private final boolean mapNullToDefault; + private final NullValuePropertyMappingStrategyGem nvpms; + private final NullValueCheckStrategyGem nvcs; private final Type targetType; public ExistingInstanceSetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, @@ -53,9 +52,9 @@ public ExistingInstanceSetterWrapperForCollectionsAndMaps(Assignment decoratedAs typeFactory, fieldAssignment ); - this.mapNullToDefault = SET_TO_DEFAULT == nvpms; + this.nvcs = nvcs; + this.nvpms = nvpms; this.targetType = targetType; - this.includeElseBranch = ALWAYS != nvcs && IGNORE != nvpms; } @Override @@ -68,11 +67,15 @@ public Set getImportTypes() { } public boolean isIncludeElseBranch() { - return includeElseBranch; + return nvcs != ALWAYS && nvpms != IGNORE; } public boolean isMapNullToDefault() { - return mapNullToDefault; + return nvpms == NullValuePropertyMappingStrategyGem.SET_TO_DEFAULT; + } + + public boolean isMapNullToClear() { + return nvpms == NullValuePropertyMappingStrategyGem.CLEAR; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java index d606658878..2766faa64c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperConfigOptions.java @@ -8,8 +8,8 @@ import java.util.Set; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; import org.mapstruct.ap.internal.gem.InjectionStrategyGem; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java index 9c2203efd5..21cb7813c4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MapperOptions.java @@ -12,8 +12,8 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import org.mapstruct.ap.internal.util.ElementUtils; +import org.mapstruct.ap.internal.util.ElementUtils; import org.mapstruct.ap.internal.option.Options; import org.mapstruct.ap.internal.gem.BuilderGem; import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem; diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl index 95662c4c79..8e771f0ddb 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl @@ -15,8 +15,12 @@ ${ext.targetBeanName}.${ext.targetReadAccessorName}.<#if ext.targetType.collectionType>addAll<#else>putAll( <@lib.handleWithAssignmentOrNullCheckVar/> ); <#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && includeElseBranch>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleLocalVarNullCheck --> - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if mapNullToDefault><@lib.initTargetObject/><#else>null; - } + <#if mapNullToClear> + ${ext.targetBeanName}.${ext.targetReadAccessorName}.clear(); + <#else > + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if mapNullToDefault><@lib.initTargetObject/><#else>null; + + } } else { diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/Bean.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/Bean.java new file mode 100644 index 0000000000..545ddffe8b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/Bean.java @@ -0,0 +1,40 @@ +/* + * 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.nullvaluepropertymapping.clear; + +import java.util.Collection; +import java.util.Map; + +public class Bean { + + private String id; + private Collection list; + private Map map; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Collection getList() { + return list; + } + + public void setList(Collection list) { + this.list = list; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanDTO.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanDTO.java new file mode 100644 index 0000000000..9529da8113 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanDTO.java @@ -0,0 +1,40 @@ +/* + * 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.nullvaluepropertymapping.clear; + +import java.util.Collection; +import java.util.Map; + +public class BeanDTO { + + private String id; + private Collection list; + private Map map; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Collection getList() { + return list; + } + + public void setList(Collection list) { + this.list = list; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanDTOWithId.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanDTOWithId.java new file mode 100644 index 0000000000..152a872d82 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanDTOWithId.java @@ -0,0 +1,19 @@ +/* + * 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.nullvaluepropertymapping.clear; + +public class BeanDTOWithId extends BeanDTO { + + private String id; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanMapper.java new file mode 100644 index 0000000000..4d453579ea --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanMapper.java @@ -0,0 +1,34 @@ +/* + * 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.nullvaluepropertymapping.clear; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface BeanMapper { + + BeanMapper INSTANCE = Mappers.getMapper( BeanMapper.class ); + + @Mapping(target = "list", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.CLEAR) + @Mapping(target = "map", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.CLEAR) + BeanDTO map(Bean source, @MappingTarget BeanDTO target); + + @Mapping(target = "id", source = "bean.id") + @Mapping(target = "list", source = "bean.list", + nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.CLEAR) + @Mapping(target = "map", source = "bean.map", + nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.CLEAR) + BeanDTO map(NestedBean source, @MappingTarget BeanDTO target); + + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.CLEAR) + BeanDTO mapWithBeanMapping(Bean source, @MappingTarget BeanDTO target); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanMapperWithStrategyOnMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanMapperWithStrategyOnMapper.java new file mode 100644 index 0000000000..0e26b91b5b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/BeanMapperWithStrategyOnMapper.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.ap.test.nullvaluepropertymapping.clear; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.CLEAR) +public interface BeanMapperWithStrategyOnMapper { + + BeanMapperWithStrategyOnMapper INSTANCE = Mappers.getMapper( BeanMapperWithStrategyOnMapper.class ); + + BeanDTO map(Bean source, @MappingTarget BeanDTO target); + + @Mapping(target = "id", source = "bean.id") + @Mapping(target = "list", source = "bean.list") + @Mapping(target = "map", source = "bean.map") + BeanDTO map(NestedBean source, @MappingTarget BeanDTO target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/NestedBean.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/NestedBean.java new file mode 100644 index 0000000000..448645c107 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/NestedBean.java @@ -0,0 +1,18 @@ +/* + * 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.nullvaluepropertymapping.clear; + +public class NestedBean { + private Bean bean; + + public Bean getBean() { + return bean; + } + + public void setBean(Bean bean) { + this.bean = bean; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/NullValuePropertyMappingClearTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/NullValuePropertyMappingClearTest.java new file mode 100644 index 0000000000..20cbcdc2b4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/clear/NullValuePropertyMappingClearTest.java @@ -0,0 +1,116 @@ +/* + * 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.nullvaluepropertymapping.clear; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +@WithClasses({ + Bean.class, + BeanDTO.class, + NestedBean.class, +}) +class NullValuePropertyMappingClearTest { + + @ProcessorTest + @WithClasses(BeanMapper.class) + void generatedMapperMethodsShouldCallClear() { + BeanDTO target = new BeanDTO(); + target.setId( "target" ); + List targetList = new ArrayList<>(); + targetList.add( "a" ); + targetList.add( "b" ); + target.setList( targetList ); + Map targetMap = new HashMap<>(); + targetMap.put( "a", "aValue" ); + target.setMap( targetMap ); + Bean source = new Bean(); + + BeanMapper.INSTANCE.map( source, target ); + assertThat( target.getId() ).isNull(); + assertThat( target.getList() ) + .isSameAs( targetList ) + .isEmpty(); + assertThat( target.getMap() ) + .isSameAs( targetMap ) + .isEmpty(); + + NestedBean nestedBean = new NestedBean(); + nestedBean.setBean( source ); + targetList.add( "a" ); + targetList.add( "b" ); + targetMap.put( "a", "aValue" ); + target.setId( "target" ); + BeanMapper.INSTANCE.map( nestedBean, target ); + assertThat( target.getId() ).isNull(); + assertThat( target.getList() ) + .isSameAs( targetList ) + .isEmpty(); + assertThat( target.getMap() ) + .isSameAs( targetMap ) + .isEmpty(); + + targetList.add( "a" ); + targetList.add( "b" ); + targetMap.put( "a", "aValue" ); + target.setId( "target" ); + BeanMapper.INSTANCE.mapWithBeanMapping( source, target ); + assertThat( target.getId() ).isNull(); + assertThat( target.getList() ) + .isSameAs( targetList ) + .isEmpty(); + assertThat( target.getMap() ) + .isSameAs( targetMap ) + .isEmpty(); + } + + @ProcessorTest + @WithClasses(BeanMapperWithStrategyOnMapper.class) + void generatedMapperWithMappingDefinedInConfigMethodsShouldCallClear() { + BeanDTO target = new BeanDTO(); + target.setId( "target" ); + List targetList = new ArrayList<>(); + targetList.add( "a" ); + targetList.add( "b" ); + target.setList( targetList ); + Map targetMap = new HashMap<>(); + targetMap.put( "a", "aValue" ); + target.setMap( targetMap ); + Bean source = new Bean(); + + BeanMapperWithStrategyOnMapper.INSTANCE.map( source, target ); + assertThat( target.getId() ).isNull(); + assertThat( target.getList() ) + .isSameAs( targetList ) + .isEmpty(); + assertThat( target.getMap() ) + .isSameAs( targetMap ) + .isEmpty(); + + NestedBean nestedBean = new NestedBean(); + nestedBean.setBean( source ); + targetList.add( "a" ); + targetList.add( "b" ); + targetMap.put( "a", "aValue" ); + target.setId( "target" ); + BeanMapperWithStrategyOnMapper.INSTANCE.map( nestedBean, target ); + assertThat( target.getId() ).isNull(); + assertThat( target.getList() ) + .isSameAs( targetList ) + .isEmpty(); + assertThat( target.getMap() ) + .isSameAs( targetMap ) + .isEmpty(); + } + +} From a5614dd798dbb6c07ed41e21708eaa2ecd153aad Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 1 Feb 2026 19:41:07 +0100 Subject: [PATCH 527/597] Prepare release notes and update copyright.txt --- NEXT_RELEASE_CHANGELOG.md | 47 ++++++++++++++++++++++++++++++++++----- copyright.txt | 14 +++++++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index 476f3e025d..c4ba49e24d 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -13,7 +13,7 @@ - Data classes with multiple constructors - Data classes with all default parameters - Sealed Classes (#3404) - Subclass exhaustiveness is now checked for Kotlin sealed classes - +* Add support for ignoring multiple target properties at once (#3838) - Using new annotation `@Ignored` ### Enhancements @@ -21,22 +21,59 @@ * Detect Builder without a factory method (#3729) - With this if there is an inner class that ends with `Builder` and has a constructor with parameters, it will be treated as a potential builder. Builders through static methods on the type have a precedence. -* Behaviour change: Warning when the target has no target properties (#1140) +* Add support for custom exception for subclass exhaustive strategy for `@SubclassMapping` mapping (#3821) - Available on `@BeanMapping`, `@Mapper` and `@MappingConfig`. * Add new `NullValuePropertyMappingStrategy#CLEAR` for clearing Collection and Map properties when updating a bean (#1830) - - +* Use deterministic order for supporting fields and methods (#3940) +* Support `@AnnotatedWith` on decorators (#3659) +* Behaviour change: Add warning/error for redundant `ignoreUnmappedSourceProperties` entries (#3906) +* Behaviour change: Warning when the target has no target properties (#1140) +* Behaviour change: Initialize `Optional` with `Optional.empty` instead of `null` (#3852) +* Behaviour change: Mark `String` to `Number` as lossy conversion (#3848) ### Bugs * Improve error message when mapping non-iterable to array (#3786) +* Fix conditional mapping with `@TargetPropertyName` failing for nested update mappings (#3809) +* Resolve duplicate invocation of overloaded lifecycle methods with inheritance (#3849) - It is possible to disable this by using the new compiler option `mapstruct.disableLifecycleOverloadDeduplicateSelector`. +* Support generic `@Context` (#3711) +* Properly apply `NullValuePropertyMappingStrategy.IGNORE` for collections / maps without setters (#3806) +* Properly recognize the type of public generic fields (#3807) +* Fix method in Record is treated as a fluent setter (#3886) +* Ensure `NullValuePropertyMappingStrategy.SET_TO_DEFAULT` initializes empty collection/map when target is null (#3884) +* Fix Compiler error when mapping an object named `Override` (#3905) ### Documentation +* General Improvements + * Javadoc + * Typos in comments + * Small code refactorings + ### Build +* Move Windows and MacOS builds outside of the main workflow +* Update release to release using the new Maven Central Portal +* Skip codecov coverage on forks +* Improve testing support for Kotlin + ### Behaviour Change -#### Warning when the target has no target properties +#### Warning when the target has no target properties (#1140) With this change, if the target bean does not have any target properties, a warning will be shown. This is like this to avoid potential mistakes by users, where they might think that the target bean has properties, but it does not. + +#### Warning for redundant `ignoreUnmappedSourceProperties` entries (#3906) + +With this change, if the `ignoreUnmappedSourceProperties` configuration contains properties that are actually mapped, a warning or compiler error will be shown. +The `unmappedSourcePolicy` is used to determine whether a warning, or an error is shown. + +#### Initialize `Optional` with `Optional.empty` instead of `null` (#3852) + +With this change, if the target `Optional` property is null, it will be initialized with `Optional.empty()` instead of `null`. + +#### Mark `String` to `Number` as lossy conversion (#3848) + +With this change, if the source `String` property is mapped to a `Number` property, a warning will be shown. +This is similar to what is happening when mapping `long` to `int`, etc. +The `typeConversionPolicy` `ReportingPolicy` is used to determine whether a warning, error or ignore is shown. diff --git a/copyright.txt b/copyright.txt index bd0034b076..356deb904d 100644 --- a/copyright.txt +++ b/copyright.txt @@ -1,12 +1,14 @@ Contributors ============ +Aleksey Ivashin - https://github.com/xumk Alexandr Shalugin - https://github.com/shalugin Amine Touzani - https://github.com/ttzn Andreas Gudian - https://github.com/agudian Andrei Arlou - https://github.com/Captain1653 Andres Jose Sebastian Rincon Gonzalez - https://github.com/stianrincon Arne Seime - https://github.com/seime +Cause Chung - https://github.com/cuzfrog Christian Bandowski - https://github.com/chris922 Chris DeLashmutt - https://github.com/cdelashmutt-pivotal Christian Kosmowski - https://github.com/ckosmowski @@ -15,6 +17,7 @@ Christophe Labouisse - https://github.com/ggtools Ciaran Liedeman - https://github.com/cliedeman Cindy Wang - https://github.com/birdfriend Cornelius Dirmeier - https://github.com/cornzy +Dennis Melzer - https://github.com/ David Feinblum - https://github.com/dvfeinblum Darren Rambaud - https://github.com/xyzst Dekel Pilli - https://github.com/dekelpilli @@ -53,7 +56,11 @@ Pavel Makhov - https://github.com/streetturtle Peter Larson - https://github.com/pjlarson Remko Plantenga - https://github.com/sonata82 Remo Meier - https://github.com/remmeier +Roel Mangelschots - https://github.com/rmschots +Ritesh Chopade - https://github.com/codeswithritesh Richard Lea - https://github.com/chigix +Roman Obolonskyi - https://github.com/Obolrom +Samil Can - https://github.com/SamilCan Saheb Preet Singh - https://github.com/sahebpreet Samuel Wright - https://github.com/samwright Sebastian Haberey - https://github.com/sebastianhaberey @@ -65,10 +72,15 @@ Taras Mychaskiw - https://github.com/twentylemon Thibault Duperron - https://github.com/Zomzog Tomáš Poledný - https://github.com/Saljack Tobias Meggendorfer - https://github.com/incaseoftrouble +Tran Ngoc Nhan - https://github.com/ Tillmann Gaida - https://github.com/Tillerino Timo Eckhardt - https://github.com/timoe Tomek Gubala - https://github.com/vgtworld Valentin Kulesh - https://github.com/unshare Vincent Alexander Beelte - https://github.com/grandmasterpixel Winter Andreas - https://github.dev/wandi34 -Xiu Hong Kooi - https://github.com/kooixh \ No newline at end of file +Xiu Hong Kooi - https://github.com/kooixh +Yang Tang - https://github.com/tangyang9464 +Zegveld - https://github.com/Zegveld +znight1020 - https://github.com/znight1020 +zral - https://github.com/zyberzebra From 14f9f6e2877e5ae1b28ce3b59e381628f72fa349 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 20:10:29 +0000 Subject: [PATCH 528/597] Releasing version 1.7.0.Beta1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0b3ca1c113..b1545ceb00 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index c73676192b..42ea5baec4 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index e62d5e4682..b3af781388 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index b480e24a06..fb4444e00d 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 21a2b151bd..d36a6428d6 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 71a61fc2fe..a9037e613c 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index f2b85d4969..ae877ef10c 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - ${git.commit.author.time} + 2026-02-01T20:10:28Z 1.0.0.Alpha3 3.6.2 diff --git a/pom.xml b/pom.xml index f55f0955d3..114085f46e 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 622ba924ee..608175a8e2 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml From 561d5dad858a4ba621bcab1f50452ea3851c17eb Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 1 Feb 2026 21:56:00 +0100 Subject: [PATCH 529/597] Adjust release to use Maven Central Publishing Portal --- .github/workflows/release.yml | 2 ++ parent/pom.xml | 31 ++++++++++++++----------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b4dffe0586..a4feca94a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,6 +62,8 @@ jobs: JRELEASER_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.GPG_PUBLIC_KEY }} JRELEASER_GPG_SECRET_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + JRELEASER_MAVENCENTRAL_SONATYPE_USERNAME: ${{ secrets.SONATYPE_CENTRAL_USERNAME }} + JRELEASER_MAVENCENTRAL_SONATYPE_TOKEN: ${{ secrets.SONATYPE_CENTRAL_TOKEN }} JRELEASER_NEXUS2_MAVEN_CENTRAL_USERNAME: ${{ secrets.SONATYPE_USERNAME }} JRELEASER_NEXUS2_MAVEN_CENTRAL_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} run: | diff --git a/parent/pom.xml b/parent/pom.xml index ae877ef10c..0043cfb44a 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -880,24 +880,21 @@ - - - ALWAYS - https://oss.sonatype.org/service/local - https://oss.sonatype.org/content/repositories/snapshots/ - true - true + + + RELEASE + https://central.sonatype.com/api/v1/publisher ${maven.multiModuleProjectDirectory}/target/staging-deploy - - - org.mapstruct - mapstruct-jdk8 - false - false - - - - + + + + org.mapstruct + mapstruct-jdk8 + false + false + + + From 6389f2a8056b01e792d02acee6dd1bf9ef3a3718 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 1 Feb 2026 21:58:29 +0100 Subject: [PATCH 530/597] Revert "Releasing version 1.7.0.Beta1" This reverts commit 14f9f6e2877e5ae1b28ce3b59e381628f72fa349. --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index b1545ceb00..0b3ca1c113 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 42ea5baec4..c73676192b 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index b3af781388..e62d5e4682 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index fb4444e00d..b480e24a06 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index d36a6428d6..21a2b151bd 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index a9037e613c..71a61fc2fe 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 0043cfb44a..01b97c10bf 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - 2026-02-01T20:10:28Z + ${git.commit.author.time} 1.0.0.Alpha3 3.6.2 diff --git a/pom.xml b/pom.xml index 114085f46e..f55f0955d3 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 608175a8e2..622ba924ee 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml From ab72ced6fb7a71591d51343493bf736379c61beb Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 20:59:33 +0000 Subject: [PATCH 531/597] Releasing version 1.7.0.Beta1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0b3ca1c113..b1545ceb00 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index c73676192b..42ea5baec4 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index e62d5e4682..b3af781388 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index b480e24a06..fb4444e00d 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 21a2b151bd..d36a6428d6 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 71a61fc2fe..a9037e613c 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 01b97c10bf..f81ea2c5d1 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - ${git.commit.author.time} + 2026-02-01T20:59:32Z 1.0.0.Alpha3 3.6.2 diff --git a/pom.xml b/pom.xml index f55f0955d3..114085f46e 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 622ba924ee..608175a8e2 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml From fc2d5e520c5bceff54c302641431465410bdf031 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 1 Feb 2026 22:44:20 +0100 Subject: [PATCH 532/597] Move jReleaser artifactOverrides in the right place --- parent/pom.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index f81ea2c5d1..962839bf19 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -885,15 +885,15 @@ RELEASE https://central.sonatype.com/api/v1/publisher ${maven.multiModuleProjectDirectory}/target/staging-deploy + + + org.mapstruct + mapstruct-jdk8 + false + false + + - - - org.mapstruct - mapstruct-jdk8 - false - false - - From 744c398326e9fa281134330dafdbdfecf1653c02 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 1 Feb 2026 22:44:32 +0100 Subject: [PATCH 533/597] Revert "Releasing version 1.7.0.Beta1" This reverts commit ab72ced6fb7a71591d51343493bf736379c61beb. --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index b1545ceb00..0b3ca1c113 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 42ea5baec4..c73676192b 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index b3af781388..e62d5e4682 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index fb4444e00d..b480e24a06 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index d36a6428d6..21a2b151bd 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index a9037e613c..71a61fc2fe 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 962839bf19..8a46290cb8 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - 2026-02-01T20:59:32Z + ${git.commit.author.time} 1.0.0.Alpha3 3.6.2 diff --git a/pom.xml b/pom.xml index 114085f46e..f55f0955d3 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 608175a8e2..622ba924ee 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml From 84318113ae02adae62e1d92ee5b884d6b58337b1 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 21:45:33 +0000 Subject: [PATCH 534/597] Releasing version 1.7.0.Beta1 --- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 ++-- pom.xml | 2 +- processor/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-config/pom.xml b/build-config/pom.xml index 0b3ca1c113..b1545ceb00 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index c73676192b..42ea5baec4 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index e62d5e4682..b3af781388 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index b480e24a06..fb4444e00d 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 21a2b151bd..d36a6428d6 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index 71a61fc2fe..a9037e613c 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 8a46290cb8..02fd68fcff 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - ${git.commit.author.time} + 2026-02-01T21:45:32Z 1.0.0.Alpha3 3.6.2 diff --git a/pom.xml b/pom.xml index f55f0955d3..114085f46e 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 622ba924ee..608175a8e2 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0-SNAPSHOT + 1.7.0.Beta1 ../parent/pom.xml From c7a0385903bfadfb8ef4779be2707b469fc2ace1 Mon Sep 17 00:00:00 2001 From: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 22:02:16 +0000 Subject: [PATCH 535/597] Next version 1.7.0-SNAPSHOT --- NEXT_RELEASE_CHANGELOG.md | 69 --------------------------------------- build-config/pom.xml | 2 +- core-jdk8/pom.xml | 2 +- core/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- integrationtest/pom.xml | 2 +- parent/pom.xml | 4 +-- pom.xml | 2 +- processor/pom.xml | 2 +- 10 files changed, 10 insertions(+), 79 deletions(-) diff --git a/NEXT_RELEASE_CHANGELOG.md b/NEXT_RELEASE_CHANGELOG.md index c4ba49e24d..e0f4cd31f0 100644 --- a/NEXT_RELEASE_CHANGELOG.md +++ b/NEXT_RELEASE_CHANGELOG.md @@ -1,79 +1,10 @@ ### Features -* Support for Java 21 Sequenced Collections (#3240) -* Native support for `java.util.Optional` mapping (#674) - MapStruct now fully supports Optional as both source and target types: - - `Optional` to `Optional` - Both source and target wrapped in `Optional` - - `Optional` to Non-`Optional` - Unwrapping `Optional` values - - Non-`Optional` to `Optional` - Wrapping values in `Optional` - - `Optional` properties in beans with automatic presence checks. Note, there is no null check done for `Optional` properties. -* Improved support for Kotlin. Requires use of `org.jetbrains.kotlin:kotlin-metadata-jvm`. - - Data Classes (#2281, #2577, #3031) - MapStruct now properly handles: - - Single field data classes - - Proper primary constructor detection - - Data classes with multiple constructors - - Data classes with all default parameters - - Sealed Classes (#3404) - Subclass exhaustiveness is now checked for Kotlin sealed classes -* Add support for ignoring multiple target properties at once (#3838) - Using new annotation `@Ignored` - ### Enhancements -* Add support for locale parameter for numberFormat and dateFormat (#3628) -* Detect Builder without a factory method (#3729) - With this if there is an inner class that ends with `Builder` and has a constructor with parameters, -it will be treated as a potential builder. -Builders through static methods on the type have a precedence. -* Add support for custom exception for subclass exhaustive strategy for `@SubclassMapping` mapping (#3821) - Available on `@BeanMapping`, `@Mapper` and `@MappingConfig`. -* Add new `NullValuePropertyMappingStrategy#CLEAR` for clearing Collection and Map properties when updating a bean (#1830) -* Use deterministic order for supporting fields and methods (#3940) -* Support `@AnnotatedWith` on decorators (#3659) -* Behaviour change: Add warning/error for redundant `ignoreUnmappedSourceProperties` entries (#3906) -* Behaviour change: Warning when the target has no target properties (#1140) -* Behaviour change: Initialize `Optional` with `Optional.empty` instead of `null` (#3852) -* Behaviour change: Mark `String` to `Number` as lossy conversion (#3848) - ### Bugs -* Improve error message when mapping non-iterable to array (#3786) -* Fix conditional mapping with `@TargetPropertyName` failing for nested update mappings (#3809) -* Resolve duplicate invocation of overloaded lifecycle methods with inheritance (#3849) - It is possible to disable this by using the new compiler option `mapstruct.disableLifecycleOverloadDeduplicateSelector`. -* Support generic `@Context` (#3711) -* Properly apply `NullValuePropertyMappingStrategy.IGNORE` for collections / maps without setters (#3806) -* Properly recognize the type of public generic fields (#3807) -* Fix method in Record is treated as a fluent setter (#3886) -* Ensure `NullValuePropertyMappingStrategy.SET_TO_DEFAULT` initializes empty collection/map when target is null (#3884) -* Fix Compiler error when mapping an object named `Override` (#3905) - ### Documentation -* General Improvements - * Javadoc - * Typos in comments - * Small code refactorings - ### Build -* Move Windows and MacOS builds outside of the main workflow -* Update release to release using the new Maven Central Portal -* Skip codecov coverage on forks -* Improve testing support for Kotlin - -### Behaviour Change - -#### Warning when the target has no target properties (#1140) - -With this change, if the target bean does not have any target properties, a warning will be shown. -This is like this to avoid potential mistakes by users, where they might think that the target bean has properties, but it does not. - -#### Warning for redundant `ignoreUnmappedSourceProperties` entries (#3906) - -With this change, if the `ignoreUnmappedSourceProperties` configuration contains properties that are actually mapped, a warning or compiler error will be shown. -The `unmappedSourcePolicy` is used to determine whether a warning, or an error is shown. - -#### Initialize `Optional` with `Optional.empty` instead of `null` (#3852) - -With this change, if the target `Optional` property is null, it will be initialized with `Optional.empty()` instead of `null`. - -#### Mark `String` to `Number` as lossy conversion (#3848) - -With this change, if the source `String` property is mapped to a `Number` property, a warning will be shown. -This is similar to what is happening when mapping `long` to `int`, etc. -The `typeConversionPolicy` `ReportingPolicy` is used to determine whether a warning, error or ignore is shown. diff --git a/build-config/pom.xml b/build-config/pom.xml index b1545ceb00..0b3ca1c113 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core-jdk8/pom.xml b/core-jdk8/pom.xml index 42ea5baec4..c73676192b 100644 --- a/core-jdk8/pom.xml +++ b/core-jdk8/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/core/pom.xml b/core/pom.xml index b3af781388..e62d5e4682 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index fb4444e00d..b480e24a06 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index d36a6428d6..21a2b151bd 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml index a9037e613c..71a61fc2fe 100644 --- a/integrationtest/pom.xml +++ b/integrationtest/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml diff --git a/parent/pom.xml b/parent/pom.xml index 02fd68fcff..8a46290cb8 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -11,7 +11,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT pom MapStruct Parent @@ -28,7 +28,7 @@ ${java.version} ${java.version} - 2026-02-01T21:45:32Z + ${git.commit.author.time} 1.0.0.Alpha3 3.6.2 diff --git a/pom.xml b/pom.xml index 114085f46e..f55f0955d3 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT parent/pom.xml diff --git a/processor/pom.xml b/processor/pom.xml index 608175a8e2..622ba924ee 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -12,7 +12,7 @@ org.mapstruct mapstruct-parent - 1.7.0.Beta1 + 1.7.0-SNAPSHOT ../parent/pom.xml From 5cf1a98d29975aa32fa4a6e2a78e2808bc8fdec9 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 2 Feb 2026 11:48:24 +0100 Subject: [PATCH 536/597] Fix location for Javadoc when generating distribution zip --- distribution/src/main/assembly/dist.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/src/main/assembly/dist.xml b/distribution/src/main/assembly/dist.xml index f0c727f8da..e0bc00496b 100644 --- a/distribution/src/main/assembly/dist.xml +++ b/distribution/src/main/assembly/dist.xml @@ -76,7 +76,7 @@ - target/site/apidocs + target/reports/apidocs docs/api From 06e27d575365147ed17a991c5cc90a1f63436eda Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:56:16 +0100 Subject: [PATCH 537/597] Remove unnecessary `keySet()` invocation (#3989) --- .../ap/internal/model/NestedTargetPropertyMappingHolder.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java index 2b407e240c..f7b28b3f84 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedTargetPropertyMappingHolder.java @@ -151,8 +151,7 @@ public NestedTargetPropertyMappingHolder build() { entryByTP.getValue(), groupedByTP.singleTargetReferences.get( targetProperty ) ); - boolean multipleSourceParametersForTP = - groupedBySourceParam.groupedBySourceParameter.keySet().size() > 1; + boolean multipleSourceParametersForTP = groupedBySourceParam.groupedBySourceParameter.size() > 1; // All not processed mappings that should have been applied to all are part of the unprocessed // defined targets From aff971d7b713bcc15eb750840b00369b61cb28f5 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 2 Feb 2026 14:08:13 +0100 Subject: [PATCH 538/597] Let GitHub determine whether or not the released version is the latest or not --- parent/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/parent/pom.xml b/parent/pom.xml index 8a46290cb8..ffa9952de5 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -904,6 +904,11 @@ ${maven.multiModuleProjectDirectory}/NEXT_RELEASE_CHANGELOG.md + + legacy From e9ebec2307fed606fb58f60d2e2226caf9f47614 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 2 Feb 2026 14:08:56 +0100 Subject: [PATCH 539/597] Factory method for LinkedHashMap and LinkedHashSet is always there for SequencedSet and SequencedMap (#3990) --- .../mapstruct/ap/internal/model/common/TypeFactory.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index c2646f63db..eb2c1dee07 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -152,15 +152,11 @@ public TypeFactory(ElementUtils elementUtils, TypeUtils typeUtils, FormattingMes ); implementationTypes.put( JavaCollectionConstants.SEQUENCED_SET_FQN, - sourceVersionAtLeast19 ? - withFactoryMethod( getType( LinkedHashSet.class ), LINKED_HASH_SET_FACTORY_METHOD_NAME ) : - withLoadFactorAdjustment( getType( LinkedHashSet.class ) ) + withFactoryMethod( getType( LinkedHashSet.class ), LINKED_HASH_SET_FACTORY_METHOD_NAME ) ); implementationTypes.put( JavaCollectionConstants.SEQUENCED_MAP_FQN, - sourceVersionAtLeast19 ? - withFactoryMethod( getType( LinkedHashMap.class ), LINKED_HASH_MAP_FACTORY_METHOD_NAME ) : - withLoadFactorAdjustment( getType( LinkedHashMap.class ) ) + withFactoryMethod( getType( LinkedHashMap.class ), LINKED_HASH_MAP_FACTORY_METHOD_NAME ) ); this.loggingVerbose = loggingVerbose; From 15312d6e46fb7744cf63e90ec70f20f518190457 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Sat, 14 Feb 2026 21:14:53 +0100 Subject: [PATCH 540/597] Fix self check in equals of Type (#3995) --- .../java/org/mapstruct/ap/internal/model/common/Type.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 29bb3cd906..8a91c80f03 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -1303,8 +1303,8 @@ public boolean equals(Object obj) { Type other = (Type) obj; if ( this.isWildCardBoundByTypeVar() && other.isWildCardBoundByTypeVar() ) { - return ( this.hasExtendsBound() == this.hasExtendsBound() - || this.hasSuperBound() == this.hasSuperBound() ) + return ( this.hasExtendsBound() == other.hasExtendsBound() + || this.hasSuperBound() == other.hasSuperBound() ) && typeUtils.isSameType( getTypeBound().getTypeMirror(), other.getTypeBound().getTypeMirror() ); } else { From 588006727dea2beb8e0c6019e52bab95ec57993e Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Sat, 14 Feb 2026 21:53:45 +0100 Subject: [PATCH 541/597] Improve performance of `Type.describe()` by removing regex matching (#3991) --- .../org/mapstruct/ap/internal/model/common/Type.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 8a91c80f03..3e81cd622d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -1331,7 +1331,7 @@ public String describe() { } else { // name allows for inner classes - String name = getFullyQualifiedName().replaceFirst( "^" + getPackageName() + ".", "" ); + String name = getNameKeepingInnerClasses(); List typeParams = getTypeParameters(); if ( typeParams.isEmpty() ) { return name; @@ -1343,6 +1343,15 @@ public String describe() { } } + private String getNameKeepingInnerClasses() { + String packageNamePrefix = getPackageName() + "."; + String fullyQualifiedName = getFullyQualifiedName(); + if (fullyQualifiedName.startsWith( packageNamePrefix ) ) { + return fullyQualifiedName.substring( packageNamePrefix.length() ); + } + return fullyQualifiedName; + } + /** * * @return an identification that can be used as part in a forged method name. From 4a75490e6d8b622083720bf60596680b49ce5e31 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:56:19 +0100 Subject: [PATCH 542/597] Simplified boolean logic in ValueMappingMethod by removing inversion (#4007) --- .../ap/internal/model/ValueMappingMethod.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java index 66b6a75961..46af8f0efd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -184,8 +184,8 @@ private List enumToEnumMapping(Method method, Type sourceType, Typ List mappings = new ArrayList<>(); List unmappedSourceConstants = new ArrayList<>( sourceType.getEnumConstants() ); - boolean sourceErrorOccurred = !reportErrorIfMappedSourceEnumConstantsDontExist( method, sourceType ); - boolean targetErrorOccurred = !reportErrorIfMappedTargetEnumConstantsDontExist( method, targetType ); + boolean sourceErrorOccurred = reportErrorIfMappedSourceEnumConstantsDontExist( method, sourceType ); + boolean targetErrorOccurred = reportErrorIfMappedTargetEnumConstantsDontExist( method, targetType ); if ( sourceErrorOccurred || targetErrorOccurred ) { return mappings; } @@ -268,8 +268,8 @@ private List enumToStringMapping(Method method, Type sourceType ) List mappings = new ArrayList<>(); List unmappedSourceConstants = new ArrayList<>( sourceType.getEnumConstants() ); - boolean sourceErrorOccurred = !reportErrorIfMappedSourceEnumConstantsDontExist( method, sourceType ); - boolean anyRemainingUsedError = !reportErrorIfSourceEnumConstantsContainsAnyRemaining( method ); + boolean sourceErrorOccurred = reportErrorIfMappedSourceEnumConstantsDontExist( method, sourceType ); + boolean anyRemainingUsedError = reportErrorIfSourceEnumConstantsContainsAnyRemaining( method ); if ( sourceErrorOccurred || anyRemainingUsedError ) { return mappings; } @@ -298,7 +298,7 @@ private List stringToEnumMapping(Method method, Type targetType ) List mappings = new ArrayList<>(); List unmappedSourceConstants = new ArrayList<>( targetType.getEnumConstants() ); - boolean sourceErrorOccurred = !reportErrorIfMappedTargetEnumConstantsDontExist( method, targetType ); + boolean sourceErrorOccurred = reportErrorIfMappedTargetEnumConstantsDontExist( method, targetType ); reportWarningIfAnyRemainingOrAnyUnMappedMissing( method ); if ( sourceErrorOccurred ) { return mappings; @@ -372,7 +372,7 @@ else if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { foundIncorrectMapping = true; } } - return !foundIncorrectMapping; + return foundIncorrectMapping; } private boolean reportErrorIfSourceEnumConstantsContainsAnyRemaining(Method method) { @@ -388,7 +388,7 @@ private boolean reportErrorIfSourceEnumConstantsContainsAnyRemaining(Method meth ); foundIncorrectMapping = true; } - return !foundIncorrectMapping; + return foundIncorrectMapping; } private void reportWarningIfAnyRemainingOrAnyUnMappedMissing(Method method) { @@ -462,7 +462,7 @@ else if ( valueMappings.nullTarget == null && valueMappings.nullValueTarget != n ); } - return !foundIncorrectMapping; + return foundIncorrectMapping; } private Type determineUnexpectedValueMappingException() { From 66625e61a4c4cbe72b58cb4187fde2cdf8e84fe4 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Wed, 4 Mar 2026 16:01:38 +0100 Subject: [PATCH 543/597] Simplify fail in assertCheckstyleRules (#4003) --- .../org/mapstruct/ap/testutil/runner/CompilingExtension.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java index a02ab2fe47..2599e62c2f 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java @@ -55,6 +55,7 @@ import org.xml.sax.InputSource; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation; import static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations; @@ -237,8 +238,7 @@ private void assertCheckstyleRules() throws Exception { int errors = checker.process( findGeneratedFiles( new File( sourceOutputDir ) ) ); if ( errors > 0 ) { String errorLog = errorStream.toString( "UTF-8" ); - assertThat( true ).describedAs( "Expected checkstyle compliant output, but got errors:\n" + errorLog ) - .isEqualTo( false ); + fail( "Expected checkstyle compliant output, but got errors:\n" + errorLog ); } } } From 876a62d282af0ca0c5b6bf21070c5441db95c9b7 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Wed, 4 Mar 2026 16:04:23 +0100 Subject: [PATCH 544/597] Update license plugin (#3999) --- .../build.gradle | 5 ++ .../settings.gradle | 5 ++ parent/pom.xml | 72 ++++++++++--------- pom.xml | 10 ++- 4 files changed, 55 insertions(+), 37 deletions(-) diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle index e62a8d087f..0df032f0f2 100644 --- a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle +++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/build.gradle @@ -1,3 +1,8 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ plugins { id 'java' } diff --git a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle index f62a77ab7b..b6ca918e91 100644 --- a/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle +++ b/integrationtest/src/test/resources/gradleIncrementalCompilationTest/settings.gradle @@ -1 +1,6 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ rootProject.name = 'gradle-incremental-compilation-test' \ No newline at end of file diff --git a/parent/pom.xml b/parent/pom.xml index ffa9952de5..ad95ffacf3 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -509,9 +509,9 @@ ${kotlin.version} - com.mycila.maven-license-plugin - maven-license-plugin - 1.10.b1 + com.mycila + license-maven-plugin + 4.6 org.codehaus.mojo @@ -649,39 +649,43 @@ - com.mycila.maven-license-plugin - maven-license-plugin + com.mycila + license-maven-plugin -
        ${basedir}/../etc/license.txt
        true - - **/.idea/** - **/.mvn/** - **/build-config/checkstyle.xml - **/build-config/import-control.xml - copyright.txt - **/LICENSE.txt - **/mapstruct.xml - **/ci-settings.xml - **/eclipse-formatter-config.xml - **/forbidden-apis.txt - **/checkstyle-for-generated-sources.xml - **/nb-configuration.xml - **/junit-platform.properties - maven-settings.xml - readme.md - CONTRIBUTING.md - NEXT_RELEASE_CHANGELOG.md - .gitattributes - .gitignore - .factorypath - .checkstyle - *.yml - mvnw* - **/*.asciidoc - **/binding.xjb - **/*.flattened-pom.xml - + + +
        ${basedir}/../etc/license.txt
        + + **/.idea/** + **/.mvn/** + **/build-config/checkstyle.xml + **/build-config/import-control.xml + copyright.txt + **/LICENSE.txt + **/mapstruct.xml + **/ci-settings.xml + **/eclipse-formatter-config.xml + **/forbidden-apis.txt + **/checkstyle-for-generated-sources.xml + **/nb-configuration.xml + **/junit-platform.properties + maven-settings.xml + readme.md + CONTRIBUTING.md + NEXT_RELEASE_CHANGELOG.md + .gitattributes + .gitignore + .factorypath + .checkstyle + *.yml + mvnw* + **/*.asciidoc + **/binding.xjb + **/*.flattened-pom.xml + +
        +
        SLASHSTAR_STYLE SLASHSTAR_STYLE diff --git a/pom.xml b/pom.xml index f55f0955d3..c753e3c1e4 100644 --- a/pom.xml +++ b/pom.xml @@ -35,10 +35,14 @@ - com.mycila.maven-license-plugin - maven-license-plugin + com.mycila + license-maven-plugin -
        etc/license.txt
        + + +
        etc/license.txt
        +
        +
        XML_STYLE SLASHSTAR_STYLE From f0d488712f76e02a51218e5eeaefb65bd2680a32 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Wed, 4 Mar 2026 22:43:48 +0100 Subject: [PATCH 545/597] Use StandardCharsets.UTF_8 in tests (#4002) --- .../mapstruct/ap/testutil/runner/CompilingExtension.java | 3 ++- .../org/mapstruct/ap/testutil/runner/GeneratedSource.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java index 2599e62c2f..cb12be725c 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingExtension.java @@ -10,6 +10,7 @@ import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -237,7 +238,7 @@ private void assertCheckstyleRules() throws Exception { int errors = checker.process( findGeneratedFiles( new File( sourceOutputDir ) ) ); if ( errors > 0 ) { - String errorLog = errorStream.toString( "UTF-8" ); + String errorLog = errorStream.toString( StandardCharsets.UTF_8 ); fail( "Expected checkstyle compliant output, but got errors:\n" + errorLog ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java index 18a53b4971..6785ea5cf2 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/GeneratedSource.java @@ -6,9 +6,9 @@ package org.mapstruct.ap.testutil.runner; import java.io.File; -import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -119,7 +119,7 @@ public JavaFileAssert forJavaFile(String path) { return new JavaFileAssert( new File( sourceOutputDir.get() + "/" + path ) ); } - private void handleFixtureComparison() throws UnsupportedEncodingException { + private void handleFixtureComparison() { for ( Class fixture : fixturesFor ) { String fixtureName = getMapperName( fixture ); URL expectedFile = getExpectedResource( fixtureName ); @@ -131,7 +131,7 @@ private void handleFixtureComparison() throws UnsupportedEncodingException { ) ); } else { - File expectedResource = new File( URLDecoder.decode( expectedFile.getFile(), "UTF-8" ) ); + File expectedResource = new File( URLDecoder.decode( expectedFile.getFile(), StandardCharsets.UTF_8 ) ); forMapper( fixture ).hasSameMapperContent( expectedResource ); } fixture.getPackage().getName(); From 1f3524570e362c34f4d0d7ce983c3596c83ad7c7 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 16 Mar 2026 00:25:03 +0100 Subject: [PATCH 546/597] Add missing self reference in GeneratedTypeBuilder (#4009) --- .../java/org/mapstruct/ap/internal/model/GeneratedType.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java index 0bc3ec7bf0..4b39e92273 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/GeneratedType.java @@ -30,9 +30,9 @@ public abstract class GeneratedType extends ModelElement { private static final String JAVA_LANG_PACKAGE = "java.lang"; - protected abstract static class GeneratedTypeBuilder { + protected abstract static class GeneratedTypeBuilder> { - private T myself; + private final T myself; protected TypeFactory typeFactory; protected ElementUtils elementUtils; protected Options options; From bff3efd10640170a5ad10dea8a5fa0e4173a932a Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 16 Mar 2026 00:25:54 +0100 Subject: [PATCH 547/597] Remove obsolete override of AssertJ version in integration tests (#4015) --- integrationtest/src/test/resources/pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/integrationtest/src/test/resources/pom.xml b/integrationtest/src/test/resources/pom.xml index c2ece3b38d..3053b211e1 100644 --- a/integrationtest/src/test/resources/pom.xml +++ b/integrationtest/src/test/resources/pom.xml @@ -26,8 +26,6 @@ ${mapstruct.version} - - 1.7.1 From 94da2c3f05af31ab9693056aab9785549212940b Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 16 Mar 2026 00:30:40 +0100 Subject: [PATCH 548/597] Remove unsued methods in Fields leftover from c2e803403027f3fae92bd15b0ba50ab7df5063e6 (#4010) --- .../mapstruct/ap/internal/util/Fields.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java index 8e16a95274..b2f8ac1442 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Fields.java @@ -6,13 +6,7 @@ package org.mapstruct.ap.internal.util; import javax.lang.model.element.Modifier; -import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; - -import org.mapstruct.ap.spi.TypeHierarchyErroneousException; /** * Provides functionality around {@link VariableElement}s. @@ -35,17 +29,4 @@ static boolean isPublic(VariableElement method) { private static boolean isNotStatic(VariableElement method) { return !method.getModifiers().contains( Modifier.STATIC ); } - - private static TypeElement asTypeElement(TypeMirror mirror) { - return (TypeElement) ( (DeclaredType) mirror ).asElement(); - } - - private static boolean hasNonObjectSuperclass(TypeElement element) { - if ( element.getSuperclass().getKind() == TypeKind.ERROR ) { - throw new TypeHierarchyErroneousException( element ); - } - - return element.getSuperclass().getKind() == TypeKind.DECLARED - && !asTypeElement( element.getSuperclass() ).getQualifiedName().toString().equals( "java.lang.Object" ); - } } From ded3daa5fcad390d60fe04d4d8e9479466722d63 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 16 Mar 2026 00:31:23 +0100 Subject: [PATCH 549/597] Upgrade Visitor6 to Visitor8 (#4011) --- .../src/main/java/org/mapstruct/ap/MappingProcessor.java | 4 ++-- .../mapstruct/ap/internal/util/AccessorNamingUtils.java | 8 ++++---- .../mapstruct/ap/spi/DefaultAccessorNamingStrategy.java | 8 ++++---- .../java/org/mapstruct/ap/spi/DefaultBuilderProvider.java | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index a66762d739..32e65c7047 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -30,7 +30,7 @@ import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.ElementKindVisitor6; +import javax.lang.model.util.ElementKindVisitor8; import javax.tools.Diagnostic.Kind; import org.mapstruct.ap.internal.gem.MapperGem; @@ -402,7 +402,7 @@ private R process(ProcessorContext context, ModelElementProcessor p private TypeElement asTypeElement(Element element) { return element.accept( - new ElementKindVisitor6() { + new ElementKindVisitor8() { @Override public TypeElement visitTypeAsInterface(TypeElement e, Void p) { return e; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java index 0f17946f72..8025acfeb1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AccessorNamingUtils.java @@ -10,8 +10,8 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.SimpleElementVisitor6; -import javax.lang.model.util.SimpleTypeVisitor6; +import javax.lang.model.util.SimpleElementVisitor8; +import javax.lang.model.util.SimpleTypeVisitor8; import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.AccessorType; @@ -84,7 +84,7 @@ public String getElementNameForAdder(Accessor adderMethod) { private static String getQualifiedName(TypeMirror type) { DeclaredType declaredType = type.accept( - new SimpleTypeVisitor6() { + new SimpleTypeVisitor8() { @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { return t; @@ -98,7 +98,7 @@ public DeclaredType visitDeclared(DeclaredType t, Void p) { } TypeElement typeElement = declaredType.asElement().accept( - new SimpleElementVisitor6() { + new SimpleElementVisitor8() { @Override public TypeElement visitType(TypeElement e, Void p) { return e; diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java index e7529dcd08..a726e9691f 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java @@ -14,8 +14,8 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; -import javax.lang.model.util.SimpleElementVisitor6; -import javax.lang.model.util.SimpleTypeVisitor6; +import javax.lang.model.util.SimpleElementVisitor8; +import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; import kotlin.Metadata; @@ -273,7 +273,7 @@ public String getElementName(ExecutableElement adderMethod) { */ protected static String getQualifiedName(TypeMirror type) { DeclaredType declaredType = type.accept( - new SimpleTypeVisitor6() { + new SimpleTypeVisitor8() { @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { return t; @@ -287,7 +287,7 @@ public DeclaredType visitDeclared(DeclaredType t, Void p) { } TypeElement typeElement = declaredType.asElement().accept( - new SimpleElementVisitor6() { + new SimpleElementVisitor8() { @Override public TypeElement visitType(TypeElement e, Void p) { return e; diff --git a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java index 333a11dbcc..c1126fa9fb 100644 --- a/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java @@ -21,8 +21,8 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; -import javax.lang.model.util.SimpleElementVisitor6; -import javax.lang.model.util.SimpleTypeVisitor6; +import javax.lang.model.util.SimpleElementVisitor8; +import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; /** @@ -126,7 +126,7 @@ private TypeElement getTypeElement(DeclaredType declaredType) { } return declaredType.asElement().accept( - new SimpleElementVisitor6() { + new SimpleElementVisitor8() { @Override public TypeElement visitType(TypeElement e, Void p) { return e; @@ -148,7 +148,7 @@ private DeclaredType getDeclaredType(TypeMirror type) { throw new TypeHierarchyErroneousException( type ); } return type.accept( - new SimpleTypeVisitor6() { + new SimpleTypeVisitor8() { @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { return t; From 0780d98fc2045092adbfe4417e46d853ecd946b7 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 16 Mar 2026 00:37:00 +0100 Subject: [PATCH 550/597] Upgrade freemarker to 2.3.34 (#4012) --- parent/pom.xml | 2 +- .../org/mapstruct/ap/internal/model/Annotation.ftl | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index ad95ffacf3..88bfd5053c 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -119,7 +119,7 @@ org.freemarker freemarker - 2.3.32 + 2.3.34 org.assertj diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl index 81f3d2ba8b..0b6737fbe4 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/Annotation.ftl @@ -7,12 +7,10 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.Annotation" --> <#switch properties?size> - <#case 0> + <#on 0> @<@includeModel object=type/><#rt> - <#break> - <#case 1> + <#on 1> @<@includeModel object=type/>(<@includeModel object=properties[0]/>)<#rt> - <#break> <#default> @<@includeModel object=type/>( <#list properties as property> From e0fa28868d8e01ecdb9df23440464aa9f1f07832 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 16 Mar 2026 00:37:41 +0100 Subject: [PATCH 551/597] Remove deprecated Number api usage from tests (#4013) --- .../ap/test/selection/twosteperror/ErroneousMapperMC.java | 2 +- .../mapstruct/ap/test/source/constants/SourceConstantsTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMC.java b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMC.java index 4b7dcf90a3..b6381cf6da 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMC.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/twosteperror/ErroneousMapperMC.java @@ -22,7 +22,7 @@ default BigDecimal methodX1(SourceType s) { } default Double methodX2(SourceType s) { - return new Double( s.t1 ); + return Double.valueOf( s.t1 ); } // CHECKSTYLE:OFF diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java index b331a2efba..1b1764fe56 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/constants/SourceConstantsTest.java @@ -47,7 +47,7 @@ public void shouldMapSameSourcePropertyToSeveralTargetProperties() throws ParseE assertThat( target.getStringConstant() ).isEqualTo( "stringConstant" ); assertThat( target.getEmptyStringConstant() ).isEqualTo( "" ); assertThat( target.getIntegerConstant() ).isEqualTo( 14 ); - assertThat( target.getLongWrapperConstant() ).isEqualTo( new Long( 3001L ) ); + assertThat( target.getLongWrapperConstant() ).isEqualTo( Long.valueOf( 3001L ) ); assertThat( target.getDateConstant() ).isEqualTo( getDate( "dd-MM-yyyy", "09-01-2014" ) ); assertThat( target.getNameConstants() ).isEqualTo( Arrays.asList( "jack", "jill", "tom" ) ); assertThat( target.getCountry() ).isEqualTo( CountryEnum.THE_NETHERLANDS ); From dab3eaf237eb025a0d59f997263fea515cb6db64 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 16 Mar 2026 00:44:29 +0100 Subject: [PATCH 552/597] #3949: Support SET_TO_NULL for overloaded target methods, requiring a cast (#3988) --- .../model/CollectionAssignmentBuilder.java | 3 +- .../ap/internal/model/PropertyMapping.java | 34 ++++++- .../model/assignment/SetterWrapper.java | 24 ++++- .../model/assignment/UpdateWrapper.java | 9 +- .../ap/internal/model/common/Type.java | 2 +- .../model/assignment/UpdateWrapper.ftl | 2 +- .../ap/internal/model/macro/CommonMacros.ftl | 2 +- .../ap/test/bugs/_3949/DateSource.java | 14 +++ .../test/bugs/_3949/Issue3949ClassMapper.java | 29 ++++++ .../bugs/_3949/Issue3949InterfaceMapper.java | 29 ++++++ .../ap/test/bugs/_3949/Issue3949Test.java | 94 +++++++++++++++++++ .../ap/test/bugs/_3949/ParentSource.java | 12 +++ .../ap/test/bugs/_3949/ParentTarget.java | 25 +++++ .../bugs/_3949/ParentTargetInterface.java | 14 +++ .../ap/test/bugs/_3949/StringSource.java | 12 +++ .../ap/test/bugs/_3949/TargetDate.java | 33 +++++++ .../test/bugs/_3949/TargetDateInterface.java | 18 ++++ .../ap/test/bugs/_3949/TargetString.java | 34 +++++++ .../bugs/_3949/TargetStringInterface.java | 18 ++++ 19 files changed, 399 insertions(+), 9 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/DateSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949ClassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949InterfaceMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentTarget.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentTargetInterface.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/StringSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetDate.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetDateInterface.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetString.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetStringInterface.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index 76e56cd976..db9e012d78 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -163,7 +163,8 @@ public Assignment build() { targetType, true, nvpms == SET_TO_NULL && !targetType.isPrimitive(), - nvpms == SET_TO_DEFAULT + nvpms == SET_TO_DEFAULT, + false ); } else if ( method.isUpdateMethod() && !targetImmutable ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 622ba9cef0..c120f035b2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -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; @@ -454,7 +456,8 @@ private Assignment assignToPlainViaSetter(Type targetType, Assignment rhs) { targetType, !rhs.isSourceReferenceParameter(), nvpms == SET_TO_NULL && !targetType.isPrimitive(), - nvpms == SET_TO_DEFAULT + nvpms == SET_TO_DEFAULT, + hasTwoOrMoreSettersWithName() ); } else { @@ -471,7 +474,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 + ); } } @@ -547,12 +553,33 @@ 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() ) + || ElementKind.INTERFACE.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() @@ -991,6 +1018,7 @@ public PropertyMapping build() { targetType, false, false, + false, false ); } else { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java index 7be948c857..94b031c622 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java @@ -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; @@ -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 nullCastType; public SetterWrapper(Assignment rhs, List thrownTypesToExclude, boolean fieldAssignment, boolean includeSourceNullCheck, boolean setExplicitlyToNull, - boolean setExplicitlyToDefault) { + boolean setExplicitlyToDefault, + boolean mustCastForNull, + Type nullCastType) { super( rhs, fieldAssignment ); this.thrownTypesToExclude = thrownTypesToExclude; this.includeSourceNullCheck = includeSourceNullCheck; this.setExplicitlyToDefault = setExplicitlyToDefault; this.setExplicitlyToNull = setExplicitlyToNull; + this.mustCastForNull = mustCastForNull; + this.nullCastType = nullCastType; } public SetterWrapper(Assignment rhs, List thrownTypesToExclude, boolean fieldAssignment ) { @@ -43,6 +51,8 @@ public SetterWrapper(Assignment rhs, List thrownTypesToExclude, boolean fi this.includeSourceNullCheck = false; this.setExplicitlyToNull = false; this.setExplicitlyToDefault = false; + this.mustCastForNull = false; + this.nullCastType = null; } @Override @@ -59,6 +69,15 @@ public List getThrownTypes() { return result; } + @Override + public Set getImportTypes() { + Set imported = new HashSet<>( super.getImportTypes() ); + if ( isSetExplicitlyToNull() && isMustCastForNull() ) { + imported.add( nullCastType ); + } + return imported; + } + public boolean isSetExplicitlyToNull() { return setExplicitlyToNull; } @@ -71,4 +90,7 @@ public boolean isIncludeSourceNullCheck() { return includeSourceNullCheck; } + public boolean isMustCastForNull() { + return mustCastForNull; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java index ff5089d6c2..c3d3f9c446 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java @@ -26,6 +26,7 @@ public class UpdateWrapper extends AssignmentWrapper { private final boolean includeSourceNullCheck; private final boolean setExplicitlyToNull; private final boolean setExplicitlyToDefault; + private final boolean mustCastForNull; public UpdateWrapper( Assignment decoratedAssignment, List thrownTypesToExclude, @@ -34,7 +35,8 @@ public UpdateWrapper( Assignment decoratedAssignment, Type targetType, boolean includeSourceNullCheck, boolean setExplicitlyToNull, - boolean setExplicitlyToDefault ) { + boolean setExplicitlyToDefault, + boolean mustCastForNull) { super( decoratedAssignment, fieldAssignment ); this.thrownTypesToExclude = thrownTypesToExclude; this.factoryMethod = factoryMethod; @@ -42,6 +44,7 @@ public UpdateWrapper( Assignment decoratedAssignment, this.includeSourceNullCheck = includeSourceNullCheck; this.setExplicitlyToDefault = setExplicitlyToDefault; this.setExplicitlyToNull = setExplicitlyToNull; + this.mustCastForNull = mustCastForNull; } private static Type determineImplType(Assignment factoryMethod, Type targetType) { @@ -100,4 +103,8 @@ public boolean isSetExplicitlyToNull() { public boolean isSetExplicitlyToDefault() { return setExplicitlyToDefault; } + + public boolean isMustCastForNull() { + return mustCastForNull; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 3e81cd622d..6287403ba3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -1061,7 +1061,7 @@ private TypeMirror boxed(TypeMirror possiblePrimitive) { * * @return an unmodifiable list of all setters */ - private List getSetters() { + public List getSetters() { if ( setters == null ) { setters = Collections.unmodifiableList( filters.setterMethodsIn( getAllMethods() ) ); } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl index d8ed4b53f6..933612f579 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl @@ -18,7 +18,7 @@ } <#if setExplicitlyToDefault || setExplicitlyToNull> else { - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>${ext.targetType.null}; + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else><#if mustCastForNull>(<@includeModel object=ext.targetType/>) ${ext.targetType.null}; } <#else> diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index 9c4ad26e03..f409fda793 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -45,7 +45,7 @@ } <#elseif setExplicitlyToDefault || setExplicitlyToNull> else { - <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>${ext.targetType.null}; + <#if ext.targetBeanName?has_content>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else><#if mustCastForNull!false>(<@includeModel object=ext.targetType/>) ${ext.targetType.null}; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/DateSource.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/DateSource.java new file mode 100644 index 0000000000..e542c388d4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/DateSource.java @@ -0,0 +1,14 @@ +/* + * 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 java.time.LocalDate; + +public class DateSource { + public LocalDate getDate() { + return null; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949ClassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949ClassMapper.java new file mode 100644 index 0000000000..135aa79056 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949ClassMapper.java @@ -0,0 +1,29 @@ +/* + * 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; + +@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_NULL) +public interface Issue3949ClassMapper { + + Issue3949ClassMapper INSTANCE = Mappers.getMapper( Issue3949ClassMapper.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); + + void updateParent(@MappingTarget ParentTarget target, ParentSource source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949InterfaceMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949InterfaceMapper.java new file mode 100644 index 0000000000..c25041ad2f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949InterfaceMapper.java @@ -0,0 +1,29 @@ +/* + * 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; + +@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_NULL) +public interface Issue3949InterfaceMapper { + + Issue3949InterfaceMapper INSTANCE = Mappers.getMapper( Issue3949InterfaceMapper.class ); + + void overwriteDate(@MappingTarget TargetDateInterface target, DateSource dateSource); + + void overwriteString(@MappingTarget TargetStringInterface target, StringSource stringSource); + + void overwriteDateWithConversion(@MappingTarget TargetDateInterface target, StringSource dateSource); + + void overwriteStringWithConversion(@MappingTarget TargetStringInterface target, DateSource stringSource); + + void updateParent(@MappingTarget ParentTargetInterface target, ParentSource source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949Test.java new file mode 100644 index 0000000000..b6abee819c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/Issue3949Test.java @@ -0,0 +1,94 @@ +/* + * 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") +@WithClasses({ + ParentSource.class, + ParentTargetInterface.class, + ParentTarget.class, + StringSource.class, + TargetStringInterface.class, + TargetString.class, + DateSource.class, + TargetDateInterface.class, + TargetDate.class +}) +public class Issue3949Test { + + @ProcessorTest + @WithClasses({ + Issue3949ClassMapper.class + }) + void shouldCompileAndSetCorrectlyToNullForClass() { + TargetDate shouldSetDateToNull = new TargetDate(); + Issue3949ClassMapper.INSTANCE.overwriteDate( shouldSetDateToNull, new DateSource() ); + assertThat( shouldSetDateToNull.getString() ).isNotNull(); + assertThat( shouldSetDateToNull.getDate() ).isNull(); + + shouldSetDateToNull = new TargetDate(); + Issue3949ClassMapper.INSTANCE.overwriteDateWithConversion( shouldSetDateToNull, new StringSource() ); + assertThat( shouldSetDateToNull.getString() ).isNotNull(); + assertThat( shouldSetDateToNull.getDate() ).isNull(); + + TargetString shouldSetStringToNull = new TargetString(); + Issue3949ClassMapper.INSTANCE.overwriteString( shouldSetStringToNull, new StringSource() ); + assertThat( shouldSetStringToNull.getDate() ).isNull(); + assertThat( shouldSetStringToNull.getDateValue() ).isNotNull(); + + shouldSetStringToNull = new TargetString(); + Issue3949ClassMapper.INSTANCE.overwriteStringWithConversion( shouldSetStringToNull, new DateSource() ); + assertThat( shouldSetStringToNull.getDate() ).isNull(); + assertThat( shouldSetStringToNull.getDateValue() ).isNotNull(); + + ParentTarget parentTarget = new ParentTarget(); + parentTarget.setChild( new ParentTarget() ); + Issue3949ClassMapper.INSTANCE.updateParent( parentTarget, new ParentSource() ); + assertThat( parentTarget.getChild() ).isNull(); + } + + @ProcessorTest + @WithClasses({ + Issue3949InterfaceMapper.class + }) + void shouldCompileAndSetCorrectlyToNullForInterface() { + TargetDateInterface shouldSetDateToNull = new TargetDate(); + Issue3949InterfaceMapper.INSTANCE.overwriteDate( shouldSetDateToNull, new DateSource() ); + assertThat( shouldSetDateToNull.getString() ).isNotNull(); + assertThat( shouldSetDateToNull.getDate() ).isNull(); + + shouldSetDateToNull = new TargetDate(); + Issue3949InterfaceMapper.INSTANCE.overwriteDateWithConversion( shouldSetDateToNull, new StringSource() ); + assertThat( shouldSetDateToNull.getString() ).isNotNull(); + assertThat( shouldSetDateToNull.getDate() ).isNull(); + + TargetStringInterface shouldSetStringToNull = new TargetString(); + Issue3949InterfaceMapper.INSTANCE.overwriteString( shouldSetStringToNull, new StringSource() ); + assertThat( shouldSetStringToNull.getDate() ).isNull(); + assertThat( shouldSetStringToNull.getDateValue() ).isNotNull(); + + shouldSetStringToNull = new TargetString(); + Issue3949InterfaceMapper.INSTANCE.overwriteStringWithConversion( shouldSetStringToNull, new DateSource() ); + assertThat( shouldSetStringToNull.getDate() ).isNull(); + assertThat( shouldSetStringToNull.getDateValue() ).isNotNull(); + + ParentTargetInterface parentTarget = new ParentTarget(); + parentTarget.setChild( new ParentTarget() ); + Issue3949InterfaceMapper.INSTANCE.updateParent( parentTarget, new ParentSource() ); + assertThat( parentTarget.getChild() ).isNull(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentSource.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentSource.java new file mode 100644 index 0000000000..551501aa24 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentSource.java @@ -0,0 +1,12 @@ +/* + * 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; + +public class ParentSource { + public ParentSource getChild() { + return null; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentTarget.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentTarget.java new file mode 100644 index 0000000000..3d8ca3b51b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentTarget.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.ap.test.bugs._3949; + +public class ParentTarget implements ParentTargetInterface { + private ParentTarget child; + + @Override + public ParentTarget getChild() { + return child; + } + + @Override + public void setChild(String child) { + throw new IllegalArgumentException(); + } + + @Override + public void setChild(ParentTarget child) { + this.child = child; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentTargetInterface.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentTargetInterface.java new file mode 100644 index 0000000000..5311fb4c9b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/ParentTargetInterface.java @@ -0,0 +1,14 @@ +/* + * 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; + +public interface ParentTargetInterface { + ParentTarget getChild(); + + void setChild(String child); + + void setChild(ParentTarget child); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/StringSource.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/StringSource.java new file mode 100644 index 0000000000..11ba1419d5 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/StringSource.java @@ -0,0 +1,12 @@ +/* + * 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; + +public class StringSource { + public String getDate() { + return null; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetDate.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetDate.java new file mode 100644 index 0000000000..85b12a618d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetDate.java @@ -0,0 +1,33 @@ +/* + * 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 java.time.LocalDate; + +public class TargetDate implements TargetDateInterface { + private LocalDate date = LocalDate.now(); + private String string = ""; + + @Override + public void setDate(LocalDate date) { + this.date = date; + } + + @Override + public void setDate(String date) { + this.string = date; + } + + @Override + public LocalDate getDate() { + return date; + } + + @Override + public String getString() { + return string; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetDateInterface.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetDateInterface.java new file mode 100644 index 0000000000..df28936914 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetDateInterface.java @@ -0,0 +1,18 @@ +/* + * 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 java.time.LocalDate; + +public interface TargetDateInterface { + void setDate(LocalDate date); + + void setDate(String date); + + LocalDate getDate(); + + String getString(); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetString.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetString.java new file mode 100644 index 0000000000..b8cbbb2bbb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetString.java @@ -0,0 +1,34 @@ +/* + * 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 java.time.LocalDate; + +public class TargetString implements TargetStringInterface { + private LocalDate date = LocalDate.now(); + private String string = ""; + + @Override + public void setDate(LocalDate date) { + this.date = date; + } + + @Override + public void setDate(String date) { + this.string = date; + } + + @Override + public String getDate() { + return string; + } + + @Override + public LocalDate getDateValue() { + return date; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetStringInterface.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetStringInterface.java new file mode 100644 index 0000000000..dd82231a51 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3949/TargetStringInterface.java @@ -0,0 +1,18 @@ +/* + * 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 java.time.LocalDate; + +public interface TargetStringInterface { + void setDate(LocalDate date); + + void setDate(String date); + + String getDate(); + + LocalDate getDateValue(); +} From 6d61c3ab9ea577ef12c0ff2e77e723f946696791 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:34:11 +0100 Subject: [PATCH 553/597] #3972: Update maven compiler plugin (#4022) * Revert "Block plexus.snapshots repository in GitHub actions #3972" This reverts commit 9d75a48df5e40c1ed87d2f68d96fbb35807e3b91 and updates the maven compiler plugin to 3.15.0 --- .github/workflows/java-ea.yml | 3 --- .github/workflows/macos.yml | 3 --- .github/workflows/main.yml | 6 ------ .github/workflows/release.yml | 4 ---- .github/workflows/windows.yml | 3 --- parent/pom.xml | 2 +- 6 files changed, 1 insertion(+), 20 deletions(-) diff --git a/.github/workflows/java-ea.yml b/.github/workflows/java-ea.yml index 70b81c40f1..fc3a4a0fb1 100644 --- a/.github/workflows/java-ea.yml +++ b/.github/workflows/java-ea.yml @@ -17,8 +17,5 @@ jobs: with: website: jdk.java.net release: EA - - uses: s4u/maven-settings-action@v4.0.0 - with: - mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Test' run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 98b4e156e0..833bb6ba39 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -16,8 +16,5 @@ jobs: with: distribution: 'zulu' java-version: 21 - - uses: s4u/maven-settings-action@v4.0.0 - with: - mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Test' run: ./mvnw ${MAVEN_ARGS} install diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ca66a22291..c4def2a89d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,9 +23,6 @@ jobs: with: distribution: 'zulu' java-version: ${{ matrix.java }} - - uses: s4u/maven-settings-action@v4.0.0 - with: - mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Test' run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=${{ matrix.java != 21 }} install -DskipDistribution=${{ matrix.java != 21 }} - name: 'Generate coverage report' @@ -62,8 +59,5 @@ jobs: with: distribution: 'zulu' java-version: ${{ matrix.java }} - - uses: s4u/maven-settings-action@v4.0.0 - with: - mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Run integration tests' run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4feca94a0..666a74656d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,10 +26,6 @@ jobs: distribution: 'zulu' cache: maven - - uses: s4u/maven-settings-action@v4.0.0 - with: - mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - - name: Set release version id: version run: | diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index fc37af6a9d..bda38f8783 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -16,8 +16,5 @@ jobs: with: distribution: 'zulu' java-version: 21 - - uses: s4u/maven-settings-action@v4.0.0 - with: - mirrors: '[{"id": "block-plexus-snapshots", "name": "Block Plexus Snapshots", "mirrorOf": "plexus.snapshots", "url": "http://localhost"}]' - name: 'Test' run: ./mvnw %MAVEN_ARGS% install diff --git a/parent/pom.xml b/parent/pom.xml index 88bfd5053c..c4ba63948d 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -413,7 +413,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.1 + 3.15.0 org.apache.maven.plugins From 0d3104908196b3ad22101dd5928ea5e6f4d85c88 Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:35:47 +0100 Subject: [PATCH 554/597] Use multi-catch in generated code (#4021) --- .../model/assignment/Java8FunctionWrapper.ftl | 10 +++------- .../model/assignment/LocalVarWrapper.ftl | 10 +++------- .../ap/internal/model/macro/CommonMacros.ftl | 11 +++++++--- .../adder/SourceTargetMapperImpl.java | 20 ++++--------------- 4 files changed, 18 insertions(+), 33 deletions(-) diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.ftl index ce1fcdcd6c..4c3b6d4804 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.ftl @@ -6,6 +6,7 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.assignment.Java8FunctionWrapper" --> +<#import "../macro/CommonMacros.ftl" as lib> <#assign sourceVarName><#if assignment.sourceLocalVarName?? >${assignment.sourceLocalVarName}<#else>${assignment.sourceReference} <#if (thrownTypes?size == 0) > <#compress> @@ -17,14 +18,9 @@ <#else> <#compress> ${sourceVarName} -> { - try { + <@lib.handleExceptions> return <@_assignment/>; - } - <#list thrownTypes as exceptionType> - catch ( <@includeModel object=exceptionType/> e ) { - throw new RuntimeException( e ); - } - + } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.ftl index 97ea856ece..163a52896a 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.ftl @@ -6,18 +6,14 @@ --> <#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.assignment.LocalVarWrapper" --> +<#import "../macro/CommonMacros.ftl" as lib> <#if (thrownTypes?size == 0) > <#if !ext.isTargetDefined?? ><@includeModel object=ext.targetType/> ${ext.targetWriteAccessorName} = <@_assignment/>; <#else> <#if !ext.isTargetDefined?? ><@includeModel object=ext.targetType/> ${ext.targetWriteAccessorName}; - try { + <@lib.handleExceptions> ${ext.targetWriteAccessorName} = <@_assignment/>; - } - <#list thrownTypes as exceptionType> - catch ( <@includeModel object=exceptionType/> e ) { - throw new RuntimeException( e ); - } - + <#macro _assignment> <@includeModel object=assignment diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index f409fda793..e857a807af 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -104,11 +104,16 @@ try { <#nested> } - <#list thrownTypes as exceptionType> - catch ( <@includeModel object=exceptionType/> e ) { + <@compress single_line=true>catch ( + <#list thrownTypes as exceptionType> + <#if exceptionType_index > 0> | + <@includeModel object=exceptionType/> + + e ) { + + throw new RuntimeException( e ); } - <#-- diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/adder/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/adder/SourceTargetMapperImpl.java index 823fbb1908..52d661c3f7 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/adder/SourceTargetMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/collection/adder/SourceTargetMapperImpl.java @@ -59,10 +59,7 @@ public Source toSource(Target source) { try { source1.setPets( petMapper.toSourcePets( source.getPets() ) ); } - catch ( CatException e ) { - throw new RuntimeException( e ); - } - catch ( DogException e ) { + catch ( CatException | DogException e ) { throw new RuntimeException( e ); } @@ -82,10 +79,7 @@ public void toExistingTarget(Source source, Target target) { } } } - catch ( CatException e ) { - throw new RuntimeException( e ); - } - catch ( DogException e ) { + catch ( CatException | DogException e ) { throw new RuntimeException( e ); } } @@ -161,10 +155,7 @@ public TargetViaTargetType toTargetViaTargetType(Source source) { } } } - catch ( CatException e ) { - throw new RuntimeException( e ); - } - catch ( DogException e ) { + catch ( CatException | DogException e ) { throw new RuntimeException( e ); } @@ -184,10 +175,7 @@ public Target fromSingleElementSource(SingleElementSource source) { target.addPet( petMapper.toPet( source.getPet() ) ); } } - catch ( CatException e ) { - throw new RuntimeException( e ); - } - catch ( DogException e ) { + catch ( CatException | DogException e ) { throw new RuntimeException( e ); } From 2d51aa0695272b1198d6c4cbe3458ca3ecbdeb9f Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:39:41 +0100 Subject: [PATCH 555/597] Refactor TypeFactory.getTypeParameters (#4020) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improves readability and avoids unnecessary overhead: * Stores `typeArguments` in a local variable (since calling `getTypeArguments()` is not cached in eclipse compiler) * Replace if/else with a ternary expression * Avoids repeated `getType(...)` calls --- .../ap/internal/model/common/TypeFactory.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index eb2c1dee07..606bbce69d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -523,15 +523,12 @@ private List getTypeParameters(TypeMirror mirror, boolean isImplementation } DeclaredType declaredType = (DeclaredType) mirror; - List typeParameters = new ArrayList<>( declaredType.getTypeArguments().size() ); + List typeArguments = declaredType.getTypeArguments(); + List typeParameters = new ArrayList<>( typeArguments.size() ); - for ( TypeMirror typeParameter : declaredType.getTypeArguments() ) { - if ( isImplementationType ) { - typeParameters.add( getType( typeParameter ).getTypeBound() ); - } - else { - typeParameters.add( getType( typeParameter ) ); - } + for ( TypeMirror typeParameter : typeArguments) { + Type type = getType( typeParameter ); + typeParameters.add( isImplementationType ? type.getTypeBound() : type ); } return typeParameters; From 9aea5a8e93c28fb824b59bd883b5c88bcb6c2e6d Mon Sep 17 00:00:00 2001 From: mk868 Date: Fri, 27 Mar 2026 07:44:34 +0100 Subject: [PATCH 556/597] #4018 Add URI to String built-in conversions (#4019) --- .../chapter-5-data-type-conversions.asciidoc | 3 + .../internal/conversion/ConversionUtils.java | 12 ++++ .../ap/internal/conversion/Conversions.java | 10 ++-- .../conversion/URIToStringConversion.java | 37 ++++++++++++ .../conversion/uri/URIConversionTest.java | 56 +++++++++++++++++++ .../ap/test/conversion/uri/URIMapper.java | 19 +++++++ .../ap/test/conversion/uri/URISource.java | 33 +++++++++++ .../ap/test/conversion/uri/URITarget.java | 30 ++++++++++ 8 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/URIToStringConversion.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URIConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URIMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URISource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URITarget.java diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 9da7ff75c1..d95e6c7a62 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -127,6 +127,9 @@ public interface CarMapper { * Between `String` and `StringBuilder` +* Between `java.net.URI` and `String`. +** When converting from a `String`, the value needs to be a valid https://datatracker.ietf.org/doc/html/rfc2396[RFC 2396 URI] otherwise an `IllegalArgumentException` is thrown. + * Between `java.net.URL` and `String`. ** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/URL[URL] otherwise a `MalformedURLException` is thrown. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java index 96960c4a11..ee052b5405 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URI; import java.net.URL; import java.sql.Time; import java.sql.Timestamp; @@ -269,6 +270,17 @@ public static String uuid(ConversionContext conversionContext) { return typeReferenceName( conversionContext, UUID.class ); } + /** + * Name for {@link java.net.URI}. + * + * @param conversionContext Conversion context + * + * @return Name or fully-qualified name. + */ + public static String uri(ConversionContext conversionContext) { + return typeReferenceName( conversionContext, URI.class ); + } + /** * Name for {@link java.net.URL}. * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 076cd2f42e..7b2f8702b1 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URI; import java.net.URL; import java.sql.Time; import java.sql.Timestamp; @@ -201,7 +202,7 @@ public Conversions(TypeFactory typeFactory) { register( UUID.class, String.class, new UUIDToStringConversion() ); register( Locale.class, String.class, new LocaleToStringConversion() ); - registerURLConversion(); + registerJavaNetConversions(); } private void registerJodaConversions() { @@ -306,13 +307,14 @@ private void registerBigDecimalConversion(Class targetType) { } } - private void registerURLConversion() { - if ( isJavaURLAvailable() ) { + private void registerJavaNetConversions() { + if ( isJavaNetAvailable() ) { + register( URI.class, String.class, new URIToStringConversion() ); register( URL.class, String.class, new URLToStringConversion() ); } } - private boolean isJavaURLAvailable() { + private boolean isJavaNetAvailable() { return typeFactory.isTypeAvailable( "java.net.URL" ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/URIToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/URIToStringConversion.java new file mode 100644 index 0000000000..c25383ae05 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/URIToStringConversion.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.internal.conversion; + +import java.net.URI; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; + +import static org.mapstruct.ap.internal.conversion.ConversionUtils.uri; +import static org.mapstruct.ap.internal.util.Collections.asSet; + +/** + * Conversion between {@link java.net.URI} and {@link String}. + * + * @author Maciej Kucharczyk + */ +public class URIToStringConversion extends SimpleConversion { + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toString()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return uri( conversionContext ) + ".create( )"; + } + + @Override + protected Set getFromConversionImportTypes(final ConversionContext conversionContext) { + return asSet( conversionContext.getTypeFactory().getType( URI.class ) ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URIConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URIConversionTest.java new file mode 100644 index 0000000000..d7d3ef4142 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URIConversionTest.java @@ -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.conversion.uri; + +import java.net.URI; + +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; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Tests conversions between {@link java.net.URI} and String. + * + * @author Maciej Kucharczyk + */ +@IssueKey("4018") +@WithClasses({ URISource.class, URITarget.class, URIMapper.class }) +public class URIConversionTest { + + @ProcessorTest + public void shouldApplyURIConversion() { + URISource source = new URISource(); + source.setUriA( URI.create( "https://mapstruct.org/" ) ); + + URITarget target = URIMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getUriA() ).isEqualTo( source.getUriA().toString() ); + } + + @ProcessorTest + public void shouldApplyReverseURIConversion() { + URITarget target = new URITarget(); + target.setUriA( "https://mapstruct.org/" ); + + URISource source = URIMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getUriA() ).isEqualTo( URI.create( target.getUriA() ) ); + } + + @ProcessorTest + public void shouldHandleInvalidURIString() { + URITarget target = new URITarget(); + target.setInvalidURI( "ht!tp://example.com" ); + + assertThatThrownBy( () -> URIMapper.INSTANCE.targetToSource( target ) ) + .isInstanceOf( IllegalArgumentException.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URIMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URIMapper.java new file mode 100644 index 0000000000..cac94a4efa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URIMapper.java @@ -0,0 +1,19 @@ +/* + * 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.conversion.uri; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface URIMapper { + + URIMapper INSTANCE = Mappers.getMapper( URIMapper.class ); + + URITarget sourceToTarget(URISource source); + + URISource targetToSource(URITarget target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URISource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URISource.java new file mode 100644 index 0000000000..502169fccb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URISource.java @@ -0,0 +1,33 @@ +/* + * 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.conversion.uri; + +import java.net.URI; + +/** + * @author Maciej Kucharczyk + */ +public class URISource { + private URI uriA; + + private URI invalidURI; + + public URI getUriA() { + return uriA; + } + + public void setUriA(URI uriA) { + this.uriA = uriA; + } + + public URI getInvalidURI() { + return invalidURI; + } + + public void setInvalidURI(URI invalidURI) { + this.invalidURI = invalidURI; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URITarget.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URITarget.java new file mode 100644 index 0000000000..b221392127 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uri/URITarget.java @@ -0,0 +1,30 @@ +/* + * 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.conversion.uri; + +/** + * @author Maciej Kucharczyk + */ +public class URITarget { + private String uriA; + private String invalidURI; + + public String getUriA() { + return uriA; + } + + public void setUriA(String uriA) { + this.uriA = uriA; + } + + public String getInvalidURI() { + return invalidURI; + } + + public void setInvalidURI(String invalidURI) { + this.invalidURI = invalidURI; + } +} From 6a567516be5e2e38a7314d892b08468c7823c6db Mon Sep 17 00:00:00 2001 From: hduelme <46139144+hduelme@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:06:09 +0100 Subject: [PATCH 557/597] Upgrade integration tests to junit5 (#4023) --- .../GradleIncrementalCompilationTest.java | 50 +++++++++---------- .../itest/auto/value/AutoValueMapperTest.java | 8 +-- .../src/test/resources/cdiTest/pom.xml | 8 +-- .../itest/cdi/CdiBasedMapperTest.java | 14 +++--- .../test/java/DefaultPackageTest.java | 6 +-- .../itest/textBlocks/TextBlocksTest.java | 6 +-- .../itest/externalbeanjar/ConversionTest.java | 6 +-- .../generator/pom.xml | 4 +- .../usage/pom.xml | 4 +- .../usage/FaultyAstModifyingTestTest.java | 6 +-- .../freebuilder/FreeBuilderMapperTest.java | 8 +-- .../mapstruct/itest/simple/AnimalTest.java | 8 +-- .../immutables/ImmutablesMapperTest.java | 8 +-- .../jaxb/JakartaJaxbBasedMapperTest.java | 8 +-- .../ap/test/bugs/_603/Issue603Test.java | 7 ++- .../ap/test/bugs/_636/Issue636Test.java | 6 +-- .../itest/java8/Java8MapperTest.java | 6 +-- .../itest/jaxb/JaxbBasedMapperTest.java | 8 +-- .../src/test/resources/jsr330Test/pom.xml | 4 ++ .../itest/jsr330/Jsr330BasedMapperTest.java | 16 +++--- .../itest/kotlin/data/KotlinDataTest.java | 8 +-- .../itest/lombok/LombokMapperTest.java | 8 +-- .../itest/lombok/LombokMapperTest.java | 8 +-- .../mapstruct/itest/modules/ModulesTest.java | 6 +-- .../mapstruct/itest/naming/NamingTest.java | 6 +-- integrationtest/src/test/resources/pom.xml | 5 +- .../itest/protobuf/ProtobufMapperTest.java | 8 +-- .../itest/records/module2/RecordsTest.java | 6 +-- .../itest/records/mapper/RecordsTest.java | 8 +-- .../mapstruct/itest/records/RecordsTest.java | 22 ++++---- .../records/nested/NestedRecordsTest.java | 6 +-- .../sealedsubclass/SealedSubclassTest.java | 10 ++-- .../itest/simple/ConversionTest.java | 18 +++---- .../src/test/resources/springTest/pom.xml | 4 ++ .../itest/spring/SpringBasedMapperTest.java | 16 +++--- .../superTypeGenerationTest/generator/pom.xml | 4 +- .../superTypeGenerationTest/usage/pom.xml | 4 +- .../usage/GeneratedBasesTest.java | 8 +-- .../generator/pom.xml | 4 +- .../targetTypeGenerationTest/usage/pom.xml | 4 +- .../usage/GeneratedTargetTypeTest.java | 8 +-- .../usesTypeGenerationTest/generator/pom.xml | 4 +- .../usesTypeGenerationTest/usage/pom.xml | 4 +- .../usage/GeneratedUsesTypeTest.java | 6 +-- parent/pom.xml | 7 +-- 45 files changed, 193 insertions(+), 190 deletions(-) diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java index 3d496906c0..a12af7690f 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/GradleIncrementalCompilationTest.java @@ -13,47 +13,49 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; import org.apache.commons.io.FileUtils; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; import org.gradle.testkit.runner.TaskOutcome; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Named; import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.runners.Parameterized.Parameters; +import static org.assertj.core.api.Assertions.assertThat; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE; -import static org.hamcrest.core.StringContains.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; /** *

        This is supposed to be run from the mapstruct root project folder. * Otherwise, use -Dmapstruct_root=path_to_project. */ @DisabledForJreRange(min = JRE.JAVA_11) -public class GradleIncrementalCompilationTest { +class GradleIncrementalCompilationTest { private static Path rootPath; - private static String projectDir = "integrationtest/src/test/resources/gradleIncrementalCompilationTest"; - private static String compileTaskName = "compileJava"; + private static final String PROJECT_DIR = "integrationtest/src/test/resources/gradleIncrementalCompilationTest"; + private static final String COMPILE_TASK_NAME = "compileJava"; @TempDir - File testBuildDir; + private File testBuildDir; @TempDir - File testProjectDir; + private File testProjectDir; private GradleRunner runner; private File sourceDirectory; private List compileArgs; // Gradle compile task arguments - @Parameters(name = "Gradle {0}") - public static List gradleVersions() { - return Arrays.asList( "5.0", "6.0" ); + static Stream gradleVersions() { + return Stream.of( + Arguments.of( Named.of( "Gradle 5.0", "5.0" ) ), + Arguments.of( Named.of( "Gradle 6.0", "6.0" ) ) ); } private void replaceInFile(File file, CharSequence target, CharSequence replacement) throws IOException { @@ -68,15 +70,13 @@ private GradleRunner getRunner(String... additionalArguments) { } private void assertCompileOutcome(BuildResult result, TaskOutcome outcome) { - assertEquals( outcome, result.task( ":" + compileTaskName ).getOutcome() ); + assertEquals( outcome, result.task( ":" + COMPILE_TASK_NAME ).getOutcome() ); } private void assertRecompiled(BuildResult result, int recompiledCount) { assertCompileOutcome( result, recompiledCount > 0 ? SUCCESS : UP_TO_DATE ); - assertThat( - result.getOutput(), - containsString( String.format( "Incremental compilation of %d classes completed", recompiledCount ) ) - ); + assertThat( result.getOutput() ) + .contains( String.format( "Incremental compilation of %d classes completed", recompiledCount ) ); } private List buildCompileArgs() { @@ -85,11 +85,11 @@ private List buildCompileArgs() { // Inject the path to the folder containing the mapstruct-processor JAR String jarDirectoryArg = "-PmapstructRootPath=" + rootPath.toString(); - return Arrays.asList( compileTaskName, buildDirPropertyArg, jarDirectoryArg ); + return Arrays.asList( COMPILE_TASK_NAME, buildDirPropertyArg, jarDirectoryArg ); } @BeforeAll - public static void setupClass() throws Exception { + static void setupClass() { rootPath = Paths.get( System.getProperty( "mapstruct_root", "." ) ).toAbsolutePath(); } @@ -102,7 +102,7 @@ public void setup(String gradleVersion) throws IOException { testProjectDir.mkdirs(); } // Copy test project files to the temp dir - Path gradleProjectPath = rootPath.resolve( projectDir ); + Path gradleProjectPath = rootPath.resolve( PROJECT_DIR ); FileUtils.copyDirectory( gradleProjectPath.toFile(), testProjectDir ); compileArgs = buildCompileArgs(); sourceDirectory = new File( testProjectDir, "src/main/java" ); @@ -111,7 +111,7 @@ public void setup(String gradleVersion) throws IOException { @ParameterizedTest @MethodSource("gradleVersions") - public void testBuildSucceeds(String gradleVersion) throws IOException { + void testBuildSucceeds(String gradleVersion) throws IOException { setup( gradleVersion ); // Make sure the test build setup actually compiles BuildResult buildResult = getRunner().build(); @@ -120,7 +120,7 @@ public void testBuildSucceeds(String gradleVersion) throws IOException { @ParameterizedTest @MethodSource("gradleVersions") - public void testUpToDate(String gradleVersion) throws IOException { + void testUpToDate(String gradleVersion) throws IOException { setup( gradleVersion ); getRunner().build(); BuildResult secondBuildResult = getRunner().build(); @@ -129,7 +129,7 @@ public void testUpToDate(String gradleVersion) throws IOException { @ParameterizedTest @MethodSource("gradleVersions") - public void testChangeConstant(String gradleVersion) throws IOException { + void testChangeConstant(String gradleVersion) throws IOException { setup( gradleVersion ); getRunner().build(); // Change return value in class Target @@ -143,7 +143,7 @@ public void testChangeConstant(String gradleVersion) throws IOException { @ParameterizedTest @MethodSource("gradleVersions") - public void testChangeTargetField(String gradleVersion) throws IOException { + void testChangeTargetField(String gradleVersion) throws IOException { setup( gradleVersion ); getRunner().build(); // Change target field in mapper interface @@ -157,7 +157,7 @@ public void testChangeTargetField(String gradleVersion) throws IOException { @ParameterizedTest @MethodSource("gradleVersions") - public void testChangeUnrelatedFile(String gradleVersion) throws IOException { + void testChangeUnrelatedFile(String gradleVersion) throws IOException { setup( gradleVersion ); getRunner().build(); File unrelatedFile = new File( sourceDirectory, "org/mapstruct/itest/gradle/lib/UnrelatedComponent.java" ); diff --git a/integrationtest/src/test/resources/autoValueBuilderTest/src/test/java/org/mapstruct/itest/auto/value/AutoValueMapperTest.java b/integrationtest/src/test/resources/autoValueBuilderTest/src/test/java/org/mapstruct/itest/auto/value/AutoValueMapperTest.java index 8f640780f3..4ce5530a5d 100644 --- a/integrationtest/src/test/resources/autoValueBuilderTest/src/test/java/org/mapstruct/itest/auto/value/AutoValueMapperTest.java +++ b/integrationtest/src/test/resources/autoValueBuilderTest/src/test/java/org/mapstruct/itest/auto/value/AutoValueMapperTest.java @@ -5,7 +5,7 @@ */ package org.mapstruct.itest.auto.value; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,10 +14,10 @@ * * @author Eric Martineau */ -public class AutoValueMapperTest { +class AutoValueMapperTest { @Test - public void testSimpleImmutableBuilderHappyPath() { + void testSimpleImmutableBuilderHappyPath() { PersonDto personDto = PersonMapper.INSTANCE.toDto( Person.builder() .age( 33 ) .name( "Bob" ) @@ -32,7 +32,7 @@ public void testSimpleImmutableBuilderHappyPath() { } @Test - public void testLombokToImmutable() { + void testLombokToImmutable() { Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) ); assertThat( person.getAge() ).isEqualTo( 33 ); assertThat( person.getName() ).isEqualTo( "Bob" ); diff --git a/integrationtest/src/test/resources/cdiTest/pom.xml b/integrationtest/src/test/resources/cdiTest/pom.xml index 0b3c394e82..cb00fbaa01 100644 --- a/integrationtest/src/test/resources/cdiTest/pom.xml +++ b/integrationtest/src/test/resources/cdiTest/pom.xml @@ -30,8 +30,8 @@ javax.inject - org.jboss.arquillian.junit - arquillian-junit-container + org.jboss.arquillian.junit5 + arquillian-junit5-container test @@ -45,8 +45,8 @@ test - org.jboss.arquillian.junit - arquillian-junit-core + org.jboss.arquillian.junit5 + arquillian-junit5-core test diff --git a/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java b/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java index 0c343fce27..4ede2eb78a 100644 --- a/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java +++ b/integrationtest/src/test/resources/cdiTest/src/test/java/org/mapstruct/itest/cdi/CdiBasedMapperTest.java @@ -10,12 +10,12 @@ import javax.inject.Inject; import org.jboss.arquillian.container.test.api.Deployment; -import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mapstruct.itest.cdi.other.DateMapper; /** @@ -23,8 +23,8 @@ * * @author Gunnar Morling */ -@RunWith( Arquillian.class ) -public class CdiBasedMapperTest { +@ExtendWith( ArquillianExtension.class ) +class CdiBasedMapperTest { @Inject private SourceTargetMapper mapper; @@ -45,7 +45,7 @@ public static JavaArchive createDeployment() { } @Test - public void shouldCreateCdiBasedMapper() { + void shouldCreateCdiBasedMapper() { Source source = new Source(); Target target = mapper.sourceToTarget( source ); @@ -56,7 +56,7 @@ public void shouldCreateCdiBasedMapper() { } @Test - public void shouldInjectDecorator() { + void shouldInjectDecorator() { Source source = new Source(); Target target = decoratedMapper.sourceToTarget( source ); diff --git a/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java b/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java index ddc834cc8e..c3f84b6ab6 100644 --- a/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java +++ b/integrationtest/src/test/resources/defaultPackage/test/java/DefaultPackageTest.java @@ -4,7 +4,7 @@ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 */ -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -12,10 +12,10 @@ * @author Filip Hrisafov */ -public class DefaultPackageTest { +class DefaultPackageTest { @Test - public void shouldWorkCorrectlyInDefaultPackage() { + void shouldWorkCorrectlyInDefaultPackage() { DefaultPackageObject.CarDto carDto = DefaultPackageObject.CarMapper.INSTANCE.carToCarDto( new DefaultPackageObject.Car( "Morris", diff --git a/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java b/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java index 42ed70b18e..504a961437 100644 --- a/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java +++ b/integrationtest/src/test/resources/expressionTextBlocksTest/src/test/java/org/mapstruct/itest/textBlocks/TextBlocksTest.java @@ -9,12 +9,12 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; -public class TextBlocksTest { +class TextBlocksTest { @Test - public void textBlockExpressionShouldWork() { + void textBlockExpressionShouldWork() { Car car = new Car(); car.setWheelPosition( new WheelPosition( "left" ) ); diff --git a/integrationtest/src/test/resources/externalbeanjar/mapper/src/test/java/org/mapstruct/itest/externalbeanjar/ConversionTest.java b/integrationtest/src/test/resources/externalbeanjar/mapper/src/test/java/org/mapstruct/itest/externalbeanjar/ConversionTest.java index 8a18e9e4fc..af10a63428 100644 --- a/integrationtest/src/test/resources/externalbeanjar/mapper/src/test/java/org/mapstruct/itest/externalbeanjar/ConversionTest.java +++ b/integrationtest/src/test/resources/externalbeanjar/mapper/src/test/java/org/mapstruct/itest/externalbeanjar/ConversionTest.java @@ -8,15 +8,15 @@ import java.math.BigDecimal; import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.externalbeanjar.Source; import org.mapstruct.itest.externalbeanjar.Issue1121Mapper; import org.mapstruct.itest.externalbeanjar.Target; -public class ConversionTest { +class ConversionTest { @Test - public void shouldApplyConversions() { + void shouldApplyConversions() { Source source = new Source(); source.setBigDecimal( new BigDecimal( "42" ) ); diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml index 08390085dd..60b73b856c 100644 --- a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/generator/pom.xml @@ -27,8 +27,8 @@ provided - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml index 90c95aa83b..976664f88d 100644 --- a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/pom.xml @@ -21,8 +21,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java index 06bfa3c533..ed6a5c0d89 100644 --- a/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java +++ b/integrationtest/src/test/resources/faultyAstModifyingAnnotationProcessorTest/usage/src/test/java/org/mapstruct/itest/faultyAstModifyingProcessor/usage/FaultyAstModifyingTestTest.java @@ -5,7 +5,7 @@ */ package org.mapstruct.itest.faultyAstModifyingProcessor.usage; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,10 +14,10 @@ * * @author Filip Hrisafov */ -public class FaultyAstModifyingTestTest { +class FaultyAstModifyingTestTest { @Test - public void testMapping() { + void testMapping() { Order order = new Order(); order.setItem( "my item" ); diff --git a/integrationtest/src/test/resources/freeBuilderBuilderTest/src/test/java/org/mapstruct/itest/freebuilder/FreeBuilderMapperTest.java b/integrationtest/src/test/resources/freeBuilderBuilderTest/src/test/java/org/mapstruct/itest/freebuilder/FreeBuilderMapperTest.java index 5047451798..3b1caebf9c 100644 --- a/integrationtest/src/test/resources/freeBuilderBuilderTest/src/test/java/org/mapstruct/itest/freebuilder/FreeBuilderMapperTest.java +++ b/integrationtest/src/test/resources/freeBuilderBuilderTest/src/test/java/org/mapstruct/itest/freebuilder/FreeBuilderMapperTest.java @@ -5,7 +5,7 @@ */ package org.mapstruct.itest.freebuilder; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,10 +14,10 @@ * * @author Eric Martineau */ -public class FreeBuilderMapperTest { +class FreeBuilderMapperTest { @Test - public void testSimpleImmutableBuilderHappyPath() { + void testSimpleImmutableBuilderHappyPath() { PersonDto personDto = PersonMapper.INSTANCE.toDto( Person.builder() .setAge( 33 ) .setName( "Bob" ) @@ -32,7 +32,7 @@ public void testSimpleImmutableBuilderHappyPath() { } @Test - public void testLombokToImmutable() { + void testLombokToImmutable() { Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) ); assertThat( person.getAge() ).isEqualTo( 33 ); assertThat( person.getName() ).isEqualTo( "Bob" ); diff --git a/integrationtest/src/test/resources/fullFeatureTest/src/test/java/org/mapstruct/itest/simple/AnimalTest.java b/integrationtest/src/test/resources/fullFeatureTest/src/test/java/org/mapstruct/itest/simple/AnimalTest.java index a180b4df52..14dd576fe0 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/src/test/java/org/mapstruct/itest/simple/AnimalTest.java +++ b/integrationtest/src/test/resources/fullFeatureTest/src/test/java/org/mapstruct/itest/simple/AnimalTest.java @@ -7,7 +7,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.ap.test.ignore.AnimalMapper; import org.mapstruct.ap.test.ignore.Animal; @@ -18,10 +18,10 @@ * * @author Gunnar Morling */ -public class AnimalTest { +class AnimalTest { @Test - public void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() { + void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() { Animal animal = new Animal( "Bruno", 100, 23, "black" ); AnimalDto animalDto = AnimalMapper.INSTANCE.animalToDto( animal ); @@ -34,7 +34,7 @@ public void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() { } @Test - public void shouldNotPropagateIgnoredPropertyInReverseMappingWhenSourceAndTargetAreSpecified() { + void shouldNotPropagateIgnoredPropertyInReverseMappingWhenSourceAndTargetAreSpecified() { AnimalDto animalDto = new AnimalDto( "Bruno", 100, 23, "black" ); Animal animal = AnimalMapper.INSTANCE.animalDtoToAnimal( animalDto ); diff --git a/integrationtest/src/test/resources/immutablesBuilderTest/mapper/src/test/java/org/mapstruct/itest/immutables/ImmutablesMapperTest.java b/integrationtest/src/test/resources/immutablesBuilderTest/mapper/src/test/java/org/mapstruct/itest/immutables/ImmutablesMapperTest.java index 421954fc90..9990f4278c 100644 --- a/integrationtest/src/test/resources/immutablesBuilderTest/mapper/src/test/java/org/mapstruct/itest/immutables/ImmutablesMapperTest.java +++ b/integrationtest/src/test/resources/immutablesBuilderTest/mapper/src/test/java/org/mapstruct/itest/immutables/ImmutablesMapperTest.java @@ -5,7 +5,7 @@ */ package org.mapstruct.itest.immutables; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,10 +14,10 @@ * * @author Eric Martineau */ -public class ImmutablesMapperTest { +class ImmutablesMapperTest { @Test - public void testSimpleImmutableBuilderHappyPath() { + void testSimpleImmutableBuilderHappyPath() { PersonDto personDto = PersonMapper.INSTANCE.toDto( ImmutablePerson.builder() .age( 33 ) .name( "Bob" ) @@ -32,7 +32,7 @@ public void testSimpleImmutableBuilderHappyPath() { } @Test - public void testLombokToImmutable() { + void testLombokToImmutable() { Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) ); assertThat( person.getAge() ).isEqualTo( 33 ); assertThat( person.getName() ).isEqualTo( "Bob" ); diff --git a/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java b/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java index b81c946d9c..afd5ba0289 100644 --- a/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java +++ b/integrationtest/src/test/resources/jakartaJaxbTest/src/test/java/org/mapstruct/itest/jakarta/jaxb/JakartaJaxbBasedMapperTest.java @@ -18,7 +18,7 @@ import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.Marshaller; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.jakarta.jaxb.xsd.test1.ObjectFactory; import org.mapstruct.itest.jakarta.jaxb.xsd.test1.OrderType; import org.mapstruct.itest.jakarta.jaxb.xsd.underscores.SubType; @@ -28,10 +28,10 @@ * * @author Iaroslav Bogdanchikov */ -public class JakartaJaxbBasedMapperTest { +class JakartaJaxbBasedMapperTest { @Test - public void shouldMapJakartaJaxb() throws ParseException, JAXBException { + void shouldMapJakartaJaxb() throws ParseException, JAXBException { SourceTargetMapper mapper = SourceTargetMapper.INSTANCE; @@ -81,7 +81,7 @@ public void shouldMapJakartaJaxb() throws ParseException, JAXBException { } @Test - public void underscores() throws ParseException, JAXBException { + void underscores() throws ParseException, JAXBException { SourceTargetMapper mapper = SourceTargetMapper.INSTANCE; diff --git a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_603/Issue603Test.java b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_603/Issue603Test.java index c732b96f22..efe68a48ee 100644 --- a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_603/Issue603Test.java +++ b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_603/Issue603Test.java @@ -5,15 +5,14 @@ */ package org.mapstruct.ap.test.bugs._603; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -public class Issue603Test { +class Issue603Test { @Test - public void shouldMapDataFromJava8Interface() { + void shouldMapDataFromJava8Interface() { final Source source = new Source(); diff --git a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_636/Issue636Test.java b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_636/Issue636Test.java index 99b7a515eb..837eb0b53a 100644 --- a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_636/Issue636Test.java +++ b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/ap/test/bugs/_636/Issue636Test.java @@ -5,14 +5,14 @@ */ package org.mapstruct.ap.test.bugs._636; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -public class Issue636Test { +class Issue636Test { @Test - public void shouldMapDataFromJava8Interface() { + void shouldMapDataFromJava8Interface() { final long idFoo = 123; final String idBar = "Bar456"; diff --git a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/itest/java8/Java8MapperTest.java b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/itest/java8/Java8MapperTest.java index 556dd2f1e4..3f88889380 100644 --- a/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/itest/java8/Java8MapperTest.java +++ b/integrationtest/src/test/resources/java8Test/src/test/java/org/mapstruct/itest/java8/Java8MapperTest.java @@ -7,17 +7,17 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** * Test for generation of Java8 based mapper implementations. * * @author Christian Schuster */ -public class Java8MapperTest { +class Java8MapperTest { @Test - public void shouldMapWithRepeatedMappingAnnotation() { + void shouldMapWithRepeatedMappingAnnotation() { Java8Mapper mapper = Java8Mapper.INSTANCE; Source source = new Source(); diff --git a/integrationtest/src/test/resources/jaxbTest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java b/integrationtest/src/test/resources/jaxbTest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java index f54658ba06..4c81dc91d2 100644 --- a/integrationtest/src/test/resources/jaxbTest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java +++ b/integrationtest/src/test/resources/jaxbTest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java @@ -18,7 +18,7 @@ import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.jaxb.xsd.test1.ObjectFactory; import org.mapstruct.itest.jaxb.xsd.test1.OrderType; import org.mapstruct.itest.jaxb.xsd.underscores.SubType; @@ -28,9 +28,9 @@ * * @author Sjaak Derksen */ -public class JaxbBasedMapperTest { +class JaxbBasedMapperTest { @Test - public void shouldMapJaxb() throws ParseException, JAXBException { + void shouldMapJaxb() throws ParseException, JAXBException { SourceTargetMapper mapper = SourceTargetMapper.INSTANCE; @@ -80,7 +80,7 @@ public void shouldMapJaxb() throws ParseException, JAXBException { } @Test - public void underscores() throws ParseException, JAXBException { + void underscores() throws ParseException, JAXBException { SourceTargetMapper mapper = SourceTargetMapper.INSTANCE; diff --git a/integrationtest/src/test/resources/jsr330Test/pom.xml b/integrationtest/src/test/resources/jsr330Test/pom.xml index 9a7dbcbc8a..e1cf959a90 100644 --- a/integrationtest/src/test/resources/jsr330Test/pom.xml +++ b/integrationtest/src/test/resources/jsr330Test/pom.xml @@ -18,6 +18,10 @@ jsr330Test jar + + + ${org.junit6.jupiter.version} + diff --git a/integrationtest/src/test/resources/jsr330Test/src/test/java/org/mapstruct/itest/jsr330/Jsr330BasedMapperTest.java b/integrationtest/src/test/resources/jsr330Test/src/test/java/org/mapstruct/itest/jsr330/Jsr330BasedMapperTest.java index 525930f0ec..6cf9043472 100644 --- a/integrationtest/src/test/resources/jsr330Test/src/test/java/org/mapstruct/itest/jsr330/Jsr330BasedMapperTest.java +++ b/integrationtest/src/test/resources/jsr330Test/src/test/java/org/mapstruct/itest/jsr330/Jsr330BasedMapperTest.java @@ -8,13 +8,13 @@ import jakarta.inject.Inject; import jakarta.inject.Named; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mapstruct.itest.jsr330.Jsr330BasedMapperTest.SpringTestConfig; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; @@ -24,8 +24,8 @@ * @author Andreas Gudian */ @ContextConfiguration(classes = SpringTestConfig.class) -@RunWith(SpringJUnit4ClassRunner.class) -public class Jsr330BasedMapperTest { +@ExtendWith(SpringExtension.class) +class Jsr330BasedMapperTest { @Configuration @ComponentScan(basePackageClasses = Jsr330BasedMapperTest.class) public static class SpringTestConfig { @@ -43,7 +43,7 @@ public static class SpringTestConfig { private SecondDecoratedSourceTargetMapper secondDecoratedMapper; @Test - public void shouldInjectJsr330BasedMapper() { + void shouldInjectJsr330BasedMapper() { Source source = new Source(); Target target = mapper.sourceToTarget( source ); @@ -54,7 +54,7 @@ public void shouldInjectJsr330BasedMapper() { } @Test - public void shouldInjectDecorator() { + void shouldInjectDecorator() { Source source = new Source(); Target target = decoratedMapper.sourceToTarget( source ); @@ -71,7 +71,7 @@ public void shouldInjectDecorator() { } @Test - public void shouldInjectSecondDecorator() { + void shouldInjectSecondDecorator() { Source source = new Source(); Target target = secondDecoratedMapper.sourceToTarget( source ); diff --git a/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java b/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java index 513f080a8b..e46b76f5f0 100644 --- a/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java +++ b/integrationtest/src/test/resources/kotlinDataTest/src/test/java/org/mapstruct/itest/kotlin/data/KotlinDataTest.java @@ -7,15 +7,15 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.kotlin.data.CustomerDto; import org.mapstruct.itest.kotlin.data.CustomerEntity; import org.mapstruct.itest.kotlin.data.CustomerMapper; -public class KotlinDataTest { +class KotlinDataTest { @Test - public void shouldMapData() { + void shouldMapData() { CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) ); assertThat( customer ).isNotNull(); @@ -24,7 +24,7 @@ public void shouldMapData() { } @Test - public void shouldMapIntoData() { + void shouldMapIntoData() { CustomerEntity entity = new CustomerEntity(); entity.setName( "Kermit" ); entity.setMail( "kermit@test.com" ); diff --git a/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java b/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java index 6053f294c3..060fb5a990 100644 --- a/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java +++ b/integrationtest/src/test/resources/lombokBuilderTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java @@ -5,7 +5,7 @@ */ package org.mapstruct.itest.lombok; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.factory.Mappers; import static org.assertj.core.api.Assertions.assertThat; @@ -15,10 +15,10 @@ * * @author Eric Martineau */ -public class LombokMapperTest { +class LombokMapperTest { @Test - public void testSimpleImmutableBuilderHappyPath() { + void testSimpleImmutableBuilderHappyPath() { PersonDto personDto = PersonMapper.INSTANCE.toDto( Person.foo() .age( 33 ) .name( "Bob" ) @@ -33,7 +33,7 @@ public void testSimpleImmutableBuilderHappyPath() { } @Test - public void testLombokToImmutable() { + void testLombokToImmutable() { Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) ); assertThat( person.getAge() ).isEqualTo( 33 ); assertThat( person.getName() ).isEqualTo( "Bob" ); diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java b/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java index f8b702896d..bdf8f46a57 100644 --- a/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java +++ b/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java @@ -5,7 +5,7 @@ */ package org.mapstruct.itest.lombok; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.factory.Mappers; import static org.assertj.core.api.Assertions.assertThat; @@ -15,10 +15,10 @@ * * @author Eric Martineau */ -public class LombokMapperTest { +class LombokMapperTest { @Test - public void testSimpleImmutableBuilderHappyPath() { + void testSimpleImmutableBuilderHappyPath() { PersonDto personDto = PersonMapper.INSTANCE.toDto( new Person( "Bob", 33, new Address( "Wild Drive" ) ) ); assertThat( personDto.getAge() ).isEqualTo( 33 ); assertThat( personDto.getName() ).isEqualTo( "Bob" ); @@ -27,7 +27,7 @@ public void testSimpleImmutableBuilderHappyPath() { } @Test - public void testLombokToImmutable() { + void testLombokToImmutable() { Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) ); assertThat( person.getAge() ).isEqualTo( 33 ); assertThat( person.getName() ).isEqualTo( "Bob" ); diff --git a/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java b/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java index 805662c806..aae996c842 100644 --- a/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java +++ b/integrationtest/src/test/resources/moduleInfoTest/src/test/java/org/mapstruct/itest/modules/ModulesTest.java @@ -7,15 +7,15 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.modules.CustomerDto; import org.mapstruct.itest.modules.CustomerEntity; import org.mapstruct.itest.modules.CustomerMapper; -public class ModulesTest { +class ModulesTest { @Test - public void shouldMapRecord() { + void shouldMapRecord() { CustomerDto dto = new CustomerDto(); dto.setName( "Kermit" ); dto.setEmail( "kermit@test.com" ); diff --git a/integrationtest/src/test/resources/namingStrategyTest/usage/src/test/java/org/mapstruct/itest/naming/NamingTest.java b/integrationtest/src/test/resources/namingStrategyTest/usage/src/test/java/org/mapstruct/itest/naming/NamingTest.java index d3a45db26b..adc7aac7cb 100644 --- a/integrationtest/src/test/resources/namingStrategyTest/usage/src/test/java/org/mapstruct/itest/naming/NamingTest.java +++ b/integrationtest/src/test/resources/namingStrategyTest/usage/src/test/java/org/mapstruct/itest/naming/NamingTest.java @@ -7,7 +7,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.naming.GolfPlayer; import org.mapstruct.itest.naming.GolfPlayerDto; import org.mapstruct.itest.naming.GolfPlayerMapper; @@ -17,10 +17,10 @@ * * @author Gunnar Morling */ -public class NamingTest { +class NamingTest { @Test - public void shouldApplyCustomNamingStrategy() { + void shouldApplyCustomNamingStrategy() { GolfPlayer player = new GolfPlayer() .withName( "Jared" ) .withHandicap( 9.2D ); diff --git a/integrationtest/src/test/resources/pom.xml b/integrationtest/src/test/resources/pom.xml index 3053b211e1..f4cd0b51de 100644 --- a/integrationtest/src/test/resources/pom.xml +++ b/integrationtest/src/test/resources/pom.xml @@ -26,6 +26,7 @@ ${mapstruct.version} + 6.0.3 @@ -135,8 +136,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/integrationtest/src/test/resources/protobufBuilderTest/src/test/java/org/mapstruct/itest/protobuf/ProtobufMapperTest.java b/integrationtest/src/test/resources/protobufBuilderTest/src/test/java/org/mapstruct/itest/protobuf/ProtobufMapperTest.java index ae3740848f..ac97eba8b4 100644 --- a/integrationtest/src/test/resources/protobufBuilderTest/src/test/java/org/mapstruct/itest/protobuf/ProtobufMapperTest.java +++ b/integrationtest/src/test/resources/protobufBuilderTest/src/test/java/org/mapstruct/itest/protobuf/ProtobufMapperTest.java @@ -5,7 +5,7 @@ */ package org.mapstruct.itest.protobuf; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,10 +14,10 @@ * * @author Christian Bandowski */ -public class ProtobufMapperTest { +class ProtobufMapperTest { @Test - public void testSimpleImmutableBuilderHappyPath() { + void testSimpleImmutableBuilderHappyPath() { PersonDto personDto = PersonMapper.INSTANCE.toDto( PersonProtos.Person.newBuilder() .setAge( 33 ) .setName( "Bob" ) @@ -33,7 +33,7 @@ public void testSimpleImmutableBuilderHappyPath() { } @Test - public void testLombokToImmutable() { + void testLombokToImmutable() { PersonProtos.Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) ); assertThat( person.getAge() ).isEqualTo( 33 ); diff --git a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java index 5f7a99273a..4064e0fe3f 100644 --- a/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java +++ b/integrationtest/src/test/resources/recordsCrossModuleInterfaceTest/module-2/src/test/java/org/mapstruct/itest/records/module2/RecordsTest.java @@ -7,14 +7,14 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.records.module1.SourceRootRecord; import org.mapstruct.itest.records.module1.SourceNestedRecord; -public class RecordsTest { +class RecordsTest { @Test - public void shouldMap() { + void shouldMap() { SourceRootRecord source = new SourceRootRecord( new SourceNestedRecord( "test" ) ); TargetRootRecord target = RecordInterfaceIssueMapper.INSTANCE.map( source ); diff --git a/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java index 2f274792b8..218fd82c6a 100644 --- a/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java +++ b/integrationtest/src/test/resources/recordsCrossModuleTest/mapper/src/test/java/org/mapstruct/itest/records/mapper/RecordsTest.java @@ -7,15 +7,15 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.records.api.CustomerDto; import org.mapstruct.itest.records.mapper.CustomerEntity; import org.mapstruct.itest.records.mapper.CustomerMapper; -public class RecordsTest { +class RecordsTest { @Test - public void shouldMapRecord() { + void shouldMapRecord() { CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) ); assertThat( customer ).isNotNull(); @@ -24,7 +24,7 @@ public void shouldMapRecord() { } @Test - public void shouldMapIntoRecord() { + void shouldMapIntoRecord() { CustomerEntity entity = new CustomerEntity(); entity.setName( "Kermit" ); entity.setMail( "kermit@test.com" ); diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java index 2f77e8d49f..2a510edafd 100644 --- a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java @@ -9,15 +9,15 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.records.CustomerDto; import org.mapstruct.itest.records.CustomerEntity; import org.mapstruct.itest.records.CustomerMapper; -public class RecordsTest { +class RecordsTest { @Test - public void shouldMapRecord() { + void shouldMapRecord() { CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) ); assertThat( customer ).isNotNull(); @@ -26,7 +26,7 @@ public void shouldMapRecord() { } @Test - public void shouldMapIntoRecord() { + void shouldMapIntoRecord() { CustomerEntity entity = new CustomerEntity(); entity.setName( "Kermit" ); entity.setMail( "kermit@test.com" ); @@ -39,7 +39,7 @@ public void shouldMapIntoRecord() { } @Test - public void shouldMapIntoGenericRecord() { + void shouldMapIntoGenericRecord() { CustomerEntity entity = new CustomerEntity(); entity.setName( "Kermit" ); entity.setMail( "kermit@test.com" ); @@ -51,7 +51,7 @@ public void shouldMapIntoGenericRecord() { } @Test - public void shouldMapIntoRecordWithList() { + void shouldMapIntoRecordWithList() { Car car = new Car(); car.setWheelPositions( Arrays.asList( new WheelPosition( "left" ) ) ); @@ -63,7 +63,7 @@ public void shouldMapIntoRecordWithList() { } @Test - public void shouldMapMemberRecord() { + void shouldMapMemberRecord() { MemberEntity member = MemberMapper.INSTANCE.fromRecord( new MemberDto( true, false ) ); assertThat( member ).isNotNull(); @@ -72,7 +72,7 @@ public void shouldMapMemberRecord() { } @Test - public void shouldMapIntoMemberRecord() { + void shouldMapIntoMemberRecord() { MemberEntity entity = new MemberEntity(); entity.setIsActive( false ); entity.setPremium( true ); @@ -80,12 +80,12 @@ public void shouldMapIntoMemberRecord() { MemberDto value = MemberMapper.INSTANCE.toRecord( entity ); assertThat( value ).isNotNull(); - assertThat( value.isActive() ).isEqualTo( false ); - assertThat( value.premium() ).isEqualTo( true ); + assertThat( value.isActive() ).isFalse(); + assertThat( value.premium() ).isTrue(); } @Test - public void shouldUseDefaultConstructor() { + void shouldUseDefaultConstructor() { Task entity = new Task( "some-id", 1000L ); TaskDto value = TaskMapper.INSTANCE.toRecord( entity ); diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java index c8ccaf1a65..c70937fa84 100644 --- a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/nested/NestedRecordsTest.java @@ -9,12 +9,12 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; -public class NestedRecordsTest { +class NestedRecordsTest { @Test - public void shouldMapRecord() { + void shouldMapRecord() { CareProvider source = new CareProvider( "kermit", new Address( "Sesame Street", "New York" ) ); CareProviderDto target = CareProviderMapper.INSTANCE.map( source ); diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java b/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java index 379341ff66..171b6519c6 100644 --- a/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java +++ b/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java @@ -7,12 +7,12 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; -public class SealedSubclassTest { +class SealedSubclassTest { @Test - public void mappingIsDoneUsingSubclassMapping() { + void mappingIsDoneUsingSubclassMapping() { VehicleCollection vehicles = new VehicleCollection(); vehicles.getVehicles().add( new Car() ); vehicles.getVehicles().add( new Bike() ); @@ -28,7 +28,7 @@ public void mappingIsDoneUsingSubclassMapping() { } @Test - public void inverseMappingIsDoneUsingSubclassMapping() { + void inverseMappingIsDoneUsingSubclassMapping() { VehicleCollectionDto vehicles = new VehicleCollectionDto(); vehicles.getVehicles().add( new CarDto() ); vehicles.getVehicles().add( new BikeDto() ); @@ -44,7 +44,7 @@ public void inverseMappingIsDoneUsingSubclassMapping() { } @Test - public void subclassMappingInheritsInverseMapping() { + void subclassMappingInheritsInverseMapping() { VehicleCollectionDto vehiclesDto = new VehicleCollectionDto(); CarDto carDto = new CarDto(); carDto.setMaker( "BenZ" ); diff --git a/integrationtest/src/test/resources/simpleTest/src/test/java/org/mapstruct/itest/simple/ConversionTest.java b/integrationtest/src/test/resources/simpleTest/src/test/java/org/mapstruct/itest/simple/ConversionTest.java index 2b6a0bc93b..69c94322bd 100644 --- a/integrationtest/src/test/resources/simpleTest/src/test/java/org/mapstruct/itest/simple/ConversionTest.java +++ b/integrationtest/src/test/resources/simpleTest/src/test/java/org/mapstruct/itest/simple/ConversionTest.java @@ -7,15 +7,15 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.simple.Source; import org.mapstruct.itest.simple.SourceTargetMapper; import org.mapstruct.itest.simple.Target; -public class ConversionTest { +class ConversionTest { @Test - public void shouldApplyConversions() { + void shouldApplyConversions() { Source source = new Source(); source.setFoo( 42 ); source.setBar( 23L ); @@ -30,18 +30,18 @@ public void shouldApplyConversions() { } @Test - public void shouldHandleNulls() { + void shouldHandleNulls() { Source source = new Source(); Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); assertThat( target ).isNotNull(); assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 1 ) ); - assertThat( target.getBar() ).isEqualTo( 0 ); + assertThat( target.getBar() ).isZero(); assertThat( target.getZip() ).isEqualTo( "0" ); } @Test - public void shouldApplyConversionsToMappedProperties() { + void shouldApplyConversionsToMappedProperties() { Source source = new Source(); source.setQax( 42 ); source.setBaz( 23L ); @@ -54,7 +54,7 @@ public void shouldApplyConversionsToMappedProperties() { } @Test - public void shouldApplyConversionsForReverseMapping() { + void shouldApplyConversionsForReverseMapping() { Target target = new Target(); target.setFoo( 42L ); target.setBar( 23 ); @@ -69,7 +69,7 @@ public void shouldApplyConversionsForReverseMapping() { } @Test - public void shouldApplyConversionsToMappedPropertiesForReverseMapping() { + void shouldApplyConversionsToMappedPropertiesForReverseMapping() { Target target = new Target(); target.setQax( 42 ); target.setBaz( 23L ); @@ -82,7 +82,7 @@ public void shouldApplyConversionsToMappedPropertiesForReverseMapping() { } @Test - public void shouldWorkWithAbstractClass() { + void shouldWorkWithAbstractClass() { Source source = new Source(); source.setFoo( 42 ); source.setBar( 23L ); diff --git a/integrationtest/src/test/resources/springTest/pom.xml b/integrationtest/src/test/resources/springTest/pom.xml index 3c1fb54552..e4210c0cba 100644 --- a/integrationtest/src/test/resources/springTest/pom.xml +++ b/integrationtest/src/test/resources/springTest/pom.xml @@ -18,6 +18,10 @@ springTest jar + + + ${org.junit6.jupiter.version} + diff --git a/integrationtest/src/test/resources/springTest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java b/integrationtest/src/test/resources/springTest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java index a5b3bb0e65..c0f3de4ce2 100644 --- a/integrationtest/src/test/resources/springTest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java +++ b/integrationtest/src/test/resources/springTest/src/test/java/org/mapstruct/itest/spring/SpringBasedMapperTest.java @@ -5,14 +5,14 @@ */ package org.mapstruct.itest.spring; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mapstruct.itest.spring.SpringBasedMapperTest.SpringTestConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; @@ -22,8 +22,8 @@ * @author Andreas Gudian */ @ContextConfiguration(classes = SpringTestConfig.class) -@RunWith(SpringJUnit4ClassRunner.class) -public class SpringBasedMapperTest { +@ExtendWith(SpringExtension.class) +class SpringBasedMapperTest { @Configuration @ComponentScan(basePackageClasses = SpringBasedMapperTest.class) @@ -40,7 +40,7 @@ public static class SpringTestConfig { private SecondDecoratedSourceTargetMapper secondDecoratedMapper; @Test - public void shouldInjectSpringBasedMapper() { + void shouldInjectSpringBasedMapper() { Source source = new Source(); Target target = mapper.sourceToTarget( source ); @@ -51,7 +51,7 @@ public void shouldInjectSpringBasedMapper() { } @Test - public void shouldInjectDecorator() { + void shouldInjectDecorator() { Source source = new Source(); Target target = decoratedMapper.sourceToTarget( source ); @@ -68,7 +68,7 @@ public void shouldInjectDecorator() { } @Test - public void shouldInjectSecondDecorator() { + void shouldInjectSecondDecorator() { Source source = new Source(); Target target = secondDecoratedMapper.sourceToTarget( source ); diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml index 5ab2d0d18f..f122047093 100644 --- a/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml +++ b/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml @@ -21,8 +21,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml index d1e1dd7dff..4c5e4611f0 100644 --- a/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml @@ -21,8 +21,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java index 4a012283d3..f34bebe36d 100644 --- a/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java @@ -5,9 +5,9 @@ */ package org.mapstruct.itest.supertypegeneration.usage; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mapstruct.itest.supertypegeneration.usage.Order; import org.mapstruct.itest.supertypegeneration.usage.OrderDto; import org.mapstruct.itest.supertypegeneration.usage.OrderMapper; @@ -18,10 +18,10 @@ * * @author Gunnar Morling */ -public class GeneratedBasesTest { +class GeneratedBasesTest { @Test - public void considersPropertiesOnGeneratedSourceAndTargetTypes() { + void considersPropertiesOnGeneratedSourceAndTargetTypes() { Order order = new Order(); order.setItem( "my item" ); order.setBaseName2( "my base name 2" ); diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml index 67df383a18..89bd9c2d2f 100644 --- a/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml +++ b/integrationtest/src/test/resources/targetTypeGenerationTest/generator/pom.xml @@ -21,8 +21,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml index bd06b79a49..0726580592 100644 --- a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml +++ b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/pom.xml @@ -21,8 +21,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/targettypegeneration/usage/GeneratedTargetTypeTest.java b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/targettypegeneration/usage/GeneratedTargetTypeTest.java index d9e3f8d789..d7fb6b00fa 100644 --- a/integrationtest/src/test/resources/targetTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/targettypegeneration/usage/GeneratedTargetTypeTest.java +++ b/integrationtest/src/test/resources/targetTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/targettypegeneration/usage/GeneratedTargetTypeTest.java @@ -5,9 +5,9 @@ */ package org.mapstruct.itest.targettypegeneration.usage; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** * Integration test for using MapStruct with another annotation processor that generates the target type of a mapping @@ -15,10 +15,10 @@ * * @author Gunnar Morling */ -public class GeneratedTargetTypeTest { +class GeneratedTargetTypeTest { @Test - public void considersPropertiesOnGeneratedSourceAndTargetTypes() { + void considersPropertiesOnGeneratedSourceAndTargetTypes() { Order order = new Order(); order.setItem( "my item" ); diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml index 6ac3a01297..24d3436533 100644 --- a/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/generator/pom.xml @@ -21,8 +21,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml index 79696df47d..d7154c6246 100644 --- a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/pom.xml @@ -21,8 +21,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter test diff --git a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java index e715e66620..55ea60a0ad 100644 --- a/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java +++ b/integrationtest/src/test/resources/usesTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/usestypegeneration/usage/GeneratedUsesTypeTest.java @@ -5,7 +5,7 @@ */ package org.mapstruct.itest.usestypegeneration.usage; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,10 +14,10 @@ * * @author Filip Hrisafov */ -public class GeneratedUsesTypeTest { +class GeneratedUsesTypeTest { @Test - public void considersPropertiesOnGeneratedSourceAndTargetTypes() { + void considersPropertiesOnGeneratedSourceAndTargetTypes() { Order order = new Order(); order.setItem( "my item" ); diff --git a/parent/pom.xml b/parent/pom.xml index c4ba63948d..f3b0fe25f9 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -141,11 +141,6 @@ gem-processor ${org.mapstruct.gem.version} - - junit - junit - 4.13.1 - com.puppycrawl.tools checkstyle @@ -201,7 +196,7 @@ org.jboss.arquillian arquillian-bom - 1.6.0.Final + 1.7.2.Final import pom From f76f11c08defcc8bb507c9247e7c217087397634 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sun, 15 Mar 2026 15:20:33 +0100 Subject: [PATCH 558/597] Enforce spaces inside parentheses for control flow statements via checkstyle --- .../src/main/resources/build-config/checkstyle.xml | 2 +- core/src/main/java/org/mapstruct/factory/Mappers.java | 4 ++-- .../main/java/org/mapstruct/ap/MappingProcessor.java | 2 +- .../mapstruct/ap/internal/conversion/Conversions.java | 2 +- .../ap/internal/model/AbstractBaseBuilder.java | 4 ++-- .../mapstruct/ap/internal/model/BeanMappingMethod.java | 2 +- .../ap/internal/model/DefaultMapperReference.java | 2 +- .../java/org/mapstruct/ap/internal/model/Mapper.java | 6 +++--- .../ap/internal/model/MappingBuilderContext.java | 2 +- .../ap/internal/model/NestedPropertyMappingMethod.java | 2 +- .../ap/internal/model/ObjectFactoryMethodResolver.java | 2 +- .../mapstruct/ap/internal/model/PropertyMapping.java | 10 +++++----- ...etterWrapperForCollectionsAndMapsWithNullCheck.java | 2 +- .../internal/model/beanmapping/MappingReference.java | 8 ++++---- .../internal/model/beanmapping/MappingReferences.java | 2 +- .../ap/internal/model/common/BuilderType.java | 2 +- .../org/mapstruct/ap/internal/model/common/Type.java | 8 ++++---- .../ap/internal/model/common/TypeFactory.java | 6 +++--- .../ap/internal/model/source/MappingMethodOptions.java | 4 ++-- .../ap/internal/model/source/MethodMatcher.java | 2 +- .../source/selector/JakartaXmlElementDeclSelector.java | 4 ++-- .../source/selector/JavaxXmlElementDeclSelector.java | 4 ++-- .../selector/MostSpecificResultTypeSelector.java | 2 +- .../internal/model/source/selector/TypeSelector.java | 2 +- .../processor/creation/MappingResolverImpl.java | 6 +++--- .../java/org/mapstruct/ap/internal/util/Strings.java | 4 ++-- .../ap/test/bugs/_1482/TargetSourceMapper.java | 2 +- .../ap/test/bugs/_1801/Issue1801BuilderProvider.java | 2 +- .../org/mapstruct/ap/test/bugs/_2663/JsonNullable.java | 2 +- .../org/mapstruct/ap/test/bugs/_2663/Nullable.java | 2 +- .../ap/test/bugs/_3089/Issue3089BuilderProvider.java | 2 +- ...nalMethodWithSourcePropertyNameInContextMapper.java | 2 +- ...nalMethodWithTargetPropertyNameInContextMapper.java | 2 +- .../mapstruct/ap/test/nestedbeans/RecursionTest.java | 6 +++--- .../selection/ExternalHandWrittenMapper.java | 2 +- 35 files changed, 59 insertions(+), 59 deletions(-) diff --git a/build-config/src/main/resources/build-config/checkstyle.xml b/build-config/src/main/resources/build-config/checkstyle.xml index 71a7d35899..14567bf067 100644 --- a/build-config/src/main/resources/build-config/checkstyle.xml +++ b/build-config/src/main/resources/build-config/checkstyle.xml @@ -141,7 +141,7 @@ - + diff --git a/core/src/main/java/org/mapstruct/factory/Mappers.java b/core/src/main/java/org/mapstruct/factory/Mappers.java index 05b616fe9a..d20bd59fa3 100644 --- a/core/src/main/java/org/mapstruct/factory/Mappers.java +++ b/core/src/main/java/org/mapstruct/factory/Mappers.java @@ -84,10 +84,10 @@ private static T doGetMapper(Class clazz, ClassLoader classLoader) throws return constructor.newInstance(); } - catch (ClassNotFoundException e) { + catch ( ClassNotFoundException e ) { return getMapperFromServiceLoader( clazz, classLoader ); } - catch ( InstantiationException | InvocationTargetException | IllegalAccessException e) { + catch ( InstantiationException | InvocationTargetException | IllegalAccessException e ) { throw new RuntimeException( e ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 32e65c7047..01a2a35955 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -264,7 +264,7 @@ private Set getMappers(final Set annotations try { Set annotatedMappers = roundEnvironment.getElementsAnnotatedWith( annotation ); - for (Element mapperElement : annotatedMappers) { + for ( Element mapperElement : annotatedMappers ) { TypeElement mapperTypeElement = asTypeElement( mapperElement ); // on some JDKs, RoundEnvironment.getElementsAnnotatedWith( ... ) returns types with diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 7b2f8702b1..c29ad5141e 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -355,7 +355,7 @@ public ConversionProvider getConversion(Type sourceType, Type targetType) { else if ( targetType.isOptionalType() ) { // Type -> Optional Type targetBaseType = targetType.getOptionalBaseType(); - if ( targetBaseType.equals( sourceType )) { + if ( targetBaseType.equals( sourceType ) ) { return TypeToOptionalConversion.TYPE_TO_OPTIONAL_CONVERSION; } ConversionProvider conversionProvider = getInternalConversion( sourceType, targetBaseType ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java index 584e0260bc..8859e542b8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java @@ -115,8 +115,8 @@ Assignment createForgedAssignment(SourceRHS source, ForgedMethod methodRef, Mapp if ( mappingMethod == null ) { return null; } - if (methodRef.getMappingReferences().isRestrictToDefinedMappings() || - !ctx.getMappingsToGenerate().contains( mappingMethod )) { + if ( methodRef.getMappingReferences().isRestrictToDefinedMappings() || + !ctx.getMappingsToGenerate().contains( mappingMethod ) ) { // If the mapping options are restricted only to the defined mappings, then use the mapping method. // See https://github.com/mapstruct/mapstruct/issues/1148 ctx.getMappingsToGenerate().add( mappingMethod ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index d67b550ad4..a73bce56e3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -629,7 +629,7 @@ private boolean isCorrectlySealed(Type mappingSourceType) { List unusedPermittedSubclasses = new ArrayList<>( mappingSourceType.getPermittedSubclasses() ); method.getOptions().getSubclassMappings().forEach( subClassOption -> { - for (Iterator iterator = unusedPermittedSubclasses.iterator(); + for ( Iterator iterator = unusedPermittedSubclasses.iterator(); iterator.hasNext(); ) { if ( ctx.getTypeUtils().isSameType( iterator.next(), subClassOption.getSource() ) ) { iterator.remove(); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/DefaultMapperReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/DefaultMapperReference.java index 6ac0c1c74d..2728f99323 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/DefaultMapperReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/DefaultMapperReference.java @@ -36,7 +36,7 @@ private DefaultMapperReference(Type type, boolean isAnnotatedMapper, boolean isS public static DefaultMapperReference getInstance(Type type, boolean isAnnotatedMapper, boolean isSingleton, TypeFactory typeFactory, List otherMapperReferences) { Set importTypes = Collections.asSet( type ); - if ( isAnnotatedMapper && !isSingleton) { + if ( isAnnotatedMapper && !isSingleton ) { importTypes.add( typeFactory.getType( "org.mapstruct.factory.Mappers" ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java index cd092ca4f4..bcf7e840e5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java @@ -199,12 +199,12 @@ protected String getTemplateName() { * @return the flat name for the type element */ public static String getFlatName(TypeElement element) { - if (!(element.getEnclosingElement() instanceof TypeElement)) { + if ( !(element.getEnclosingElement() instanceof TypeElement) ) { return element.getSimpleName().toString(); } StringBuilder nameBuilder = new StringBuilder( element.getSimpleName().toString() ); - for (Element enclosing = element.getEnclosingElement(); enclosing instanceof TypeElement; enclosing = - enclosing.getEnclosingElement()) { + for ( Element enclosing = element.getEnclosingElement(); enclosing instanceof TypeElement; enclosing = + enclosing.getEnclosingElement() ) { nameBuilder.insert( 0, '$' ); nameBuilder.insert( 0, enclosing.getSimpleName().toString() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index 1ebf99ffeb..59c3efd141 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -228,7 +228,7 @@ public List getReservedNames() { nameSet.add( method.getName() ); } // add existing names - for ( SourceMethod method : sourceModel) { + for ( SourceMethod method : sourceModel ) { if ( method.isAbstract() ) { nameSet.add( method.getName() ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java index ed841ba5bc..6fbd5e6f82 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.java @@ -179,7 +179,7 @@ public List getPropertyEntries() { @Override public Set getImportTypes() { Set types = super.getImportTypes(); - for ( SafePropertyEntry propertyEntry : safePropertyEntries) { + for ( SafePropertyEntry propertyEntry : safePropertyEntries ) { types.addAll( propertyEntry.getType().getImportTypes() ); if ( propertyEntry.getPresenceChecker() != null ) { types.addAll( propertyEntry.getPresenceChecker().getImportTypes() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java index 8b526b9177..88965dedca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ObjectFactoryMethodResolver.java @@ -74,7 +74,7 @@ public static MethodReference getFactoryMethod( Method method, ctx ); - if (matchingFactoryMethods.isEmpty()) { + if ( matchingFactoryMethods.isEmpty() ) { return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index c120f035b2..5defa65bf5 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -314,7 +314,7 @@ private Assignment forge( ) { else if ( sourceType.isMapType() && targetType.isMapType() ) { assignment = forgeMapMapping( sourceType, targetType, rightHandSide ); } - else if ( sourceType.isMapType() && !targetType.isMapType()) { + else if ( sourceType.isMapType() && !targetType.isMapType() ) { assignment = forgeMapping( sourceType, targetType.withoutBounds(), rightHandSide ); } else if ( ( sourceType.isIterableType() && targetType.isStreamType() ) @@ -427,7 +427,7 @@ private Assignment assignToPlainViaSetter(Type targetType, Assignment rhs) { Assignment factory = ObjectFactoryMethodResolver .getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( rightHandSide ), ctx ); - if ( factory == null && targetBuilderType != null) { + if ( factory == null && targetBuilderType != null ) { // If there is no dedicated factory method and the target has a builder we will try to use that MethodReference builderFactoryMethod = ObjectFactoryMethodResolver.getBuilderFactoryMethod( targetType, @@ -732,9 +732,9 @@ private PresenceCheck getSourcePresenceCheckerRef(SourceReference sourceReferenc String variableName = sourceParam.getName() + "." + propertyEntry.getReadAccessor().getReadValueSource(); Type variableType = propertyEntry.getType(); - for (int i = 1; i < sourceReference.getPropertyEntries().size(); i++) { + for ( int i = 1; i < sourceReference.getPropertyEntries().size(); i++ ) { PropertyEntry entry = sourceReference.getPropertyEntries().get( i ); - if (entry.getPresenceChecker() != null && entry.getReadAccessor() != null) { + if ( entry.getPresenceChecker() != null && entry.getReadAccessor() != null ) { if ( variableType.isOptionalType() ) { presenceChecks.add( new OptionalPresenceCheck( variableName, @@ -852,7 +852,7 @@ private Assignment forgeMapping(Type sourceType, Type targetType, SourceRHS sour // because we are forging a Mapping for a method with multiple source parameters. // If the target type is enum, then we can't create an update method if ( !targetType.isEnumType() && ( method.isUpdateMethod() || forceUpdateMethod ) - && targetWriteAccessorType != AccessorType.ADDER) { + && targetWriteAccessorType != AccessorType.ADDER ) { parameters.add( Parameter.forForgedMappingTarget( targetType ) ); returnType = ctx.getTypeFactory().createVoidType(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java index a0c9f799e9..b9f1d2acb0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java @@ -57,7 +57,7 @@ public Set getImportTypes() { imported.add( typeFactory.getType( EnumSet.class ) ); } } - if (isDirectAssignment() || getSourcePresenceCheckerReference() == null ) { + if ( isDirectAssignment() || getSourcePresenceCheckerReference() == null ) { imported.addAll( getNullCheckLocalVarType().getImportTypes() ); } return imported; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java index d013a77a7a..9baa84dc77 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReference.java @@ -45,7 +45,7 @@ public TargetReference getTargetReference() { public MappingReference popTargetReference() { if ( targetReference != null ) { TargetReference newTargetReference = targetReference.pop(); - if (newTargetReference != null ) { + if ( newTargetReference != null ) { return new MappingReference(mapping, newTargetReference, sourceReference ); } } @@ -55,7 +55,7 @@ public MappingReference popTargetReference() { public MappingReference popSourceReference() { if ( sourceReference != null ) { SourceReference newSourceReference = sourceReference.pop(); - if (newSourceReference != null ) { + if ( newSourceReference != null ) { return new MappingReference(mapping, targetReference, newSourceReference ); } } @@ -76,7 +76,7 @@ public boolean equals(Object o) { return false; } - if (!Objects.equals( mapping.getTargetName(), that.mapping.getTargetName() ) ) { + if ( !Objects.equals( mapping.getTargetName(), that.mapping.getTargetName() ) ) { return false; } @@ -97,7 +97,7 @@ public boolean equals(Object o) { } - if (!Objects.equals( sourceReference.getPropertyEntries(), that.sourceReference.getPropertyEntries() ) ) { + if ( !Objects.equals( sourceReference.getPropertyEntries(), that.sourceReference.getPropertyEntries() ) ) { return false; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java index ced945bb90..edc5b3157e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java @@ -109,7 +109,7 @@ public boolean hasNestedTargetReferences() { for ( MappingReference mappingRef : mappingReferences ) { TargetReference targetReference = mappingRef.getTargetReference(); - if ( targetReference.isNested()) { + if ( targetReference.isNested() ) { return true; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java index 9300f683ce..f399c46dc9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/BuilderType.java @@ -108,7 +108,7 @@ else if ( typeUtils.isSameType( builder.getTypeMirror(), builderCreationOwner ) // When the builderCreationMethod is constructor, its return type is Void. In this case the // builder type should be the owner type. - if (builderInfo.getBuilderCreationMethod().getKind() == ElementKind.CONSTRUCTOR) { + if ( builderInfo.getBuilderCreationMethod().getKind() == ElementKind.CONSTRUCTOR ) { builder = owner; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index 6287403ba3..8f512c3ace 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -285,7 +285,7 @@ public String createReferenceName() { return name; } - if ( isTopLevelTypeToBeImported() && nameWithTopLevelTypeName != null) { + if ( isTopLevelTypeToBeImported() && nameWithTopLevelTypeName != null ) { return nameWithTopLevelTypeName; } @@ -1346,7 +1346,7 @@ public String describe() { private String getNameKeepingInnerClasses() { String packageNamePrefix = getPackageName() + "."; String fullyQualifiedName = getFullyQualifiedName(); - if (fullyQualifiedName.startsWith( packageNamePrefix ) ) { + if ( fullyQualifiedName.startsWith( packageNamePrefix ) ) { return fullyQualifiedName.substring( packageNamePrefix.length() ); } return fullyQualifiedName; @@ -1665,7 +1665,7 @@ else if ( typeToMatch.hasSuperBound() && parameterized.getSuperBound() != null return new ResolvedPair( typeFactory.getType( parameterized ), declared ); } } - else if (parameterized.getSuperBound() != null ) { + else if ( parameterized.getSuperBound() != null ) { ResolvedPair match = visit( parameterized.getSuperBound(), declared ); if ( match.match != null ) { return new ResolvedPair( typeFactory.getType( parameterized ), declared ); @@ -1897,7 +1897,7 @@ public List getPermittedSubclasses() { if ( kotlinMetadata != null ) { return kotlinMetadata.getPermittedSubclasses(); } - if (SEALED_PERMITTED_SUBCLASSES_METHOD == null) { + if ( SEALED_PERMITTED_SUBCLASSES_METHOD == null ) { return emptyList(); } try { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java index 606bbce69d..37c38ab590 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java @@ -294,7 +294,7 @@ else if ( mirror.getKind() == TypeKind.ARRAY ) { packageName = elementUtils.getPackageOf( componentTypeElement ).getQualifiedName().toString(); qualifiedName = componentTypeElement.getQualifiedName().toString() + arraySuffix; } - else if (componentTypeMirror.getKind().isPrimitive()) { + else if ( componentTypeMirror.getKind().isPrimitive() ) { // When the component type is primitive and is annotated with ElementType.TYPE_USE then // the typeMirror#toString returns (@CustomAnnotation :: byte) for the javac compiler name = NativeTypes.getName( componentTypeMirror.getKind() ) + builder.toString(); @@ -497,7 +497,7 @@ public List getThrownTypes(ExecutableType method) { } public List getThrownTypes(Accessor accessor) { - if (accessor.getAccessorType().isFieldAssignment()) { + if ( accessor.getAccessorType().isFieldAssignment() ) { return new ArrayList<>(); } Element element = accessor.getElement(); @@ -526,7 +526,7 @@ private List getTypeParameters(TypeMirror mirror, boolean isImplementation List typeArguments = declaredType.getTypeArguments(); List typeParameters = new ArrayList<>( typeArguments.size() ); - for ( TypeMirror typeParameter : typeArguments) { + for ( TypeMirror typeParameter : typeArguments ) { Type type = getType( typeParameter ); typeParameters.add( isImplementationType ? type.getTypeBound() : type ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java index 08ac1a683a..ea62ee3657 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingMethodOptions.java @@ -246,11 +246,11 @@ private boolean methodsHaveIdenticalSignature(SourceMethod templateMethod, Sourc } private boolean parametersAreOfIdenticalTypeAndOrder(SourceMethod templateMethod, SourceMethod sourceMethod) { - if (templateMethod.getParameters().size() != sourceMethod.getParameters().size()) { + if ( templateMethod.getParameters().size() != sourceMethod.getParameters().size() ) { return false; } for ( int i = 0; i < templateMethod.getParameters().size(); i++ ) { - if (!templateMethod.getParameters().get( i ).getType().equals( + if ( !templateMethod.getParameters().get( i ).getType().equals( sourceMethod.getParameters().get( i ).getType() ) ) { return false; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index 3d8bfea135..c62012472c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -165,7 +165,7 @@ private boolean lineUp() { // Represent result as map. Map resolvedPairs = new HashMap<>(); for ( TypeVarCandidate candidate : methodParCandidates.values() ) { - for ( Type.ResolvedPair pair : candidate.pairs) { + for ( Type.ResolvedPair pair : candidate.pairs ) { resolvedPairs.put( pair.getParameter(), pair.getMatch() ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java index df5cd848a5..d5595ef6b7 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JakartaXmlElementDeclSelector.java @@ -28,7 +28,7 @@ class JakartaXmlElementDeclSelector extends XmlElementDeclSelector { XmlElementDeclInfo getXmlElementDeclInfo(Element element) { XmlElementDeclGem gem = XmlElementDeclGem.instanceOn( element ); - if (gem == null) { + if ( gem == null ) { return null; } @@ -39,7 +39,7 @@ XmlElementDeclInfo getXmlElementDeclInfo(Element element) { XmlElementRefInfo getXmlElementRefInfo(Element element) { XmlElementRefGem gem = XmlElementRefGem.instanceOn( element ); - if (gem == null) { + if ( gem == null ) { return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java index 1d02e97e90..912da3e696 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/JavaxXmlElementDeclSelector.java @@ -28,7 +28,7 @@ class JavaxXmlElementDeclSelector extends XmlElementDeclSelector { XmlElementDeclInfo getXmlElementDeclInfo(Element element) { XmlElementDeclGem gem = XmlElementDeclGem.instanceOn( element ); - if (gem == null) { + if ( gem == null ) { return null; } @@ -39,7 +39,7 @@ XmlElementDeclInfo getXmlElementDeclInfo(Element element) { XmlElementRefInfo getXmlElementRefInfo(Element element) { XmlElementRefGem gem = XmlElementRefGem.instanceOn( element ); - if (gem == null) { + if ( gem == null ) { return null; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java index 87ec3b4dc1..88e302eba0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/MostSpecificResultTypeSelector.java @@ -23,7 +23,7 @@ public List> getMatchingMethods(List